美文网首页
(五)OpenGL ES着色语言

(五)OpenGL ES着色语言

作者: YongtaoHuang | 来源:发表于2019-10-11 21:43 被阅读0次

    上一篇:(四)着色器和程序:https://www.jianshu.com/p/b1b4656cef9e

    着色器是OpenGL ES 3.0 API的一个基础核心概念。每个OpenGL ES 3.0程序都需要且仅需要一个顶点着色器和一个片段着色器。

    OpenGL ES着色语言基础知识

    着色器的语法和C编程语言很相似,但是在版本规范原生数据类型上有重大区别。

    着色器版本规范

    首先第一行必须定义着色器使用的OpenGL版本,我们使用OpenGL ES 3.0:

    #version 300 es
    

    变量和变量类型

    计算机图形学中,两个基本数据类型组成了变换的基础:向量和矩阵。
    着色器语言中存在基于标量、向量和矩阵的数据类型,如下图:


    着色器语言数据类型.png

    变量构造器

    OpenGL ES着色语言在类型转换方面非常严格,不允许隐含类型转换。每种内建变量类型都有一组相关的构造器。

    向量和矩阵分量

    向量分量提取:

    vec3 tmp = vec3(0.0, 1.0, 2.0);
    // tmp.x = 0.0
    // tmp.xyzx = {0.0, 1.0, 2.0 0.0}
    

    矩阵分量提取:

    mat3 tmp = mat3{ 0.0, 1.0, 2.0, // 第一列
        3.0, 4.0, 5.0, // 第二列
        6.0, 7.0, 8.0} // 第三列
    // tmp[0] = {0.0, 1.0, 2.0}
    // tmp[1][1] = 4.0
    

    常量

    可以将任何基本类型声明为常数变量。

    const float pi =3.14159;
    

    结构体

    与C语言结构体类似:

    // 定义
    struct fogStruct {
        vec4 color;
        float start;
        float end;
    } fogVar;
    
    // 赋值
    fogVar = fogStruct(vec4(0.0, 1.0, 0.0, 0.0), // color
        0.5, // start
        2.0); // end
    

    数组

    与C语言数组类似:

    // 浮点数数组
    float floatArray[4];
    // 向量数组
    vec4 vecArray[2];
    // -------------------
    float a[4] = float[](1.0, 2.0, 3.0, 4.0);
    float b[4] = float[4](1.0, 2.0, 3.0, 4.0);
    vec2 c[2] = vec2[2](vec2(1.0), vec2(1.0));
    

    运算符

    着色器运算符和C语言的运算符一致:


    运算符1.png
    运算符2.png

    函数

    着色语言限定符有三种:in,inout和out:


    着色语言限定符.png

    函数定义示例,基本漫射光线的简单函数:

    vec4 diffuse(vec3 normal,
    vec3 light,
    vec4 baseColor) {
        return baseColor * dot(normal, light); 
    }
    

    注意:OpenGL ES著色语言的函数不能递归

    内建函数

    下面计算基本反射照明的着色器代码:

    float nDotL = dot(normal, light);
    float rDotV = dot(viewDir, (2.0 * normal) * nDotL - light);
    float specular = specularColor * pow(rDotV, specularPower);
    

    上述dot和pow都是OpenGL的内建函数。

    控制流语句

    主要包含判断逻辑和循环逻辑:
    判断:

    if(color.a < 0.25) {
        color *= color.a;
    } else {
        color = vec4(0.0);
    }
    

    循环:

    while(){
    }
    // =========
    do{
    } while();
    

    统一变量

    统一变量(uniform)是OpenGL ES着色语言中的变量类型限定符之一。

    uniform mat4 viewProjMatrix;
    

    统一变量的命名空间在顶点着色器和片段着色器中都是共享的。即,如果顶点着色器和片段着色器都链接到一个程序对象,它们就会共享同一组变量。
    统一变量通常保存在硬件中,这个区域被称为“常量存储”,其大小固定,所以统一变量个数受限。可以通过函数:

    gl_MaxVertexUniformVectors() // 获取最大顶点着色器数目
    gl_MaxFragmentUniformVectors() // 获取最大片元顶点着色器数目
    

    统一变量块

    统一变量块比统一变量的优势:
    1、统一变量缓冲区对象可以多个程序共享,但只需要设置一次;
    2、统一变量缓冲区对象一般可以存储更大量的统一变量;
    3、统一变量缓冲区对象之间切换比一次单加载一个统一变量更高效。

    uniform TransformBlock {
        mat4 matViewProj;
        mat3 matNormal;
        mat3 matTexGen;
    };
    

    统一变量块的内存布局限定符有shared、packed、std140、row_major和column_major:

    统一变量块的内存布局限定符.png

    默认方式是(shared, column_major):

    layout(shared, column_major) uniform;
    

    顶点和片段着色器输入/输出

    顶点输入变量用于指定顶点着色器种每个顶点的输入,用in关键字指定,它们通常存储位置、法线、纹理和颜色这样的数据。
    顶点着色器样板:

    #version 300 es
    uniform mat4 u_matViewProjection;
    layout(location = 0) in vec4 a_position; // 顶点位置 
    layout(location = 1) in vec3 a_color; // 顶点颜色
    out vec3 v_color;
    void main(void) {
        gl_Position = u_matViewProjection * a_position;
        v_color = a_color;
    }
    

    具有匹配的输出/输入声明的顶点和片段着色器:

    // 顶点着色器
    #version 300 es
    uniform mat4 u_matViewProjection;
    // 顶点着色器输入
    layout(location = 0) in vec4 a_position;
    layout(location = 1) in vec3 a_color;
    // 顶点着色器输出
    out vec3 v_color;
    void main(void) {
        gl_Position = u_matViewProjection * a_position;
        v_color = a_color;
    }
    // ==============================
    // 片元着色器
    #version 300 es
    precision mediump float;
    // 来自顶点着色器的输入
    in vec3 v_color;
    // 片元着色器输出
    layout(location = 0) out vec4 o_fragColor;
    void main() {
        o_fragColor = vec4(v_color, 1.0);
    }
    

    插值限定符

    OpenGL ES 3.0主要包含2种插值器:
    平滑着色:smooth
    平面着色:flat
    上述两种插值器还可以用质心采样:centroid来修饰。

    预处理器和指令

    预处理器:
    类似C++预处理器,可以使用如下指令定义宏和条件测试:

    #define
    #undef
    #if
    #ifdef
    #ifndef
    #else
    #elif
    #endif
    

    扩展行为指令:

    extension指令用于启用和设置扩展行为。下述示例即希望预处理器在

    NVIDIA阴影采样器立方体扩展不受支持时产生警告:

    #extension GL_NV_shadow_samplers_cube : enable
    

    统一变量和插值器打包

    底层硬件种可用于每个变量存储的资源是固定的。我们采用打包规则来节省程序所需内存空间。

    打包规则规定了插值器和统一变量映射到物理存储空间的方式,它基于物理存储空间被组织为一个存储位置4列和1行的网格的概念。举下面的统一变量块为例说明:

    uniform mat3 m;
    uniform float f[6];
    uniform vec3 v;
    

    未打包情况:


    未打包情况.png

    打包情况:


    打包情况.png

    因为GPU通常会按照向量位置索引对常量存储进行所以。打包必须使数组跨越行边界。

    了解打包很重要,这样才能编写再任何OpenGL ES 3.0实现上都不超过最小允许存储的可移植着色器。

    精度限定符

    着色器变量可以声明为低、中和高。

    // highp  高精度
    // mediump 中精度
    // lowp 低精度
    highp vec4 position;
    

    也可以用精度限定符指定某种类型的精度:

    precision highp float; // 所有float都是高精度 32bit
    precision mediump float; // 所有float都是中精度 16bit
    precision lowp float; // 所有float都是低精度 10bit
    

    不变性

    OpenGL ES着色器语言种引入了invariant关键字可以用于任何可变的顶点着色器输出,可以避免编译器可能进行导致指令重新排序的优化。
    一旦某个输出变量声明了不变性,编译器便保证相同的计算和着色器输入条件下结果相同。
    警告:慎用invariant,它回导致性能下降。

    小结

    上一篇:(四)着色器和程序:https://www.jianshu.com/p/b1b4656cef9e

    相关文章

      网友评论

          本文标题:(五)OpenGL ES着色语言

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