美文网首页
OpenGL 第五个实例 glsl语言的应用

OpenGL 第五个实例 glsl语言的应用

作者: Alexander | 来源:发表于2021-10-20 09:31 被阅读0次

    glsl是一个以C语言为基础的高阶着色器语言, 通过编写glsl提供开发和对绘图管线更多的直接控制,不再需要使用汇编语言或者其他硬件规格的语言.

    GLSL中提供了许多内建的函数,来方便我们的使用。可以在官方手册中查找相关的函数

    GLSL官方手册

    ![OpenGL流程] image.png

    常用修饰符

    • const: 常量值必须在声明时初始化,只读属性,不可更改.
    • attribute:表示只读的顶点数据,只会用在顶点着色器中,数据来自当前的顶点状态或者顶点数组,首先attribute必须是全局范围声明的,不能在函数内部声明attribute属性, 被attribute修饰的属性可以是浮点类型的值, 举证或者向量, 但不可以是结构体或者数组.
    • uniform:一致变量,在着色器执行期间一致变量的值是不变的,与const常量不同的是这个值在编译时期是未知的,且是由着色器外部进行初始化,一致变量在顶点着色器和片段着色器之间数据是共享的,所以它只能在全局范围进行声明.
    • varying: 顶点着色器的输出,例如颜色或纹理坐标, 插值后的数据作为片段着色器的只读输入数据,必须是全局声明的全局变量,可以是向量,矩阵,浮点类型的标量,但不能是结构体和数组.

    常用的内置变量

    • gl_Position: vec4类型的输出属性, 即变换后的顶点位置,用于固定的裁剪操作, 所有的顶点着色器都必须要使用到这个内置变量.
    • gl_FragColor: vec4类型, 是片段着色器的输出颜色.

    例如: 下面的顶点着色器

    
    attribute vec4 position;
    attrubute vec2 textCoordinate;
    
    uniform mat4 rotateMatrix;
    varying lowp vec2 varTextCoord;
    
    vodi main() {
        
        varTextCoord = textCoordinate;
        vec4 vPods = position;
        vPods = vPods * rotateMatrix;
        gl_Position = vPods;
    }
    

    GLSL 语言

    GLSL中提供了许多内建的函数,来方便我们的使用。可以在官方手册中查找相关的函数GLSL官方手册

    注释

    单行注释://
    多行注释:/**/
    

    变量

    GLSL的变量命名方式和C语言一样,变量名称可以使用数字、下划线、字母组成。但是不能以数字开头。需要注意的是GLSL的变量并不能以gl_作为前缀,原因是其官方保留了这个前缀作为GLSL的内部变量。

    基本类型

    GLSL中除了整型、布尔类型、浮点类型外,它还引入了一些着色器用到的数据基本类型,他们同样能作为结构体累不的类型,具体看下图。

    数据类型 类型描述
    void 跟C语言的void类似,表示空类型,也可作为函数的返回值,表示的是无返回值返回
    bool 布尔类型,返回的是true或者false的表达式
    int 整型
    folat 浮点类型
    bvecn 表示包含n个bool类型的分量,eg:bvec2、bvec3、bvec4
    ivecn 表示包含n个int类型的分量,eg:ivec2、ivec3、ivec4
    mat2、mat2x2 2x2的浮点矩阵类型
    mat3、mat3x3 3x3的浮点矩阵类型
    mat4x4 4x4的浮点矩阵类型
    matnxm nxm的浮点矩阵类型,eg:mat2x3(2列3行的矩阵)、mat3x4(3列4行的浮点矩阵)。OpenGL的矩阵是列主顺序的
    sampler1D 用于内奸的纹理函数中引用指定的1D纹理句柄,只可以作为一致变量或者函数中使用
    sampler2D 2维纹理句柄
    sampler3D 3维纹理句柄
    samplerCube cube map纹理句柄
    sampler1DShadow 一维深度纹理句柄
    sampler2DShadow 二维深度纹理句柄

    结构体

    结构体可以组合基本数据类型和数组来自定义结构体,在定义一个结构体的同时,也可以定义一个结构体实例给它,或者后面再定义也是可以的。

    • 1,使用 =符号来给结构体赋值
    • 2,使用==!=来判断两个结构体是否相同,只有两个结构体中的所有成员变量都相同时,两个结构体才相等,和iOS一样使用点语法进行访问。

    结构体中至少要包含一个成员变量,固定大小的数组也是可以为成员的,GLSL语言中是不允许结构体嵌套的,但是如果结构体已经预先声明的话,也是可以嵌套到定义的结构体中。

    struct velocity {
        
        vec3 direction;
    }velo;
    

    数组

    在GLSL中只能定义一维数组,数组的类型可以是GLSL中的任何基本数据类型,我们也可以在定义数组的是时候指定显示大小的数组,这个数组可以是参数也可以是返回值。

        vec4 lightPositions[8];
        vec4 lightPos[] = lightPositions;
    

    获取数组长度大小可以用length,例如:lightPositions.length // 打印结果为8

    修饰符

    修饰符 修饰符描述
    const 常量值修饰符,必须在声明时初始化,只读属性,不可写
    attribute 表示只读的顶点数据,只用在顶点着色器中,数据来自当前的顶点状态或者顶点数组,它修饰的属性必须是全局范围声明的属性,也就是说不能在函数内部,一个attribute修饰的属性可以是浮点类型的标量、向量、或者矩阵。重点是:attribute不能修饰数组和结构体
    uniform 一致变量,在着色器执行期间一致变量的值是不变的,与const常量不同的是,这个值在编译时期是未知的且有着色器外部进行初始化,一致变量在顶点着色器和片段着色器之间数据是共享的,所以它修饰的属性只能是全局范围进行声明的属性值。
    varying 顶点着色器的输出,例如纹理坐标和颜色,(插值后的数据)作为片段着色器的只读输入数据,同样必须是全局范文声明的全局变量,可以是浮点类型的标量、向量、举证,但同样不能修饰数组和结构体
    centoridvarying 在没有多重采样的情况下,与varying的特性是一样的,而在多重采样时,centoridvarying在光栅化的图形内部进行求值,而不是在片段中心的固定位置求值。
    invariant 用于表示定点着色器的输出和任何匹配片段着色器的输入,在不同的着色器中计算产生的值必须是一致的,所有的控制流和数据流,写入一个invariant变量也是一致的。编译器为了保证结果是完全一致,需要放弃那些可能会导致不一致值的潜在优化,所以,除非必要,不要使用这个修饰符,在多通道渲染中必变z-fighting时可能会被用到。
    in 用在函数的参数中,目的是表示函数时输入的,在函数中改变这个值,并不会影响对调用函数,相当于是C语言中的传值,是一个默认修饰符。
    out 用在函数中,修饰的参数表示是输出参数,值是会改变的
    inout 用在函数的参数,表示这个蚕食既是输入参数也是输出参数

    内置变量

    内置变量可以与固定的函数功能进行交互,使用签不需要声明

    顶点着色器可用的内置变量。
    内置变量名称 类型 描述
    gl_Color vec4 输入属性, 表示顶点的主颜色
    gl_SeconddaryColor vec4 输入属性,表示的是顶点的辅助颜色
    gl_Normal vec3 输入属性,表示顶点的法线值
    gl_Vertex vec4 输入属性,表示物体空间的顶点位置
    gl_MultiTexColor vec4 输入属性,表示的是顶点的第n个纹理的坐标
    gl_FogCoord float 输入属性,表示顶点的雾坐标
    gl_Position vec4 输出属性,变换后的顶点位置,用于后面的裁剪等操作,所有的顶点着色器都必须写这个值
    gl_ClipVertex vec4 输出坐标,用于用户裁剪平面的裁剪
    gl_PointSize float 点的大小
    gl_FrontColor vec4 正面的主颜色的varying输出
    gl_BackColor vec4 背面主颜色的varying的输出
    gl_FrontSecondaryColor vec4 正面的辅助颜色的varying的输出
    gl_BackSecondaryColor vec4 背面的辅助颜色的varying
    gl_TexCoord[] vec4 纹理坐标的数组varying输出
    gl_FogFragCoord float 雾坐标的varying输出
    片段着色器的内置变量
    内置变量名称 类型 描述
    gl_Color vec4 包含主颜色的插值,只读输入属性
    gl_SecondarycOLOR vec4 包含辅助颜色的插值,只读输入属性
    gl_TexCoord[] vec4 包含纹理坐标数组的插值,只读输入属性
    gl_FogFragCoord float 包含了雾坐标的插值,只读输入属性
    gl_FragCoord float 只读输入属性,表示窗口的x、y、z和1/w
    gl_FrontFaciong bool 只读输入属性,如果是窗口正面图元的一部分,则这个值为true
    gl_PointCoord vec2 点精灵的二维空间坐标范围在(0.0, 0.0)到(1.0, 1.0)之间,仅用于点图元和点精灵开启的情况下。
    gl_FragData[] vec4 使用glDrawBuffers输出的数据数组。不能与gl_FragColor结合使用。
    gl_FragColor vec4 输出的颜色用于随后的像素操作
    gl_FragDepth float 输出的深度用于随后的像素操作,如果这个值没有被写,则使用固定功能管线的深度值代替

    表达式

    GLSL语言的操作符与C语言相似,优先级如下表从高到底排列

    操作符(优先级: ) 描述
    () 用于表达式组合,函数的调用,构造
    [] 数组下标,向量或矩阵选择器
    . 结构体和向量的成员选择(点语法)
    ++ - 前缀或后缀的自增自减的操作符
    "+" "-" ! 一元操作符,表示正、负、逻辑非(有双引号是因为对应操作符在markdown中是特使符号)
    */ 乘、除操作符
    ”+“、”-“ 二元操作符, 表示加减操作
    <> <= >= == != 小于,大于,小于等于, 大于等于,等于,不等于 判断符
    && 逻辑与
    ?: 条件判断符
    = += –= *= /= 赋值操作符
    , 表示序列

    数组访问

    GLSL数组下表同样也是从0开始, 其范围是在[0, length-1],若数组越界,就会被系统定义为行为未定义, 当然,如果着色器的编译器在编译时识别数组越界,就会提示编译失败。

    vec4 myColor, ambient, didduse[6], specular[6];
    
    myColor = ambient + didduse[4] + specular[4];
    

    构造函数

    构造函数可以用于初始化包含多个成员的变量、数组、结构体,构造函数也可以使用在表达式中

    
    vec3 myNormal = vec3(1.0, 1.0, 1.0);
    greenTint = myColor + vec3(0.0, 1.0, 0.0);
    ivec4 myColor = ivec4(255);
    
    

    成分选择

    向量中单独的成分是可以通过{x,y,z,w}、{r,g,b,a}或者{s,t,p,q}的记法来表示,这些不同的记法用于顶点、颜色、纹理坐标。在成分选择中还可以使用特殊方式,比如重复使用向量中的元素、颠倒顺序等, 但是如果是在赋值时,就不能重复使用。

    // 调换顺序
    vec3 yxz = myVec.yxz;
    
    // 重复使用
    vec4 mySstt = myVec.sstt;
    
    // 在赋值时, 可以自主选择想要的顺序,但是不能重复成分
    vec4 myColor = {0.0, 1.0, 2.0, 1.0};
    myColor.x = -0.1;
    myColor.yz = vec2(3.0, 5.0);
    myColor.wx = vec2(1.0, 3.0);
    
    // 不合法的使用
    myColor.zz = vec2(2.0, 3.0);
    
    

    我们可以使用下标来访问向量矩阵中的元素,如果越界,那么当前行为就会被定义为未定义的行为事件。

    循环

    同C语言和C++语言一样,GLSL语言也提供了for、while、do/while的循环方式,continue则继续下一次循环,break这停止并结束当前循环。

    for (l = 0, l < macCount, l++) {
        if (!arr[l])
        continue;
        a += arr[l];
    }
    while(i < maxCount) {
        sum += ary[i];
        i++;
    }
    

    控制流

    if (a > 0) {
    a = c;
    } else {
    a =b;
    }

    discard

    在片段着色器中一种特殊的控制流即:discard。在片段着色器中使用discard会退出片段着色器,不会再继续执行片段着色器,片段也不会写入帧缓冲区中。

    if (color.a < 0.9)
    discard; // 退出当前片段着色器,不会再执行之后的的相关渲染工作
    

    main

    每个shader中必须要有一个main()函数,main
    函数中的void参数是可选的,但如果返回值是void时,那么就是必须的。

    void main(void) {
        ....
    }
    

    GLSL中的函数,必须是全局范围定义和声明的,不能在函数定义中国声明或者定义函数,函数必须要有返回值类型,但参数是可选的,参数可以是intoutinoutconst等。

    
    // 声明函数
    
    // 函数声明
    bool isAnyNegative(const vec4 v) {
        
        bool isNegative = isAnyNegative(gl_Color);
    }
    
    // 定义函数
    bool isAnyNegative(const vec4 v) {
        
        if (v.x < 0.0 || v.y < 0.0 || v.z < 0.0 || v.w < 0.0) {
            return true;
        } else {
            return false;
        }
    }
    

    在GLSL中函数名称是可以重名的,只需要参数个数或者类型不同即可

    > float min(float a, float b) {
      return a+b;
    }
    
    vec3 min(vec3 v1, vec3 v2) {
      return v1+v2;
    }
    

    运算符

    运算符(优先级: ) 运算符 说明
    1 () 聚组:a*(b+c)
    2 []、()、++、-- []是数组下标;()是函数调用:fun(arg1, arg2);. 则是链式访问,比如myStruct.a;++或者--则表示自增和自减。
    3 ++、--、+、—、! ++自增,--自减,正负号(一般正号不写)a ,-a;!a表示取反
    4 *、/ 乘或除运算符
    5 +、- 加减数学运算符
    6 < > <= >= 关系运算符
    7 == != 相等性运算符
    8 && 逻辑与
    9 ^^ 逻辑排他或(用处基本等于!=)
    10 II 逻辑或
    11 ? : 三目运算符
    12 = += -= *= /= 赋值与复合赋值
    13 , 顺序分配符

    glsl中是没有隐式类型转换的,所有在glsl中要求的是任何表达式的左右值类型必须是一致的。

    总结

    在图形管道中GLSL是可直接执行OpenGL的着色语言的,着色器中最常用的两种着色器:顶点着色器(该着色器是将形状转换到真实的3D坐标系中)和片元着色器(主要是计算最终渲染的颜色和其他属性)。

    顶点着色器回顾

    顶点着色器的操作是3D空间的坐标并且每个顶点都会调用一次这个函数,其目的是设置gl_Position变量,这个变量是一个特殊的全局内置变量,是用来存储当前顶点的位置;

    
    void main() {
    
        gl_Position = ...;
    }
    
    

    这个void main()函数时定义全局的gl_Position变量的标准方式,所有在这个函数中代码都会被着色器执行,比如:将3D空间中的位置投射在2D屏幕上时,这些信息do偶会被保存在计算结果的变量中。

    顶点着色器的内置变量:
    gl_Position:顶点着色器的裁剪空间输出位置向量,只要是要在屏幕上显示,就需要在顶点着色器红设置gl_Position
    gl_pointSize : 比如它其中一个图元是 GL_POINTS,说明每一个顶点都是一个图元,都会被渲染成一个点,我们可以同OpenGL中的glPointSize函数来设置渲染出来点的大小,也可以在顶点着色器中修改这个值。换句话说,它能对每个顶点设置不同的值,用来设置像素的size,是个float类型。
    gl_VertexIDgl_Positiongl_PointSize都是输出变量。因为他们的值是作为顶点着色器的输出被外界读取,我们可以对他们进行写入,从而改变结果,而gl_VertexID就可以用来进行读取操作。

    片段着色器回顾

    片段(或纹理)着色器在计算时定义了一个像素的rgba颜色值,每个像素只会调用一次片段着色器,这个着色器的作用是设置gl_FragCoord,这个变量同样属于内置变量,它的z分量等于对应片段着色器的深度值。

    gl_FragCoord的x分量和y分量记录的是当前片段在屏幕空间桌表系统下的x、y坐标。

    void main() {
        
        // 计算的结果就包含了rgba的颜色信息
        gl_FragCoord = ...;
    }
    

    相关文章

      网友评论

          本文标题:OpenGL 第五个实例 glsl语言的应用

          本文链接:https://www.haomeiwen.com/subject/ixftaltx.html