OpenGL ES 着色语言中的函数声明方法同 C 语言中声明函数的方法相同,但是其在函数参数的传递方式上存在区别。
OpenGL ES 着色语言提供特殊的限定符,定义函数是否可以修改传递的参数。
限定符 | 说明 |
---|---|
in | 默认限定符,表示在参数按值传递,函数不能修改传递的参数值 |
inout | 表示变量按照引用传入函数,函数可以修改传入的变量 |
out | 表示变量的值并不传入函数,但是函数可以对传入的变量进行赋值 |
vec4 myFunc(inout float myFloat, out vec4 myVec4, mat4 myMat4);
OpenGL ES 着色语言中的函数不能递归,这是为了支持没有堆栈的 GPU 。
OpenGL ES 着色语言中提供了许多内建函数用于各种计算任务。
统一变量是 OpenGL ES 着色语言中的变量类型限定符之一,其用来存储应用程序通过 OpenGL ES 3.0 API 传入着色器的只读值。
当然,如果是在编译时就已经确定的变量,应该以常量传入着色器,而不是统一变量,这样可以提高效率。
uniform mat4 viewProjMatrix;
uniform vec3 lightPosition;
统一变量在全局作用域中声明,在一个程序对象中,其命名空间是与顶点着色器和片段着色器共享的,若两个着色器中声明同名的统一变量,那么其声明必须是相匹配的。
统一变量通常保存在硬件中,而因硬件的不同,所以程序中可以使用的统一变量数量受到限制。通过读取内建变量 gl_MaxVertexUniformVectors
和 gl_MaxFragmentUniformVectors
的值来确定,或者使用 glGetintegerv
来查询 GL_MAX_VERTEX_UNIFORM_VECTORS
和 GL_MAX_FRAGMENT_UNIFORM_VECTORS
来得到可用的统一变量的数量。
对于链接到同一个程序的着色器可以共享同一组统一变量,但是如果想要在不同的程序间共享一组统一变量,则需要使用统一变量缓冲区对象。
在 OpenGL ES 着色语言中,通过应用统一变量块实现统一缓冲区对象。
uniform TransformBlock
{
mat3 myMat1;
mat4 myMat2;
mat4 myMat3;
};
在统一变量块中声明的统一变量,可以在着色器中访问,如同常规形式声明的变量一样。
使用布局限定符可以改变统一变量块的统一缓冲区对象在内存中的布局方式,在全局作用域中使用限定符对 uniform 进行限定,那么进行的设置适用于所有的统一变量块。
layout(shared, column_major) uniform; //默认设置
layout(packed, row_major) uniform;
单独的对某一个统一变量块进行限定,那么其设置便会覆盖全局作用域中的默认设置,并且,统一变量块中的单独统一变量也可以指定布局限定符。
layout(std140) uniform TransformBlock
{
mat4 myMat4;
layout(row_major) mat3 myMat3;
mat2 myMat2;
}
限定符 | 说明 |
---|---|
shared | 指定多个着色器或者多个程序中统一变量块的内存布局相同,使用这个限定符,不同定义中的 row_major /column_major 值必须相等 |
packed | 指定编译器可以优化统一变量块的内存布局,使用这个限定符,必须查询偏移位置并且统一变量块无法共享 |
std140 | 指定统一变量块布局基于 OpenGL ES 3.0 规范中的标准规则 |
row_major | 矩阵在内存中以行优先顺序布局 |
column_major | 矩阵在内存中以列优先顺序布局(默认) |
OpenGL ES 着色语言中一个特殊的变量类型是顶点输入变量,或者称为顶点属性变量。顶点输入变量的意义在于其可以为顶点着色器中要绘制的每一个顶点设置数据,如顶点的位置、颜色、法线、纹理坐标等。同统一变量一样,顶点属性变量数量也是受底层硬件限制的,可以使用内建变量 gl_MaxVertexAttribs
得到支持的最大属性数量,也可以通过使用 glGetIngegerv
查询 GL_MAX_VERTEXT_ATTRIBS
得到。另外,还可以使用布局限定符指定顶点属性的索引,这样可以根据需要指定顶点属性变量的位置,如果不进行指定,那么链接程序将自动为顶点输入变量分配位置。
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec3 a_color;
使用 in 声明顶点输入变量,使用 out 声明顶点输出变量,而输出变量相应的就是片段着色器的输入变量,并且这两者的声明要相对应,并且不能使用布局限定符,由 OpenGL ES 自动选择位置。这个输出值,会在光栅化阶段对图元进行线性插值,所以这些变量通常被称作插值器。
类似的,可以根据内建变量 gl_MaxVertxOutputVectors
和 gl_MaxFragmentInputVectors
获取支持的最大顶点输出变量数最大片段输入变量数。另外,还可以使用 glGetIntegerv 查询 GL_MAX_VERTEXT_OUTPUT_COMPONENTS
和 GL_MAX_FRAGMENT_INPUT_COMPONENTS
得到相应的总分量值数量,注意这里得到的不是向量的数量。
对于片段着色器的输出,可以是一个或多个,所以当渲染多个目标时,可以使用布局限定符控制输出前往的渲染目标。
虽然顶点输出变量和片段输入变量不能使用布局限定符,但是可以使用插值限定符来指定着色方式,默认情况下,来自顶点着色器的输出变量在图元中线性插值,即执行平滑着色,而后,片段着色器接收线性插值后的数值作为输入。
//顶点着色器
smooth out vec3 v_color;
//片段着色器
smooth in vec3 v_color;
除了平滑着色,还可以选择平面着色。对于平面着色,图元中的值并没有进行插值,而是将其中一个顶点作为驱动顶点,将该顶点的值用于图元中所有的片段。
//顶点着色器
flat out vec3 v_color;
//片段着色器
flat in vec3 v_color;
使用关键字 centroid
可以开启质心采样,这样可以在使用多重采样时强制插值发生在被渲染的图元的内部,防止图元边缘出现伪像,但是这会影响像素的精确性。
//顶点着色器
smooth centroid out vec3 v_color;
//片段着色器
smooth centroid in vec3 v_color;