OpenGL ES手册翻译---2.OpenGL ES操作(三)

2.10 顶点着色器

顶点着色器处理通过DrawArraysDrawElements指定的顶点。每个被顶点着色器(见2.10.4节)使用的顶点属性被设置为被处理的数组元素相对应的通用顶点属性值,或者在没有为属性绑定顶点数组时,被设置为相对应的当前通用顶点属性。

顶点着色器执行后,被处理的顶点被传递给图元装配器(见2.11节)。

顶点着色器由一组包含着源代码的字符串构成,这些代码将会在每个被处理的顶点上执行操作。顶点着色器使用的语言将会在 OpenGL ES Shading Language Specification 中介绍。

使用顶点着色器时,着色器代码首先会加载到shader object中,然后compile。此外,预先编译的着色器二进制代码可以直接加载到着色器对象中。OpenGL ES实现必须支持这些加载着色器方法的一种。如果二进制值SHADER_COMPLILERTRUE,着色器编译就是被支持的。如果整型值NUM_SHADER_BINARY_FORMATS比0大,那么着色器加载二进制代码也是支持的。

顶点着色器对象附着在program object上。程序对象可以被linked,链接附着在program上的所有编译过的着色器对象,生成可执行代码。当一个链接过的程序对象正在被使用,程序对象中的顶点着色器代码的可执行代码将会被用来处理顶点。

除了顶点着色器,片段着色器也可以被创建,编译,链接到程序对象。片段着色器会在光栅化期间对片段着色产生影响,如同3.8节描述的那样。一个程序对象必须同时包括顶点着色器和片段着色器。

正在被GL使用的附着在程序对象上的顶点着色器被认为是active,它也正在被用来处理顶点。如果没有程序对象,顶点着色器的执行会导致未定义的结果。

2.10.1加载和编译着色器资源

每个shader object中封装了构成program的源代码,这个program将会在可编程阶段被执行。

shader object的命名空间为无符号整数,0被GL保留。这个命名空间和program对象共享。下面部分按照作用在shader和program对象上的函数名字对函数作介绍。如果提供的名字既不是shader的名字,也不是program的名字,接受shader和program的函数将会产生INVALID_VALUE的错误,如果提供的名字所关联的对象不是期望的类型,将会产生INVALID_OPERATION错误。

创建一个shader对象,使用函数:

uint CreateShader(enum type);

shader对象在创建后是空的。type参数指被创建的shader对象的类型。对vertex shader来说,type必须是VERTEX_SHADER。一个非零整数可以被用来应用返回的shader对象。如果发生错误,将会返回0.

函数

void shaderSource(uint shader, sizei count, const char **string, const int *length);

把源代码加载到命名为shader的着色器对象中。stringcount个数组,指向可选的null-terminated的字符串构成的源代码。length参数是一个数组,数组中的每个数字表示每个string中char字符的个数(字符串长度)。如果length中的元素是负的,那么对应的字符串为null-terminated;这种情况下,只有length中的有符号的元素会被考虑。如果length是NULL,所有string参数中的字符串都被认为是null-terminal类型。ShaderSource函数给shader设置源代码,源代码设置在string中。如果shader中已经有之前加载的源代码,那么已经存在的源代码将会被完全取代。传递给lenght的值不包括空结束符的数量。

加载到shader对象中的字符串,被期望是能够构成有效着色器的源代码,这部分在OpenGL ES Shading Language Specification中会描述。

着色器的源代码一旦被加载,着色器对象就可以被

void CompileShader(uint shader);

函数编译。

每个shader对象都有一个布尔状态,COMPILE_STATUS,这个值被编译结果修改。这个状态可以通过GetShaderiv(见6.1.8节)来查询。如果着色器编译后没有错误,准备好使用了,shader的状态将会被设置为TURE,否则为FALSE。编译会因为很多原因失败,这些原因被罗列在OpenGL ES Shading Language Specification中。如果编译失败,上次的编译状态将会全部丢失。因为错误编译后不能回复shader之前的状态。

使用ShaderSource改变着色器对象的源代码,不会改变着色器状态和已经编译过的着色器代码。

每个着色器对象都有信息日志,它是一个文本字符串通过复写来描述编译结果。这个信息日志可以通过GetShaderInfoLog来查询,这里面包含着更多的编译目的(见6.1.8节)。

