opengles2.0 shader备忘

           


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变量的长度

                根据长度选型使用哪个函数来设置变量的值。


  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值