OpenGL ES 着色器
2021-01-17 本文已影响0人
lieon
获得链接后的着色器对象一般包括6个步骤
- 创建一个顶点着色器对象和一个片段着色器对象
- 将源代码连接到每个着色器对象
- 编译着色器对象
- 创建一个程序对象
- 将编译后的着色器对象连接对程序对象
- 链接程序对象
OpenGL ES 着色器语言
变量和变量类型
- 标量 float, int, uint, bool用于浮点,整数,无符号整数和布尔值的基于标量的数据类型
- 浮点向量 float, vec2, vec3, vec4 有1、2、3、4个分量的基于浮点的向量类型
- 整数向量 int, ivec2, ivec3, ivec4 有1、2、3、4个分量的基于整数的向量类型
- 无符号整数向量 uint, uvec2, uvec3, uvec4
- 布尔向量 bool, bvec2, bvec3, bvec4
- 矩阵 mat2, mat2x3
向量和矩阵的构造及选择
float myfloat = 1.0
float myfloat2 = 1; // Error: invalid type conversion
bool myBool = true;
int myInt = 0;
int myInt2 = 0.0; // Error: invalid type conversion
vec4 myVec4 = vec4(1.0); // myVec4 = {1.0, 1.0, 1.0, 1.0}
vec3 myVec3 = vec3(1.0, 0.0, 0.5); // myVec3 = {1.0, 1.0, 1.0}
vec3 tmp = vec3(myVec3);
myVec4 = vec4(myVec2, tmp); // myVec4 = {myVec2.x, myVec2.y, tmp.x, tmp.y}
mat3 myMat3 = mat3(1.0, 0.0, 0.0,
0.0, 1.0, 0.0,
1.0, 1.0, 1.0,
);
向量和矩阵分量
- 向量的单独分量可以用两种方式访问:使用 "." 运算符或者通过数组下标访问。根据组成向量的分量数量,每个分量可以通过使用 {x,y,z,w}、{r,g,b,a}或者 {s,t,p,q }组合访问
vec3 = myVec3 = vec3(0.0, 1.0, 2.0); vec3 tmp; tmp = myVec3.xyz; // tmp = {0.0, 1.0, 2.0} tmp = myVec3.xxx; // tmp = {0.0, 0.0, 0.0} tmp = myVec3.zyx; // tmp = {2.0, 1.0, 0.0} mat4 myMat4 = mat4(1.0); vec4 colo = myMat[0]; float m1_1 = myMat4[1][1]; float m2_2 = myMat4[2].z; // Get element at [2][2] ```
常量
const float zero = 0.0;
const float pi = 3.14159
结构体和数组
- 结构体
struct fogStruct { vec4 color; float start; float end; } fogVar; fogvar = fogStruct(vec4(1.0, 1.0, 0.0, 0.0), 0.5, 2.0 )
- 数组
float array[4]; vec4 vecs[2];
运算符,控制流和函数
- 运算符,与C语言一样,除了 == 和 != 之外,比较运算符只能标量。要比较向量,可以使用内建函数,逐个分量进行比较
- 函数,与C语言一样
- 内建函数
float nDotL = dot(normal, light); float rDotV = dot(vieDir, (2.0 * normal) * nDotL - light); float specular = specularColor * pow(rDotV, specularPower);
输入/输出变量、统一变量、统一变量块和布局限定符
统一变量
- OpenGL ES着色器语言中的变量类型限定符之一是统一变量,存储应用程序通过OpenGL ES 3.0API传入着色器的只读值。本质上,一个着色器的任何参数在所有顶点或者片段中都应该统一变量的形式传入。在编译时已知值的变量应该是常量,而不是统一变量,这样可以提高效率
- 统一变量是在全局作用域中声明,只需要统一限定符
uniform mat3 viewProhMat; uniform mat3 viewMat; uniform vec3 lightPos;
- 统一变量通常保存在硬件中,这个区域称作为 “常量存储区”, 是硬件中为存储常量值二分配的特殊空间
统一变量块
#version 300 es
uniform TrnasformBlock {
mat4 matViewProj;
mat3 matNormal;
mat3 matTexGen;
}
layout(location = 0) in vec4 a_position;
void main() {
gl_position = matViewProj * a_position;
}
布局限定符
- 布局限定符可用于指定支持统一变量块的统一缓冲区对象在内存中的布局方式。布局限定符可以提供给单独的统一变量块,或者用于所有统一变量块。
layout(shared, colum_major) uniform; // dafault if not layout(packed, row_major) uniform; // specified
- 单独的统一变量块也可以通过覆盖全局作用域上的默认设置来设置布局。此外,统一变量块中的单独统一变量也可以指定布局限定符,如下:
layout(std140) uniform TransformBlock { mat4 matViewProj; layout(row_major) mat3 matNormal; mat3 matTextGen; };
- 统一变量布局限定符
- shared shared限定符指多个着色器或者多个程序中统一变量块的内存布局相同。要使用这个限定符,不同的定义中的row_major/colomn_major值必须相等。
- packed packed布局限定符指定编译器可以优化统一变量块的内存布局。使用这个限定符时必须查询偏移位置,而且统一变量块无法在顶点/片段着色器或者程序间共享
- std140
- row_major 矩阵在内存以行优先顺序布局
- column_major 矩阵在内存以列优先顺序布局
- 统一变量布局限定符
顶点和片段着色器输入/输出
- OpenGL ES着色器语言的另一个特殊变量类型是顶点输入变量,顶点输入变量用于指定顶点着色器中的每个顶点的输入。用in关键字指定。它们通常存储位置,发现,纹理坐标和颜色
#version 300 es uniform mat4 u_matViewProj; layout(location = 0) in vec4 a_position; // 使用layout限定符用指定顶点属性的索引 layout(location = 1) in vec3 a_color; out vec3 v_color; // v_color 为输出变量,其内容从a_color输入变量中复制而来。每个顶点着色器将在一个或者多个输出变量中输出需要传递给片段着色器的数据 void main(void) { gl_position = u_matViewProj * 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(void) { o_fragColor = vec4(v_color, 1.0); }
- 片段着色器将输出一个或者多个颜色。在典型的情况下,只渲染都一个颜色缓冲区,在这种情况下,布局限定符是可选的(假定输出变量进入位置0),在片段着色器中会有一个输出变量,该值将是传递给管线逐片段操作部分的输出颜色。但是,当渲染到多个渲染目标(MRT)时。我们可以使用布局限定符指定每个输出前往的渲染目标
插值限定符
- 在没有使用限定符时,默认的插值行为是执行平滑着色。也就是说,来自顶点着色器的输出变量在图元中线性插值,片段着色器接收线性插值之后的数值作为输入。
// ...顶点着色器 // 顶点着色器的输出 smooth out vec3 v_color; // ...片段着色器 // v_color 从顶点着色器输出作为输入 soomth in vec3 v_color;
- 平面着色输出/输入
// ...顶点着色器 // 顶点着色器的输出 flat out vec3 v_color; // ...片段着色器 // v_color 从顶点着色器输出作为输入 flat in vec3 v_color;
预处理和指令
- 与C++一致,但是宏不能定义为带参数的
统一变量和插值器打包
- 没看懂
精度限定符
- 变量可以声明为低,中,高精度
- 在没有正确使用精度限定符时可能造成伪像
- 在OpenGL ES规范中没有规定底层硬件中必须支持多种精度,所以某个OpenGL ES实现在最高精度上进行所有运算并简单地忽略限定符是完全正常的。
- 在某些实现上,使用较低的精度可能带来好处
highp vec3 positionl varrying lowp vec3 color; mediump float specularExp; ``` - 如果变量声明时没有使用进度限定符,它将拥有默认精度 ```C++ precision highp float; precision mediump int; ```
不变性
-
invariant
, 一旦变量声明了不可变性,编译器便保证相同的计算和着色器输入条件下结果相同
#version 300 es
uniform mat4 u_viewProMat;
layout(location = 0) in vec4 a_vertext;
invariant gl_position
void main() {
gl_position = u_viewProMat * a_vertext;
}