着色器编译所分配的资源,可以通过函数

void ReleaseShaderCompiler(void);

来释放。这只是一个应用层面的小提示,并不会真正阻止之后的着色器编译操作。如果着色器代码的加载和编译都是在ReleaseShaderCompiler之后调用的,CompileShader也会调用成功,并且不产生任何错误。

着色器编译所支持的不同计数格式的范围和精度,可以通过函数GetShaderPrecisionFormat(见6.1.8节)来获得。

着色器对象可以通过下面函数删除

void DeleteShader(uint shader);

如果shader没有附着在任何program上,它将会被立即删除。此外,shader被标记为删除后,将会在没有附着在任何program上被删除。一旦被标记为要删除,shader对象的bu'er'zhuang'tai布尔状态值DELETE_STATUS被设置为true。DELETE_STATUS值可以通过函数GetShaderiv(见6.1.8节)来查询。DeleteShader将会默认忽视0值。

如果SHADER_COMPLIER不是TRUE,那么调用ShaderSourceCompileShader,或者ReleaseShaderCompiler都将会产生INVALID_OPERATION的错误。

2.10.2 加载二进制着色器

编译好的二进制着色器代码可以通过函数

void ShaderBinary(sizei count, const uint *shaders, enum binaryformat, const void *binary, sizei height);

来加载。shaders包含一系列count大小的着色器对象句柄。每个句柄指向一个独一无二的着色器类型(顶点着色器或者片段着色器)。binary指向存储在客户端的lenght长度个字节的编译好的二进制着色器代码,binaryformat指预编译代码的格式。

二进制图像将根据定义指定的二进制格式的扩展规范进行解码。OpenGL ES没有指定二进制格式,但是提供了一种获取这些扩展提供的格式的token值得机制。着色器二进制格式支持的种类数目可以通过NUM_SHADER_BINARY_FORMAT查询获得。支持的二进制格式列表可以通过SHADER_BIANRY_FORMATS查询获取。

根据shaders中着色器对象的类型,ShaderBinary将会独立的加载二进制的顶点和片段着色器,或者加载一个可执行的二进制库,优化过的顶点和片段着色器都存储在这个二进制库中。

如果binaryformat是不支持的格式,SHADER_BINARY_FORMAT将会返回INVALID_ENUM的错误。如果binary所指向的数据的格式和binaryformat中的不匹配,将会产生INVALID_VALUE的错误。特定二进制格式的附加错误, 将会根据扩展中定义的具体格式格式对应的产生。如果同样类型的着色器(顶点或者片段着色器)有超过一个的句柄指向它,将会产生INVALID_OPERATION错误。

如果ShaderBinary失败了,已经被加载的着色器对象的状态不会恢复。

注意,如果二进制着色器支持接口,那么OpenGL ES可能要求优化顶点和片段着色器对,以便被一起编译,然后送给指定的LinkProgram。没有如上所述的优化,可能会导致LinkProgram失败。

2.10.3 程序对象

GL可编程阶段所使用的着色器对象被收集在一起,形成program对象。在可编程阶段执行programs的过程被称作executables。定义executable所需要的所有信息都被封装在一个program对象中。一个program对象通过函数

uint CreateProgram(void);

创建。

Program对象在他们被创建的时候是空的。返回的一个非零数可以用来引用program对象。如果发生错误,将会返回0。

把着色器对象附着在program对象上,使用函数

void AttachShader(uint program, unit shader);

着色器对象可以在源代码加载之前或者编译之前就附着在program对象上。相同类型的多个着色器对象不能附着在一个程序对象上。但是,一个着色器对象是可以附着在多个程序对象上的。如果shader已经附着在了program上,或者其他相同类型的着色器已经附着在program上时,将会产生INVALID_OPERATION的错误。

从程序对象分离着色器对象,使用函数

void DetachShader(uint program, uint shader);

如果shader已经被标记为要被删除,并且没有附着在任何program对象上时,shader将会被删除。

如果shader没有附着在program上,将会产生INVALID_OPERATION错误。如果program对象不是用CreateProgra创建的有效的程序对象,将会产生INVALID_OPERATION错误。

为了使用包含在程序对象中的着色器对象,程序对象必须要被链接。函数:

void LinkProgram(uint program);

