绘制纹理
精度限定符用于指定任何基于浮点或者整数变量的精度,关键字有高、中、低。
highp vec4 position;
varying lowp vec4 color;
mediump float specularExp;
精度限定符,还有默认精度的概念,如果没有设置,就设置默认的,默认精度限定符使用以下语法在顶点或片段着色器的顶部指定:
precision highp float;
precision mediump int;
为浮点指定的精度将用作基于浮点值的所有变量的默认精度。同样,为int指定的精度将用作所有基于整数的变量的默认精度。在顶点着色器中,如果没有指定默认精度,则int和float的默认精度都很高。
精度选择:
- 对于通常在顶点着色器中执行的操作,最可能需要的精度限定符是高精度限定符。
- 用矩阵变换位置,变换法线和纹理坐标,或者生成纹理坐标的操作需要高精度地完成。
- 颜色计算和光照方程很可能以中等精度完成。
这将取决于正在执行的颜色计算的种类以及正在执行的操作所需的范围和精度。我们相信highp很可能是顶点着色器中大多数操作使用的默认精度,因此在下面的示例中使用highp作为默认精度限定符。
Open gl es的着色器限制
这些限制应该会有所帮助开发人员编写了一个便携式顶点着色器,可以在大多数OpenGL ES 2.0实现上编译和运行。
顶点着色器的长度
指令计数超过顶点 着色器允许的最大指令,顶点着色器源将无法编译。所以出现了一些限制,来保证可以正常运行。
临时变量
临时变量是指在函数内部声明的变量或存储中间值的变量。
因为OpenGL ES着色语言是高级语言,所以没有办法指定所有OpenGL ES 2.0实现必须支持的最小临时变量数。
因此,顶点着色器可能会遇到这个问题,并且不会在所有ES 2.0实现上编译。
流量控制
OpenGL ES 2.0要求实现支持顶点着色器中的循环,而不要求它们必须展开。例如,您可以有一个For循环,循环索引从0到1023。这通常不会被着色器编译器展开,因为展开的着色器的代码大小对于大多数ES 2.0实现来说可能太大。以下限制适用于顶点着色器中使用的循环:
【循环可以减小程序的大小】
- 在for循环中只能使用一个循环索引。
- 循环索引必须初始化为常数整数表达式。
- for循环中声明的条件表达式必须是下列之一:
loop_indx < constant_expression loop_indx <= constant_expression loop_indx > constant_expression loop_indx >= constant_expression loop_indx != constant_expression loop_indx == constant_expression
- 只能使用以下表达式之一在for循环语句中修改循环索引:
loop_index-- loop_index++ loop_index -= constant_expression loop_index += constant_expression
- 循环索引可以作为只读参数传递给for循环中的函数(即循环索引可以与使用in参数限定符声明的参数一起使用)。
Examples of valid for loop constructs are shown here.
const int numLights = 4;
int i, j;
for (i=0; i<numLights; i++)
{
…
}
for (j=4; j>0; j--)
{
…
foo(j); // argument to function foo that takes j
// is declared with the in qualifier.
}
Examples of invalid for loop constructs are shown here.
uniform int numLights;
int i;
for (i=0; i<numLights; i++) // conditional expression is
// not constant
{
…
}
for (i=0; i<8; i++)
{
i = foo(); // return value of foo() cannot be
// assigned to loop index i
}
for (j=4; j>0;)
{
…
j--; // loop index j cannot be modified
// inside for loop
}
虽然OpenGL ES 2.0着色语言规范指定了while和do-while循环,但这并不是必需的,因此可能不被所有OpenGL ES 2.0实现所支持。
条件语句
完全支持以下条件语句,没有任何限制。
if(bool_expression)
{
…
}
if(bool_expression)
{
…
}
else
{
…
}
图形处理器通常并行执行具有多个顶点的顶点着色器或具有多个片段的片段着色器。
并行执行的顶点或片段的数量将取决于GPU的性能目标。
if和if-else条件语句中的bool_expression对于并行执行的顶点或片段可以有不同的值。
由于GPU并行执行的顶点或片段数量减少,这可能会影响性能。
我们建议,为了获得最佳性能,条件语句应该与bool_expression值一起使用,这些值对于并行执行的顶点或片段是相同的。如果使用统一的表达式,就会出现这种情况.
数组索引
完全支持制服(不包括采样器)的数组索引。数组索引可以是常量、统一值或计算值。采样器只能使用常数积分表达式进行索引。常数积分表达式是文字值(如4),常数整数变量(如const int sampler _ indx = 3;),或者常量表达式(例如3 + sampler_indx)。
属性矩阵和向量可以使用常数积分表达式进行索引。不强制使用非常数积分表达式对属性矩阵和向量进行索引。然而,这是一个非常有用的特性。下面的代码显示了一个执行顶点蒙皮的顶点着色器。a_matrixweights是一个存储矩阵权重的顶点属性,最多可存储四个矩阵。
attribute vec4 a_matrixweights; // matrix weights
attribute vec4 a_matrixindices; // matrix palette indices
int i;
for (i=0; i<=3; i++)
{
float m_wt = a_matrixweights[i];
int m_indx = int(a_matrixindices[i]) * 3;
…
}
以粗体突出显示的代码a_matrixweights[i]和a_matrixindices[i]不需要支持,因此可能无法编译。
注意:索引常数矩阵和向量、变量和变量或变量和变量数组的规则与已经描述的属性的规则相同
计算顶点着色器中使用的制服数量
gl _ MaxVertexUniformVectors描述顶点着色器中可以使用的最大制服数量。任何兼容的OpenGL ES 2.0实现都必须支持的gl _ MaxVertexUniformVectors的最小值是128个vec4条目。统一存储用于存储以下变量:
- 用统一限定符声明的变量。
- 常量变量。
- • Literal values.
- 特定于实现的常数
顶点着色器中使用的统一变量的数量以及用常量限定符、文字值和特定于实现的常量声明的变量必须符合第5章中描述的打包规则。如果这些不适合,那么顶点着色器将无法编译。开发人员可以应用打包规则,并确定存储统一变量、常量变量和文字值所需的统一存储量。无法确定特定于实现的常量的数量,因为该值不仅会因实现而异,还会根据顶点着色器使用的内置着色语言函数而变化。通常,当使用内置超越函数时,需要特定于实现的常数。
就文字值而言,OpenGL ES 2.0着色语言规范声明不假设常数传播。这意味着同一文字值的多个实例将被多次计数。可以理解,在顶点着色器中使用文字值(如0.0或1.0)更容易,但我们建议尽可能避免这种情况。应该声明适当的常量变量,而不是使用文字值。这避免了多次使用相同的文字值计数,如果顶点统一存储要求超过实现支持的范围,这可能导致顶点着色器无法编译。
考虑下面的例子,它描述了顶点着色器代码的一个片段,该代码为每个顶点变换两个纹理坐标:
#define NUM_TEXTURES 2
uniform mat4 tex_matrix[NUM_TEXTURES]; // texture matrices
uniform bool enable_tex[NUM_TEXTURES]; // texture enables
uniform bool enable_tex_matrix[NUM_TEXTURES]; // texture matrix
// enables
attribute vec4 a_texcoord0; // available if enable_tex[0] is true
attribute vec4 a_texcoord1; // available if enable_tex[1] is true
varying vec4 v_texcoord[NUM_TEXTURES];
v_texcoord[0] = vec4(0.0, 0.0, 0.0, 1.0);
// is texture 0 enabled
if (enable_tex[0])
{
// is texture matrix 0 enabled
if(enable_tex_matrix[0])
v_texcoord[0] = tex_matrix[0] * a_texcoord0;
else
v_texcoord[0] = a_texcoord0;
}
v_texcoord[1] = vec4(0.0, 0.0, 0.0, 1.0);
// is texture 1 enabled
if (enable_tex[1])
{
// is texture matrix 1 enabled
if(enable_tex_matrix[1])
v_texcoord[1] = tex_matrix[1] * a_texcoord1;
else
v_texcoord[1] = a_texcoord1;
}
刚才描述的代码可能导致对文字值0、1、0.0、1.0的每个引用都按照统一存储进行计数。为了保证这些文字值在统一存储中只计数一次,顶点着色器代码段应该编写如下
#define NUM_TEXTURES 2
const int c_zero = 0;
const int c_one = 1;
uniform mat4 tex_matrix[NUM_TEXTURES]; // texture matrices
uniform bool enable_tex[NUM_TEXTURES]; // texture enables
uniform bool enable_tex_matrix[NUM_TEXTURES]; // texture matrix
// enables
attribute vec4 a_texcoord0; // available if enable_tex[0] is true
attribute vec4 a_texcoord1; // available if enable_tex[1] is true
varying vec4 v_texcoord[NUM_TEXTURES];
v_texcoord[c_zero] = vec4(float(c_zero), float(c_zero),
float(c_zero), float(c_one));
// is texture 0 enabled
if(enable_tex[c_zero])
{
// is texture matrix 0 enabled
if(enable_tex_matrix[c_zero])
v_texcoord[c_zero] = tex_matrix[c_zero] * a_texcoord0;
else
v_texcoord[c_zero] = a_texcoord0;
}
v_texcoord[c_one] = vec4(float(c_zero), float(c_zero),
float(c_zero), float(c_one));
// is texture 1 enabled
if(enable_tex[c_one])
{
// is texture matrix 1 enabled
if(enable_tex_matrix[c_one])
v_texcoord[c_one] = tex_matrix[c_one] * a_texcoord1;
else
v_texcoord[c_one] = a_texcoord1;
}
希望这一节有助于很好地理解OpenGL ES 2.0着色语言的局限性,以及如何编写应该在大多数OpenGL ES 2.0实现上编译和运行的顶点着色器。
一个简单的顶点着色器
顶点着色器将位置及其相关的颜色数据作为输入或属性,通过4 × 4矩阵转换位置,并输出转换后的位置和颜色。
Example 8-1 A Simple Vertex Shader
// uniforms used by the vertex shader
uniform mat4 u_mvp_matrix; // matrix to convert P from
// model space to clip space.
// attributes input to the vertex shader
attribute vec4 a_position; // input position value
attribute vec4 a_color; // input vertex color
// varying variables – input to the fragment shader
varying vec4 v_color; // output vertex color
void
main()
{
v_color = a_color;
gl_Position = u_mvp_matrix * a_position;
}
然后,设置和光栅化阶段使用变换的顶点位置和图元类型将图元光栅化成片段。对于每个片段,插值的v_color将被计算并作为输入传递给片段着色器。