opengles 支持二进制shader,是为了节省在线编译的时间,但是二进制shader的移植性不好,各个厂商有自己
的二进制格式。有的显卡支持在线编译,可能有的显卡只支持二进制格式的shader,检测显卡是否支持在线编译
调用函数glGetBooleanv(GL_SHADER_COMPILER),如果支持在线编译shader就可以使用函数glShaderSource来指定shader
在编译完shader之后,你可以调用glReleaseShaderCompiler(void)函数暗示编译器可以释放shader编译器占的资源。
如果你的gpu只支持二进制格式的shader,根据opengles标准的规定,必须支持至少一种二进制格式,要判断gpu支持哪些格式
的二进制shader,可以使用下面的代码来检测:
GLboolean shaderCompiler;
GLint numBinaryFormats;
GLint *formats;
// Determine if a shader compiler available
glGetBooleanv(GL_SHADER_COMPILER, &shaderCompiler);
// Determine binary formats available
glGetIntegerv(GL_NUM_SHADER_BINARY_FORMATS, &numBinaryFormats);
formats = malloc(sizeof(GLint) * numBinaryFormats);
glGetIntegerv(GL_SHADER_BINARY_FORMATS, formats);
使用函数:glShaderBinary来指定二进制shader,二进制shader是离线shader编译器编译出来的,这个编译器跟gpu
厂商有关。有的厂商规定一个二进制shader文件里面需要有顶点shader和片段shader一起,而有的厂商支持单独的顶点二进制
shader和片段二进制shader,在然后在线将这两种二进制shader链接起来,对于有的厂商采用第一种方案,是由于link两种
二进制shader是比较耗时的操作。
gpu到底采用哪种方案可以在厂商特定的opengles扩展(二进制格式)里面可以看到。
对于采用二进制shader和在线编译shader这两种方案的区别是,支持在线编译shader的方案,可以从shader里面读取回
shader源代码,而二进制格式不能读回shader源代码,另外二进制格式的方案,比较节省时间。此外没有多大的区别了。
对opengles shader中的数组变量需要注意两个问题,第一,很多厂商都不支持在编译时使用变量去索引数组中的元素
float floatArray[4];
vec4 vecArray[2];
int n;
floatArray[n] = 3.0; //这样是不行的。下标必须是常数。
float myArr[4];
for(int i = 0; i < 3; i++)
{
sum += myArr[i]; // NOT ALLOWED IN OPENGL ES, CANNOT DO
// INDEXING WITH NONCONSTANT EXPRESSION
}
这里在延伸一个,对于for循环也有个限制:
uniform int loopIter;
// NOT ALLOWED IN OPENGL ES, loopIter ITERATION COUNT IS NONCONSTANT
for(int i = 0; i < loopIter; i++)
{
sum += i;
}
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
}
流控制语句和循环在opengles中最好少用,比较耗时。
但这种有一个例外,就是在顶点shader里面访问uniform类型的数组变量的时候可以使用变量作为下标来索引数组。
第二个需要注意的问题就是,不能在声明数组的时候给他赋初始值。
float floatArray[4] = {1.0,2.0,3.0,4.0} //这样是不行的。没有为数组赋初始值的语法。
必须一个一个的赋值。这种特性是由于gpu硬件的特定决定的。
预处理器相关:
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
__LINE__ // Replaced with the current line number in a shader
__FILE__ // Always 0 in OpenGL ES 2.0
__VERSION__ // The OpenGL ES shading language version (e.g., 100)
GL_ES // This will be defined for ES shaders to a value of 1
#version 100 //在shader开头必须设置为100 OpenGL ES Shading Language v1.00
opengles扩展:
// Set behavior for an extension
#extension extension_name : behavior
// Set behavior for ALL extensions
#extension all : behavior
例如:
#extension GL_OES_texture_3D : enable //后面这个值可以为require,enable,warn,disable
变量的精度:
highp vec4 position;
varying lowp vec4 color;
mediump float specularExp;
有三种精度类型,lowp mediump,highp,声明变量的时候要指定精度,如果不指定精度,就使用缺省精度
在shader的开头可以指定某种类型的缺省精度:
precision highp float; //指定float类型的缺省精度为highp
precision mediump int; //指定int类型的缺省精度为mediump
对于顶点shader来说如果没有指定缺省精度,对于int和float类型来说默认都是highp。
但是对于片段shader来说,对于float类型一定要明确设定缺省精度,或者对每个float类型的变量都指定精度。
opengles2.0标准没有规定片段shader必须支持highp精度,所以如果想要在片段shader中使用highp精度,
需要判断下:
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
还有一个扩展项:OES_fragment_precision_high,也可以判断是否片段shader支持highp精度。
invariant关键字:
只能在顶点shader中使用这个关键字,这个关键字用于修饰变量的,告诉编译器不要对这个变量进行优化,
避免同样的代码在不同的时刻产生不同的结果,尤其是在进行多通路渲染物体的时候。可以用这个关键字
修饰内建的变量,也可以修饰自己定义的变量:
invariant gl_Position;
invariant varying texCoord;
也可以指定对所有的变量都使用这个关键字:
#pragma STDGL invariant(all)
但是这个会shader编译器的优化功能不起作用,还是要看需要来设置invariant
顶点shader里面的attribute vec或mat变量可以当做数组来索引,可以使用变量下标去索引:
attribute vec4 a_matrixweights;
float m_wt = a_matrixweights[i];//这个有可能编译不通过,opengles并没有强制要求支持这种特性。
顶点shader的内建变量:
1 内建的特殊输出变量:
gl_Position 用于输出顶点的clip 坐标,就是顶点坐标*modelviewproj矩阵之后的坐标。精度为highp
gl_PointSize 以像素为单位的点精灵的点的大小,渲染点精灵的时候用的,精度为mediump,会被clip到
平台所支持的点精灵的大小范围。
gl_FrontFacing 不有不需要你在顶点shader里面直接写这个值,系统自动设置,是boolean变量。
2 内建的uniform state:
只有一个内建的uniform state:
struct gl_DepthRangeParameters
{
highp float near;
highp float far;
highp float diff;
}
uniform gl_DepthRangeParameters gl_DepthRange;
3 内建的常量
const mediump int gl_MaxVertexAttribs =8;
const mediump int gl_MaxVertexUniformVectors = 128;
const mediump int gl_MaxVaryingVectors = 8;
const mediump int gl_MaxVertexTextureImageUnits = 0;
const mediump int gl_MaxCombinedTextureImageUnits = 8;//顶点shader+片段shader所支持的纹理单元数。
右边指定的值是每个平台必须支持的最小的值。
平台实际支持的值可以这样来获取:
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS,&i)
glGetIntegerv(GL_MAX_VERTEX_UNIFORM_VECTORS,&i);
glGetIntegerv(GL_MAX_VARYING_VECTORS,&i);
glGetIntegerv(GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS,&i);
glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS,&i);
片段shader:
内建变量:
gl_FragColor:
这个变量用于输出最后的颜色值到颜色缓存。可以使用glColorMask
关闭对颜色缓存的写入。
gl_FragCoord:
这是一个只读变量,保存显示在窗口中的坐标:(x, y, z, 1/w)
gl_FrontFacing:
这也是个只读变量,表面当前片段是不是属于正面。
gl_PointCoord:
只读变量,用于渲染点精灵的时候用的,保存的是点精灵的纹理坐标。
片段shader里面如何做alpha测试:
主要是使用discard关键字。
片段shader里面如何做User Clip Planes:
也是使用discard关键字,平面的方程:Ax + By + Cz + D = 0,(A,B,C)为平面法向量
D为原点到平面的距离,判断一个点P是否在平面的上方:
如果Dist = (A × P.x) + (B × P.y) + (C × P.z) + D > 0,就表示这个点在平面的上方。
program
关于program中的uniform和attribute变量:
uniform变量:
这种变量在shader里面是只读的,当顶点shader和片段shader被编译连接成program后,
他们的uniform变量都是放在一个set里面了。如果顶点shader和片段shader里面有同名并且
相同类型的unifrom变量,那这两个变量就合并成一个了。link program的过程中会给这些
uniform变量指定location。
active uniform变量:program程序有一个active unifrom变量列表,
如果你的shader里面声明了一个unifrom变量,但你在shader里面从来没有
使用过它,那么这个uniform变量会被编译器优化掉,不会放在active uniform变量列表里面。
获取active unifrom变量的个数以及纤细信息:
glGetProgramiv(GL_ACTIVE_UNIFORMS)
glGetProgramiv(GL_ACTIVE_UNIFORM_MAX_LENGTH)
//这个函数获得active uniform变量列表中名字最长的变量名的长度。
glGetActiveUniform()
使用这个函数获得某个active uniform变量的所有信息,类型,名字等。
glGetUniformLocation(GLuint program,const char* name)
获得unifrom变量的location。
glUniformNf
glUniformNfv (N可以为1-4)
glUniformNi
glUniformNiv (N可以为1-4)
glUniformMatrixNfv (N可以为2-4)
上面的这些个函数是设置各种类型的uniform变量的值。
uniform变量可以是数组,glGetActiveUniform得到unifrorm变量的长度
根据长度选型使用哪个函数来设置变量的值。