将会链接名为program的程序对象。每个程序对象都有一个布尔状态,LINK_STATUS,存储着链接的结果。这个状态可以通过GetProgramiv(见6.1.8)来查询。如果有效的executable被创建,状态将会被设置为TRUE,否则为FALSE。链接过程会因为很多原因失败,具体在OpenGL ES Shading Launuage Specification会详细说明。如果附着在程序对象上的一个或者多个着色器没有编译成功,或者program对象只包含了顶点着色器或者片段着色器的一个,或者在program中使用的激活的统一变量和激活的采样变量比允许使用的多(见2.10.4部分),链接也都会失败。如果LinkProgram失败,程序对象之前的链接状态信息将会全部丢失。因此,失败链接后,program将不能回复到原来的状态。如果使用CreateProgram创建的program是无效的程序对象,将会产生INVALID—_VALUE错误。

根据链接操作的结果,每个程序对象都有一个可以被复写的信息日志。这个信息日志可以通过GetProgramInfoLog来获取更多的链接操作信息和有效的信息(见6.1.8部分)。

一个有效的executable被创建后,使用函数

void UseProgram(uint program);

将会成为当前渲染状态的一部分。如果程序对象包含有效的可执行代码(已经链接成功的),这个函数将会安装可执行代码,变为当前渲染状态的一部分。如果UseProgram在调用时,program为0,那么当前渲染状态将会指向无效的程序对象,顶点着色器和片段着色器中调用的DrawArraysDrawElements函数导致的结果都是不可预料的。而这,并不是错误。如果program对象没有被成功链接,将会产生INVALD_OPERATION错误,当前渲染状态不会改变。

如果正在使用的程序对象重新链接还是失败,链接状态将会被设置为FALSE,但是,直到下一次调用UseProgram将其从可用程序中删除之前,已经存在的可执行代码和相关的状态都会作为当前渲染状态被保留。在从可用程序中被删除后,在被重新链接成功之前,都将不会成为当前渲染状态的一部分。

程序对象可以使用函数

void DeleteProgram(uint program);

来删除。如果program不是GL上下文中的当前程序对象,那将会被立刻删除。否则,program将会被标记为要删除,当它不再是任何GL上下文的当前程序对象时就会被删除。当程序对象被删除时,所有附着在程序对象上的着色器对象都会分离。DeleteProgram将默认忽略program为0的值。

2.10.4 着色器变量

顶点着色器可以在它执行的时候引入多种变量。Vertex attributes是指每个顶点的值,如2.7部分。Uniforms是每个program的变量,在程序执行期间为常量。Samples是为纹理(见3.7节)专门使用的一种特殊形式的uniform值。Varying保存着顶点着色器执行的值,以供后续再渲染管线中使用。下面将讨论每种变量类型。

  1. 顶点属性

顶点着色器可以定义属性变量的命名,这些命名和由VertexAttrib设置的通用顶点属性绑定。在程序链接之前,或者在程序链接时自动由GL分配的时候,这些绑定由应用程序指定。

当声明为floatvec2vec3,vec4的属性变量绑定到索引为i的通用顶点属性时,属性变量的值独立的从通用属性i的x(x,y)(x,y,z)或者(x,y,z,w)通道中获取。当属性变量声明为mat2,矩阵值从通用属性i和i+1的(x,y)通道中获取。当属性变量声明为mat3的时候,矩阵值从通用顶点i到i+2中的(x,y,z)通道中获取。当属性变量声明为mat4的时候,矩阵将会从通用顶点i到i+3的(x,y,z,w)通道中获取。

一个通用顶点属性被编译器和链接器处理过,并且属性可以在着色器被执行时被访问,这个通用顶点属性就被认为是active的。通用顶点属性在顶点着色器被声明了但从来都没有被使用,就认为它不是active的。当编译器和链接器都不能决定的情况下,属性被认为是active的。如果活跃的顶点属性超过MAX_VERTEX_ATTRIBS,程序对象将会链接失败。

决定程序使用的活跃顶点属性以及类型,可以使用函数:

void GetActiveAttrib(uint program, uint index, sizei bufSize, sizei *length, int *size, enum *type, char *name);

