基本语法
向量构造
-
如果向量构造器中只提供了一个标量参数,则向量中所有值都会设定为该标量值。
-
如果提供了多个标量值或提供了向量参数,则会从左至右使用提供的参数来给向量赋值,如果使用多个标量来赋值,则需要确保标量的个数要多于向量构造器中的个数。
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, 0.0, 0.5}
vec3 temp = vec3(myVec3); // temp = myVec3
vec2 myVec2 = vec2(myVec3); // myVec2 = {myVec3.x, myVec3.y}
myVec4 = vec4(myVec2, temp, 0.0); // myVec4 = {myVec2.x, myVec2.y, temp, 0.0}
矩阵构造
-
如果对矩阵构造器只提供了一个标量参数,该值会作为矩阵的对角线上的值。例如 mat4(1.0) 可以构造一个 4×4 的单位矩阵。
-
矩阵可以通过多个向量作为参数来构造,例如一个 mat2 可以通过两个 vec2 来构造。
-
矩阵可以通过多个标量作为参数来构造,矩阵中每个值对应一个标量,按照从左到右的顺序。
在 OpenGL ES 中,矩阵的值会以列的顺序来存储。在构造矩阵时,构造器参数会按照列的顺序来填充矩阵,如下:
mat3 myMat3 = mat3(1.0, 0.0, 0.0, // 第一列
0.0, 1.0, 0.0, // 第二列
0.0, 1.0, 1.0); // 第三列
矢量和矩阵的分量
依据构成向量的组件个数,向量的组件可以通过 {x, y, z, w},{r, g, b, a} 或 {s, t, r, q} 等 swizzle 操作来获取。
在 OpenGL ES 2.0 中的某些情况下,数组下标不支持使用非常数的整型表达式(如使用整型变量索引),这是因为对于向量的动态索引操作,某些硬件设备处理起来很困难。
在 OpenGL ES 2.0 中仅对 uniform 类型的变量支持这种动态索引。
对于矩阵来说,可以通过数组下标“[]”来获取某一列的值。
vec3 myVec3 = vec3(0.0, 1.0, 2.0); // myVec3 = {0.0, 1.0, 2.0}
vec3 temp;
temp = myVec3.xyz; // temp = {0.0, 1.0, 2.0}
temp = myVec3.xxx; // temp = {0.0, 0.0, 0.0}
temp = myVec3.zyx; // temp = {2.0, 1.0, 0.0}
mat4 myMat4 = mat4(1.0); // Initialize diagonal to 1.0 (identity)
vec4 col0 = myMat4[0]; // Get col0 vector out of the matrix
float m1_1 = myMat4[1][1]; // Get element at [1][1] in matrix
float m2_2 = myMat4[2].z; // Get element at [2][2] in matrix
矢量和矩阵的操作
vec3 v, u;
mat3 m;
u = v * m;
// 等价于:
u.x = dot(v, m[0]); // m[0] is the left column of m
u.y = dot(v, m[1]); // dot(a,b) is the inner (dot) product of a and b
u.z = dot(v, m[2]);
u = m * v;
// 等价于:
u.x = m[0].x * v.x + m[1].x * v.y + m[2].x * v.z; // 取 m 的行 0 * 向量 v
u.y = m[0].y * v.x + m[1].y * v.y + m[2].y * v.z;
u.z = m[0].z * v.x + m[1].z * v.y + m[2].z * v.z;
mat m, n, r;
r = m * n;
// 等价于:
r[0].x = m[0].x * n[0].x + m[1].x * n[0].y + m[2].x * n[0].z;
r[1].x = m[0].x * n[1].x + m[1].x * n[1].y + m[2].x * n[1].z;
r[2].x = m[0].x * n[2].x + m[1].x * n[2].y + m[2].x * n[2].z;
r[0].y = m[0].y * n[0].x + m[1].y * n[0].y + m[2].y * n[0].z;
r[1].y = m[0].y * n[1].x + m[1].y * n[1].y + m[2].y * n[1].z;
r[2].y = m[0].y * n[2].x + m[1].y * n[2].y + m[2].y * n[2].z;
r[0].z = m[0].z * n[0].x + m[1].z * n[0].y + m[2].z * n[0].z;
r[1].z = m[0].z * n[1].x + m[1].z * n[1].y + m[2].z * n[1].z;
r[2].z = m[0].z * n[2].x + m[1].z * n[2].y + m[2].z * n[2].z;
// 创建一个 2x2 的矩阵
mat2 two = mat2(0.1, 0.2, // 第一列
0.3, 0.4); // 第二列
// 0.1 0.3
// 0.2 0.4
// 创建一个 3x3 的矩阵
mat3 three = mat3(0.1, 0.2, 0.3, // 第一列
0.4, 0.5, 0.6, // 第二列
0.7, 0.8, 0.9); // 第三列
// 0.1 0.4 0.7
// 0.2 0.5 0.8
// 0.3 0.6 0.9
结构体
struct customStruct {
vec4 color;
vec2 position;
} customVertex;
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 语言不同,在 GLSL 中,关于数组有两点需要注意:
-
除了 uniform 变量之外,数组的索引只允许使用常数整型表达式。
-
在 GLSL 中不能在创建的同时给数组初始化,即数组中的元素需要在定义数组之后逐个初始化,且数组不能使用 const 限定符。
函数
GLSL 中函数不能够递归调用,且必须声明返回值类型(无返回值时声明为 void)。
限定符
限定符 | 描述 |
---|---|
< none: default > | 局部可读写变量,或者函数的参数 |
const | 编译时常量,或只读的函数参数 |
attribute | 由应用程序传输给顶点着色器的逐顶点的数据 |
uniform | 在图元处理过程中其值保持不变,由应用程序传输给着色器 |
varying | 由顶点着色器传输给片段着色器中的插值数据 |
本地变量和方法参数只能使用 const 限定符,方法返回值和结构体成员不能使用限定符。
不包含任何限定符或者包含 const 限定符的全局变量可以包含初始化器,这种情况下这些变量会在 main() 函数开始之后第一行代码之前被初始化,这些初始化值必须是常量表达式。
没有任何限定符的全局变量如果没有在定义时初始化或者在程序中被初始化,则其值在进入 main() 函数之后是未定义的。uniform、attribute 和 varying 限定符修饰的变量不能在初始化时被赋值,这些变量的值由 OpenGL ES 计算提供。
全局变量限制符只能为 const、attribute、uniform 和 varying 中的一个,不可复合。
const 限定符
数组或者包含数组的结构体不能被声明为常量(因为数组不能在定义时被初始化)。
const float zero = 0.0;
const float pi = 3.14159;
const vec4 red = vec4(1.0, 0.0, 0.0, 1.0);
const mat4 identity = mat4(1.0);
attribute 限定符
attribute 变量只用于顶点着色器中,用来存储顶点着色器中每个顶点的输入(per-vertex inputs)。
attribute 通常用来存储位置坐标、法向量、纹理坐标和颜色等。注意 attribute 是用来存储单个顶点的信息。OpenGL ES 2.0 实现支持的最少 attribute 个数是 8 个。
// 顶点着色器 .vsh
attribute vec4 position;
attribute vec4 color;
varying vec4 colorVarying;
void main(void) {
colorVarying = color;
gl_Position = position;
}
uniform 限定符
OpenGL ES 2.0 的实现必须提供至少 128 个顶点 uniform 向量及 16 片段 uniform 向量。
uniform mat4 viewProjMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;
varying 限定符
OpenGL ES 2.0 实现中的 varying 变量最小支持数为 8。
varying 变量存在内插(interpolate)的过程。这些变量在片段着色器中需要有相对应的声明且数据类型一致,然后在光栅化过程中进行插值计算。
invariant 限定符
invariant 可以作用于顶点着色器输出的任何一个 varying 变量。所有的 invariant 输出量的上游数据流或控制流必须一致。
invariant gl_Position; // make existing gl_Position be invariant
varying mediump vec3 Color;
invariant Color; // make existing Color be invariant
invariant varying mediump vec3 Color;
#pragma STDGL invariant(all) // 所有输出变量为 invariant
invariant varying texCoord; // varying 在传递数据的时候声明为 invariant
precision 限定符
highp vec4 position;
varying lowp vec4 color;
mediump float specularExp;
// 默认精度限定符放在着色器代码起始位置
precision highp float;
precision mediump int;
在片段着色器中可以使用以下代码,判断是否支持在片段着色器中使用高精度。
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
浮点数范围 | 浮点数大小范围 | 浮点数精度范围 | 整数范围 | |
---|---|---|---|---|
highp | (-2^62, 2^62) | (2^-62, 2^62) | 相对:2^-16 | (-2^16, 2^16) |
mediump | (-2^14, 2^14) | (2^-14, 2^14) | 相对:2^-10 | (-2^10, 2^10) |
lowp | (-2, 2) | (2^-8, 2) | 绝对:2^-8 | (-2^8, 2^8) |
限定符的顺序
-
在一般变量中:invariant > storage > precision (storage:存储,precision:精度)
-
在函数参数中:storage > parameter > precision (parameter:参数)
invariant varying lowp float color; // invariant > storage > precision
void doubleSize(const in lowp float s){ // storage > parameter > precision
float s1 = s;
}
来源:
https://blog.hawkhai.com/blog/2020/12/12/shader-OpenGL-ES-syntax
-- END --
进技术交流群,扫码添加我的微信:Byte-Flow
获取视频教程和源码
推荐:
Android FFmpeg 实现带滤镜的微信小视频录制功能
全网最全的 Android 音视频和 OpenGL ES 干货,都在这了
一文掌握 YUV 图像的基本处理
抖音传送带特效是怎么实现的?
所有你想要的图片转场效果,都在这了
面试官:如何利用 Shader 实现 RGBA 到 NV21 图像格式转换?
我用 OpenGL ES 给小姐姐做了几个抖音滤镜
发表评论