OpenGL ES-Shader语言基础

写在前面:

        本文是我学习Shader中的一些梳理,资料来源《OpenGL ES 3.X游戏开发上卷》。如果有对OpenGL ES基础不了解的可以去看我的另一篇博客OpenGL ES入门-名词扫盲icon-default.png?t=M276https://juejin.cn/post/7080801741008011277见解不尽正确,欢迎批评指正。

一、数据类型

1.1、标量

        标量从Java/C语言的角度来说就像是我们的基本数据类型,但是并没有byte/char/short/long/double这些。

  • bool 布尔值 true/false
  • int/uint 整形/无符号整型
  • float 浮点型数据

这些数据类型相对于有Java/C基础的完全不存在代沟

1.2、向量

        向量从Java/C语言的角度来说就像是我们的一维数组,数组的数据类型也就标量的那几种类型,容积的大小从2到4,例如Java上表示一个容积为3一维浮点型数组为float[3],那么着色器语言中表示就是vec3.除了浮点型还有和标量对应的bool/int/uint类型的向量。具体如下图:

向量主要存储颜色,位置,纹理坐标等一维数组类型数据。假定申明一个向量

vec3 my_vec;

我们访问的方式有两种

  • my_vec.r/my_vec.g/my_vec.b my_vec.x/my_vec.y/my_vec.z my_vec.s/my_vec.t/my_vec.q

        其实 r、g、b、a / x、y、z、w / s、t、p、q 都是可以访问的下标对应的0、1、2、3。只是可能为了可读性更高我们习惯用r、g、b、a 访问颜色向量,用x、y、z、w 访问坐标向量,用 s、t、p、q 访问纹理向量.如果只*vec2那么可以只使用前两位,*vec4可以使用四位去访问。

  • my_vec[0]/my_vec[1]/my_vec[2] 这种思想其实就是一种数组思想。

1.3、矩阵

        矩阵从Java/C的角度来讲像是一个二维数组,其实和我们线性代数里的矩阵理解一样。它两维的容积的大小都是从2到4,它的访问就和二维数组的访问方式一致,所有矩阵类型如下图:

        mat2和mat2x2其实是一种数据类型只是表达方式不一样而已,mat3/mat4同

1.4、采样器

        采样器是专门用于纹理处理的一类数据类型。一般情况下,一个采样器变量代表一副或一套纹理贴图,所有的采样器类型如下图:

        采样器不在着色器程序内部初始化,而是通过Java/C层传入进行赋值。

1.5、结构体

        和C语言基本一致,只是数据类型要是着色器语言定义的。

1.6、数组

        和Java/C语言基本一致,只是数据类型要是着色器语言定义的。

二、初始化

        个人觉得着色器语言的初始化是比较灵活的。标量的初始化和Java/C的方式基本一致,向量/矩阵/数组类型都像是Java/C++的构造函数一样。

矩阵的初始化也有一些灵活变化的技巧,具体分为如下几种情况。

  • 初始化时矩阵的各个元素既可以使用字面常量,也可以使用变量,还可以从其他向量直接 获取。
  • 初始化时若矩阵只有对角线上有值且相同,可以通过给出 1 个字面常量初始化矩阵。
  • 初始化时矩阵 M1 的行列数(N×N)小于构造器中矩阵 M2 的行列数(M×M)时,即 N<M,矩阵M1的元素值为矩阵M2左上角N x N个对应元素的值。
  • 初始化时矩阵 M1 的行列数(N×M)与构造器中矩阵 M2 的行列数(P×Q)不同,且 P 和 Q 之间的最大值大于 N 和 M 之间的最大值时(假设 M1 为 mat2×3,M2 为 mat4×2),矩阵 M1 左 上角 N×N 个元素的值为矩阵 M2 左上角 N×N 个元素的值,矩阵 M1 的其他行的元素值为 0。
  • 初始化时矩阵 M1 的行列数(N×N)小于构造器中矩阵 M2 的行列数(M×M)时,即 N>M,矩阵 M1 左上角 M×M 个元素的值为矩阵 M2 的对应元素值,矩阵 M1 右下角剩余对角线 元素的值为 1,矩阵 M1 剩余其他的元素值为 0。

        但对于一些特殊限定符in/uniform/out/const申明的数据类型不能进行初始化。由于着色器内建变量和内置函数的存在,为了避免命名冲突,我们应该不使用gl_开头的变量和函数命名。