这个函数提供了index上的属性的信息。index0选择了第一个活跃的属性,ACTIVE_ATTRIBUTES - 1选择最后一个活跃属性。ACTIVE_ATTRIBUTES的值可以通过GetProgramiv来获取(见6.1.8节)。如果index大于或者等于ACTIVE_ATTRIBUTES,将会产生INVALID_VALUE的错误。注意,index仅仅是唯一标识了活跃属性中的一个成员,和相应绑定的变量的通用属性没有关系。

参数program是函数LinkProgram之前已经处理过的那个程序对象的名字。对于已经链接成功的program来说不是很必要。链接会因为活跃属性超过限制而失败。

被选中的属性的名字会以无结束符的字符串形式在参数name中返回。除去结束符后的实际的name的字符数会在length中返回。如果length是NULL,将不会返回。最大的字节长度,包括结束符在内的,由bufSize来指定。返回的属性名字一定是通用属性的名字。最长的程序中可用的属性的名字由ACTIVE_ATTRIBUTE_MAX_LENGHT给出,可以通过GetProgramiv(见6.1.8)来查询。

对于被选择的属性,属性的类型通过type返回。属性的大小通过size返回。size的大小是以tpye返回的类型为单元的。返回类型有FLOAT,FLOAT_VEC2,FLOAT_VEC3,FLOAT_VEC4,FLOAT_MAT2,FLOAT_MAT3,FLOAT_MAT4

如果发生错误,返回参数lenght,size,type,name都不会发生变化。

函数将会尽可能返回活跃属性的更多信息。如果没有对应的合适的信息,lenght将被置为0,name将返回空字符串。在失败链接后调用GetActiveAttrib,就可能会出现这种情况。

在程序对象成功编译后,绑定到属性变量名字上的序列号可以被查询了。函数

int GetAttribLocation(uint program, const char *name);

返回通用属性序列号,这个序列号是命名为name的属性变量在命名为program的程序对象被链接时绑定的。name一定是无结束符的字符串。如果name是活跃的,而且属性是矩阵,GetAtribLocation返回矩阵第一列的序列号。如果program没有成功链接,将产生INVALID_OPERATION的错误。如果name不是活跃属性,或者发生错误,将会返回-1。

向通用顶点属性序列绑定属性变量也可以显式指定,使用函数:

void BindAttribLocation(uint program,uint index, const char *name);

这个函数指,当程序program是下一个被链接的时候,在程序中,名为name的属性变量绑定在通用顶点属性index上。如果name之前已经绑定过了,那分配的绑定将会被index取代。name必须是无结束符的字符串。如果index等于或者大于MAX_VEREX_ATTRIBS将会产生INNALID_VALUE的错误。BindAttribLocation在链接之后才会起作用。通常情况下,它并不会修改已经被链接过的程序中的活跃属性变量的绑定。

如果name以GL保留的"gl_"做前缀命名,将会导致产生INVALID_OPERATION错误。

当程序被链接后,所有没有通过BindAttribLocation绑定的活跃属性,将会由GL自动绑定到顶点属性。这种绑定可以通过GetAttribLocation来查询。如果分配的活跃属性变量的绑定引起GL引用一个不存在的通用属性(比如大于或者等于MAX_VERTEX_ATTRIBS),LinkProgram将会失败。如果BindAttribLocation绑定分配的属性没有足够的空间,分配给需要多个临近通用属性的活跃的矩阵属性,LinkProgram也将失败。

BindAttribLocation可以在任何顶点着色器附着在程序对象之前就起执行。因此它可以用来绑定任何名字(除了gl_开头的)到序列号上,包括一个从来都没有被使用在任何顶点着色器的属性上的名字。为属性变量分配的绑定中,不存在的或者不活跃的,将会被忽略。

通用属性被送到通用属性序列i的值是当前状态的一部分。如果新的程序对象被激活,这些值将会被GL以这样的方式继续追踪,就是这些值将会被芯的程序对象的属性所监控,同样也绑定到序列i上。

一个应用在一个位置上绑定多个属性名字也是可能的,这被称为aliasing。只有在可执行程序中只有一个别名属性处于活动状态,或者如果没有通过着色器的路径消耗了属于同一位置的属性集的多个属性,则此操作才有效。如果链接器发现每个通过着色器的路径都消耗了多个别名属性,链接将会出错,但在这种情况下的实现并不会产生错误。编译器和链接器允许假定没有别名,在没有别名的时候做一些优化工作。

  1. 统一变量

着色器可以申明uniform变量,在OPenGL ES SHAding Language Specification中有详细的描述。这些uniform是关于图元的一些常量,一般的,他们是一些被用于所有图元的常量。加载着色器程序后,只要程序对象没有被重新链接,这些变量就会被保留,值被存储下来。当代码执行,uniform被访问时,uniform由编译器和链接器决定,这是的uniform被称为是active的。编译器和链接器无法做出决定性的结论时,uniform也被认为是活跃的。

访问顶点着色器的uniform变量可以使用的存储数量通过常数MAX_VERTEX_UNIFORM_VECTORS指定。这个值代表在顶点着色器的uniform变量存储空间中,可以存储的四元素的浮点指针,整数或者布尔向量的个数。如果试图使用比顶点着色器允许使用的uniform变量空间更多的uniform变量,将会产生链接错误。

当程序被成功链接,所有这个程序的活跃的uniforms都会被初始化为0.每个uniform成功链接后,都会为其产生一个location。这些活跃的uniforms可以通过使用location和适当的Uniform*函数进行修改。这些locations是独立的,新的location将会在重新链接后被分配。

得到程序对象中活跃的uniform变量可以使用函数:

int GetuniformLocation(uint program, const char *name);

这个函数将会返回命名为name的Uniform变量的location。name一定是无结束符的字符串,也没有空格。如果name和程序中活跃uniform的变量名称不对应,或者name是一保留字符串gl_开头的,将会返回-1。如果program没有成功链接,将会产生INVALID_OPERATION错误。程序链接后,uniform变量的location不会发生改变,除非程序被重新链接。

有效的name不能是结构体,结构体数组,以及向量或者矩阵的任何部分。为了辨识一个有效的name.或者[]被用在命名中,用来指定name是结构体的成员或者数组的元素。

unifrom数组的第一个元素等价于使用uniform数组后缀[0]。除非name字符串最后部分是指uniform数组,否则,数组的第一个元素的位置可以通过使用uniform数组名字来获得,或者数组名字后缀[0]来获得。

决定程序使用的活跃uniform属性以及大小和类型,使用函数:

void GetActiveUnform(uint program, uint index, sizei bufSize, sizei *length, int *size, enum *type, char *name);

这个函数提供了index的uniform的信息。index为0时选择第一个活跃的uniform,index为ACTIVE_UNFORMS-1时选择最后一个活跃的uniform。ACTIVE_UNIFORM可以通过GetProgramiv(见6.1.8节)查询。如果index大于等于ACTIVE_UNIFORMS,将会产生INVALID_VALUE错误。注意,index仅仅标识了活跃uniforms数组的成员,和uniform变量分配的location没有关系。

program参数是函数linkProgram正在使用的program的名字。program是否链接成功不是必须的。链接可以因为活跃的uniformms超过了限制而失败。

如果发生错误,返回的lengthsizetypename参数都不可修改。

对于选中的uniform变量,名字通过name返回。name是没有结束符的字符串。name的实际的字符的个数排除了结束符,通过length返回。如果length返回NULL代表没有长度值返回。name的最大长度的可以包含的字符数量,包括结束符在内,由bufSize指定。返回的uniform变量的名字也可以当作是内建uniform变量状态的名字。内建uniform变量状态的完整列表在OpenGL ES Shading Language specification的7.5节有详细的描述。uniform变量名字在program中允许的最大的长度由ACTIVE_UNIFORM_MAX_LENGTH给出,可以通过GetProgramiv查询(详见6.1.8节)。

每个在着色器中声明的uniform变量,如果需要的话,可以通过“[]”或者“.”(点)运算符分解成几个字符串,只要这些字符串可以合法的传递给GetUniformLocation就可以。这些字符串的每一个都构成一个活跃的uniform变量,每个字符串都会被分配一个索引。

如果活跃的uniform变量是一个数组,在name中返回的uniform变量的名字将会是数组名字后缀“[0]”的形式。

对于选中的uniform变量,uniform变量的名字的类型将会返回到type中。uniform变量的大小将会返回到size中。size的值是以type中的类型为单位的。可以返回的类型可以是FLOAT,FLOAT_VEC2,FLOAT_VEC3,FLOAT_VEC4,INT,INT_VEC2,INT_VEC3,INT_VEC4,BOOL,BOOL_VEC2,BOOL_VEC3,BOOL_VEC4,FLOAT_MAT2,FLOAT_MAT3,FLOAT_MAT4,SAMPLER_2D,SAMPLER_CUBE