三、限定符

3.1、存储限定符

        in 申明的变量在顶点着色器中主要接收来自渲染管线的数据,由宿主程序(Java/C)传入赋值,所以它本身不允许进行初始化。在片元着色器中in主要接收来自顶点着色器传入的数据。

        out表示向外输出数据,对于顶点着色器来讲可以通过使用out限定符指明输出的数据,而片元着色器通过in限定符接收,申明的变量名注意一致。

        uniform 可以用在顶点或片元着色器中,可以修饰所有基本数据类型,效果和in变量类似,例如我们想通过外部动态的传入一个值,就可以在着色器内部使用uniform来接收,宿主程序通过获取着色器程序内部的引用位置来进行赋值。

3.2、插值限定符

3.3、layout

        layout通常和(location=n)一起使用,用于指定in/out/uniform类型的参数所在的位置,一般如果不指定location,程序会自动依次指定location,那我们就需要代码去获取location。

3.4、精度

        在使用浮点型的数据类型时,在顶点着色器中可以直接声明浮点型数据类型,而在片元着色器中我们必须指定浮点型精度,否则会编译出错。我们可以通过指定精度来提高绘制效果或者避免编译错误。精度类型分为一下三种:

lowp 低精度 / mediump 中精度 / highp 高精度

四、内建变量

        内建变量是着色器语言内置的一些变量,我们无需特意声明他们,我们可以直接使用。所以一开始的时候我也莫名其妙,明明有个变量没有声明为什么能跑。

4.1、顶点着色器内建变量

4.1.1、内建输入变量

4.1.2、内建输出变量

4.2、片元着色器内建变量

4.2.1、内建输入变量

  • gl_FragCoord(vec4 类型)中含有当前 片元在视口中的坐标值 x、y、z 与 1/w(如图 4-3 所 示)。其中 x 与 y 分别为片元相对于视口的二维坐标。 如果视口的大小为 800*480(单位为像素),那么 x 的取值范围为 0~800,y 的取值范围为 0~480,z 部分为该片元的深度值。
  • gl_FrontFacing 是一个布尔型的内建变量,通过读取该内建变量的值可以判断正在处理的片元是否属于在光栅化阶段生成此片元所对应图元的正面。如果属于正面,gl_FrontFacing 的值为 true, 反之为 false。其一般用于开发双面光照功能相关的应用程序中。 对于点、线段等没有正反面之分的图元,其生成的片元都会被默认为是正面的。对于三角形 图元来说,其正反面取决于应用程序中对卷绕的设置及图元中顶点的具体卷绕情况。
  • gl_PointCoord 是 vec2 类型的内建变量,当启用点精灵时,gl_PointCoord 的值表示当前图元 中片元的纹理坐标,其值的范围从 0.0 到 1.0。如果当前图元不是一个点或者未启用点精灵, gl_PointCoord 的值是不确定的。

4.2.2、内建输出变量

        gl_FragDepth,其值为片元深度值,从OpenGL ES 3.0 开始允许开发人员对其进行赋值,然后送进深度缓冲内参与后继计算。

4.3、内建常量

4.4、内建uniform变量

五、总结

        着色器语言中的运算符,流程控制、函数声明等大致上和C语言大致相同,有C语言基础的都能看懂所以不做描述。处理内建变量其实还有大量的内置函数,内置函数我们前期可以通过类似查表法去查找我们需要用到的内置函数,篇幅较大所以在此处也不做描述。如有需要本文书籍的PDF可以评论区留下邮箱(ps:资源来源网络,仅个人学习不做商用)

猜你喜欢

转载自blog.csdn.net/qq_37841321/article/details/123908866