如果uniform数组中的一个或者多个元素是活跃的,GetActiveUniform将会在name中返回数组的名字,名字依然遵守上述的规则。type中返回数组的类型。size参数是数组的最大索引加1。编译器或者链接器决定了使用的最大索引。每个uniform数组中,GL只会报告唯一一个活跃的uniform变量。

GetActiveUnifom 将会尽可能多的返回当前活跃的uniform变量的信息。如果没有可用信息可以返回,length将会被设置成0,name将会是一个空字符串。如果GetActiveUniform出错导致链接失败,就会出现这种情况。

当前活跃的program对象的uniform变量,通过下面几个函数赋值:

void Uniform{1234}{if}(int location T value);
void Uniform{1234}{if}v(int location, sizei count, T value);
void UniformMatrix{234}fv(int location, sizei count, boolean transpose, const float *value);

赋给uniform变量的值通过location做唯一的标识

Uniform*f{v} 函数将会在uniform变量的location位置上加载count个一到四组的浮点指针类型的值,这些值被定义为浮点数,浮点数向量,浮点数组或者是浮点指针向量的数组。

Uniform*i{v} 函数将会在uniform变量的location位置上加载count个一到四组整形值,
这些值分别被定义为一个采样数,一个整形数,一个整形向量,采样数组,整形数组,或者是整形向量数组。只有Uniform1i{v}函数可以被用来加载采样值(如下)。

UniformMatrix{234}fv 函数将会在uniform变量的location位置加载2x2,3x3或者4x4的矩阵(和函数名中的234向对应),或者浮点指针,这些值被定义为一个矩阵或者矩阵数组。这些矩阵按照列主顺序指定,transpose转置为FALSE

uniform加载的值是布尔类型,布尔向量,布尔数组,或者是布尔向量数组,Uniform*i{v}Uniform*f{v} 都可以用来加载。GL会自动完成类型转换。如果输入的值是0或者0.0f,uniform值被设置为FALSE,其他的值会被转换为TRUEUniform* 函数中使用的大小必须和着色器中声明的大小相匹配。比如,加载一个声明为bvec2的uniform变量,Uniform2i{v}或者Uniform2f{v}都可以使用。如果使用时产生不匹配的错误将会产生INVALID_OPERATION错误。在这个例子中,使用Uniform1iv函数就会产生错误。

对于其他的所有的Uniform*函数给Uniform变量赋值时,都必须和着色器中声明的大小和类型匹配。也不会有类型转换。比如,加载一个申明为vec4的uniform变量,必须要使用Uniform4f{v}函数。加载一个3x3的矩阵,必须使用UniformMatrix3fv函数。如果使用不匹配的Uniform*函数赋值,将会产生INVALID_VALUE错误。在这个例子中,使用Uniform4i{v},就会产生这个错误。

在一个声明为数组的uniform变量中,从任意的k位置开始加载N个元素,从k开始的k+N-1个位置的元素将会被新值代替。超过数组最大元素索引的数组的赋值,虽然会被GetActiveUniform报告,但是GL会自动忽略。

如果location的值是-1,Uniform*函数将会静默忽略传入的数据,当前的uniform值不会被改变。

如果UniformMatrix*函数的transpose参数不是FALSE,就会产生INVALID_VALUE错误,uniform值也不会改变。

如果发生下面的情况,Uniform*函数将会产生INVALID_OPERATION错误,uniform值也不会被改变:

  • Uniform*函数和着色器中声明的uniform变量的大小不匹配,
  • Uniform*函数的类型和着色器中声明的非布尔类型的uniform变量的类型不匹配,
  • count赋值大于1,但是着色器中声明的uniform变量不是数组变量,
  • 在当前使用的program对象中,在指定的location位置没有变量,而且location不是-1,
  • 当前没有可用的program对象。
  1. 采样变量

Samplers在OpenGL ES Shading Language中,是一种特殊的uniform变量,用来在纹理查找中标识纹理对象。采样变量的值表示将要被访问的纹理图像单元。给采样变量设置i值表示选择了数字i的纹理图像单元。i的取值从0到基于实现的支持纹理图像单元的最大数。

采样变量的类型指定了纹理图像单元的目标类型。纹理对象受限于纹理图像单元的目标,这些纹理对象被用来查找纹理。比如,一个sampler2D类型的变量,要选择纹理图像单元的TEXTURE_2D作为目标。使用BindTexture把纹理对象绑定到纹理图像单元的目标上。当前已经绑定纹理的纹理图像单元通过ActiveTexture访问。

采样变量的位置需要通过查询GetUniformLocation函数访问,这一点和Uniform变量是相似的。采样值需要通过Uniform1i{v}设置。使用Uniform*的其他函数来设置采样变量的值是不允许的,将会导致INVALID_OPERATION错误。

在一个program对象中,使用不同类型的采样变量指向同样的纹理图像单元是不允许的。这种情形只有在下一次渲染指令发生时才会被采用,同时会产生INVALID_OPERATION错误。

program对象中正在实际使用的采样变量被称为当前采样变量。LinkProgram函数决定这个采样变量是否被激活。LinkProgram函数试图决定在program对象中包含的着色器中的激活的采样变量数据是否超过允许的最大限度。如果超过,链接就会失败(对于不同类型的着色器会有不同的限制)。每个激活的采样变量都被限度限制,甚至是多个采样变量指向同一个纹理图像单元。如果在链接时期不能决定,将会在下一次渲染指令发生时决定,同时产生* INVALID_OPERATION*错误。

  1. varying 变量

一个顶点着色器可以定义一个或者多个varying变量(见the OpenGL ES Shading Language specification)。这些值被用来在图元渲染时做插值运算。在着色器编程指南中,定义了一些内建的varying变量,这些变量由顶点着色器响应, 在顶点处理后的光栅化过程中是必要的。

处理varying变量时合适的插值数目,由相关的常量MAX_VARYING_VECTORS给出。这个值代表了可以被插值的四元素的浮点指针向量的数目。被声明为矩阵或者数组的varying变量会消耗多个插值器。当程序链接后,在顶点着色器或者从片段着色器中读取的varying变量,将会受到这个数字的限制。顶点位置(gl_Position)的转换矩阵不是varying变量,不受到限制。着色器访问的varying变量超过MAX_VARYING_VECTORS将会导致链接失败,除非设备相关的优化器可以在硬件资源和程序适配上发挥作用。

2.10.5 着色器执行

如果程序对象包含了顶点着色器,通过调用UseProgram 激活成功链接的程序对象,顶点着色器的执行版本将会处理顶点数据。

下面是顶点着色器执行的一些注意事项。

  1. 纹理访问

如果GL实现是支持的,顶点着色器有能力在纹理映射表中查询。顶点着色器最大的纹理图像单元的数目是MAX_VERTEX_TEXTURE_IMAGE_UNITS;最大数目是0意味着GL实现不支持在顶点着色器中访问纹理。GL片段阶段的最大数目的纹理图像单元数目是MAX_TEXTURE_IMAGE_UNITS。顶点着色器和片段处理加起来使用纹理图像单元数目不能超过MAX_COMBINED_TEXTUTRE_IMAGE_UNITS。如果顶点着色器和片段处理阶段访问相同的纹理图像单元,将会被当做是使用了两个纹理图像单元,受MAX_COMBINDED_TEXTURE_IMAGE_UNITS限制。

当纹理映射表在顶点着色器中执行时,过滤纹理值τ按照3.7.7和3.7.8节中描述的规则计算,纹理源的颜色Cs根据表3.12(3.8.2节)做转换。一个四通道的向量(Rs,Gs,Bs,As)返回给顶点着色器。

在顶点着色器中,使用纹理坐标相对于窗口坐标系的偏导数来执行自动的详细级别的计算是不可能的,如同3.7.7节那样。因此,是没有图像数组级别的自动选择的。纹理贴图的放大或者缩小,被一个详细的级别值控制,这个等级值可以在纹理查询函数中选择性的传入。如果纹理查询函数提供了显式的等级值l,等级偏好值就会被设置为λbase(x, y) = l(代替了3.11中的等式)。如果纹理查询函数没有提供显式的等级值,λbase(x, y) = 0。放缩因子ρ(x, y)和它的近似函数f(x,y)(见3.12节等式)会被忽略。

在顶点着色器使用采样变量将会在同样的条件下返回(R, G, B, A) = (0, 0, 0, 1),正如3.8.2节中在“纹理访问”中定义的片段着色器一样。

  1. 有效性验证

在链接期间判定一个program对象是否真正的执行是不可能的。因此,在第一个渲染命令(DrawArrays or DrawElements)发出后,来判定当前活跃的program对象是否被执行,有效性检验才完成。如果不能被执行,片段就不会被渲染,渲染指令会产生INVALID_OPERATION的错误。

这个错误还会在下面情况下产生:

  • 在当前prigram对象中任何两个活跃的采样变量是不同的类型,但是指向同一个纹理图像单元。

这些渲染命令产生报告的INVALID_OPERATION错误,可能不会提供足够的信息来找出为什么当前的活跃的program对象不会被执行。对于在执行的program对象,完全没有可用的信息,但是在当前GL状态下,这些信息是低效率的或者次优的。为了帮助开发者,使用函数:

void ValidateProgram(uint program);

来根据当前的GL状态验证program对象。每个program对象有一个布尔状态,VALIDATE_STATUS,这个状态随着有效性验证的结果改变。这个状态可以通过getProgramiv(见6.1.8节)查询。如果有效性验证成功,这个状态被设置为TRUE,否则被设置为FALSE。如果验证成功,program对象就会确保被执行,并给出当前GL的状态。如果验证失败,program对象保证不会被执行,并给出当前GL的状态。

函数ValidateProgram将会检查可能在渲染函数执行时会发生的导致INVALID_OPERATION错误的所有条件,其他的条件也可能会被检查。比如,会给出一些如果优化着色器代码的提示。一个空的program会验证失败。program对象的信息日志被验证结果的信息覆盖,这个信息日志也可能是空字符串。写在信息日志中的结果只有在应用开发期间是有用的;一个应用不应该期望在不同的GL实现中产生相同的信息。

缺少指令空间或者缺少临时变量不会导致着色器编译失败和program对象链接失败。代码实现应该保证所有的有效的着色器和程序对象可以被成功的编译,链接和执行。

  1. 未定义的行为

当在着色器中使用数组或者矩阵变量时,可以访问由运行时计算的索引的并超出了变量的声明的范围的变量。这些越界的访问有一些未定义的行为,会发生一些系统错误(可能包括程序终止)。对这些在着色器中发生的错误提供的保护是依赖于实现代码的。

2.10.6 规定的状态

GL通过维护状态来指明当前正在使用哪个着色器和program对象对应的名字。最开始,不存在着色器和program对象,也没有使用中的名字。

GL状态需要每个着色器对象由以下构成:

  • 一个无符号整形指定着色器对象的名字
  • 一个整形指定SHADER_TYPE的值
  • 一个布尔值指定删除状态,初始值为FALSE
  • 一个布尔值指定最后一次编译的状态,初始值为FALSE
  • 一个char类型的数组描述信息日志,初始值为空
  • 一个整形指定信息日志的长度
  • 一个char类型的数组包含一个串联的着色器代码字符串,初始值为空
  • 一个整形指定串联的着色器代码字符串的长度

GL状态需要每个program对象由以下组成:

  • 一个无符号整形指定program对象的名字
  • 一个布尔值指定删除状态,初始值为FALSE
  • 一个布尔值指定最后一次链接的状态,初始值为FLASE
  • 一个布尔值指定最后一次有效性验证的状态,初始值为FALSE
  • 一个整形指定附着在program上的着色器对象的数目
  • 一个无符号整形数组追踪附着在program上的着色器的名字
  • 一个char数组指定日志信息,初始值为空
  • 一个整形指定日志信息的长度
  • 一个整形指定活跃的统一变量的个数
  • 对每个活跃的统一变量,有三个整形表示统一变量的位置,大小和类型,以及一个char数组表示统一标量的名字
  • 一个数组持有每个活跃的统一变量的值
  • 一个整形持有活跃的属性变量的数目
  • 对每个活跃的属性变量,三个整形持有属性变量位置,大小和类型,以及一个char数组持有属性变量的名字。

另外,持有当前program对象的名字需要一个无符号的整形。在最开始,如在program对象设置为0时调用UseProgram,当前程序对象是无效的。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容