无特殊说明,文中的 GLSL 均指 OpenGL ES 2.0 的着色语言。
着色器语言 GLSL (opengl-shader-language) 入门大全
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}
在 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 中,关于数组有两点需要注意:
GLSL 中函数不能够递归调用,且必须声明返回值类型(无返回值时声明为 void)。
限定符 | 描述 |
---|---|
< none: default > | 局部可读写变量,或者函数的参数 |
const | 编译时常量,或只读的函数参数 |
attribute | 由应用程序传输给顶点着色器的逐顶点的数据 |
uniform | 在图元处理过程中其值保持不变,由应用程序传输给着色器 |
varying | 由顶点着色器传输给片段着色器中的插值数据 |
本地变量和方法参数只能使用 const 限定符,方法返回值和结构体成员不能使用限定符。
不包含任何限定符或者包含 const 限定符的全局变量可以包含初始化器,这种情况下这些变量会在 main() 函数开始之后第一行代码之前被初始化,这些初始化值必须是常量表达式。 没有任何限定符的全局变量如果没有在定义时初始化或者在程序中被初始化,则其值在进入 main() 函数之后是未定义的。 uniform、attribute 和 varying 限定符修饰的变量不能在初始化时被赋值,这些变量的值由 OpenGL ES 计算提供。
全局变量限制符只能为 const、attribute、uniform 和 varying 中的一个,不可复合。
数组或者包含数组的结构体不能被声明为常量(因为数组不能在定义时被初始化)。
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 变量只用于顶点着色器中,用来存储顶点着色器中每个顶点的输入(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;
}
OpenGL ES 2.0 的实现必须提供至少 128 个顶点 uniform 向量及 16 片段 uniform 向量。
uniform mat4 viewProjMatrix;
uniform mat4 viewMatrix;
uniform vec3 lightPosition;
OpenGL ES 2.0 实现中的 varying 变量最小支持数为 8。
varying 变量存在内插(interpolate)的过程。这些变量在片段着色器中需要有相对应的声明且数据类型一致,然后在光栅化过程中进行插值计算。
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
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 varying lowp float color; // invariant > storage > precision
void doubleSize(const in lowp float s){ // storage > parameter > precision
float s1 = s;
}
float myFloat;
vec4 myVec4;
mat4 myMat4;
myVec4 = myVec4 * myFloat; // Multiplies each component of myVec4 by a scalar myFloat
myVec4 = myVec4 * myVec4; // Multiplies each component of myVec4 together (e.g., myVec4 ^ 2)
myVec4 = myMat4 * myVec4; // Does a matrix * vector multiply of myMat4 * myVec4
myMat4 = myMat4 * myMat4; // Does a matrix * matrix multiply of myMat4 * myMat4
myMat4 = myMat4 * myFloat; // Multiplies each matrix component by the scalar myFloat
循环终止条件也必须是循环变量和常量的简单比较,在循环内部不能改变循环变量的值。
float myArr[4];
for(int i = 0; i < 3; i++) {
sum += myArr[i]; // NOT ALLOWED IN OPENGL ES, CANNOT DO INDEXING WITH NONCONSTANT EXPRESSION
}
...
uniform int loopIter;
// NOT ALLOWED IN OPENGL ES, loopIter ITERATION COUNT IS NONCONSTANT
for(int i = 0; i < loopIter; i++) {
sum += i;
}
GLSL 中的函数不能够递归调用。
限定符 | 描述 |
---|---|
in | 默认使用的缺省限定符,指明参数传递的是值,并且函数不会修改传入的值(C 语言中传递值) |
inout | 指明参数传入的是引用,如果在函数中对参数的值进行了修改,当函数结束后参数的值也会修改(C 语言中传递引用) |
out | 参数的值不会传入函数,但是在函数内部修改其值,函数结束后其值会被修改 |
vec4 myFunc(inout float myFloat, // inout parameter
out vec4 myVec4, // out parameter
mat4 myMat4); // in parameter (default)
#define
#undef
#if
#ifdef
#ifndef
#else
#elif
#endif
// 这一标记需要写在代码的最开始位置,对于 OpenGL ES 2.0 的着色器应将此值设置为 100。
#version 100 // OpenGL ES Shading Language v1.00
#ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif
#endif
#define NUM 100
#if NUM == 100
#endif
// Set behavior for an extension
#extension extension_name : behavior
// Set behavior for ALL extensions
#extension all : behavior
// 实现不支持 3D 纹理扩展
#extension GL_OES_texture_3D : enable
gl_Position 为齐次顶点位置坐标。
片段着色器中有一个只读变量 gl_FragCoord,存储了片段的窗口相对坐标 x、y、z 及 1/w。 该值是在顶点处理阶段之后对图元插值生成片段计算所得。z 分量是深度值用来表示片段的深度。
mediump vec4 gl_FragCoord;
bool gl_FrontFacing;
mediump vec4 gl_FragColor;
mediump vec4 gl_FragData[gl_MaxDrawBuffers];
mediump vec2 gl_PointCoord;
Implementation dependent constants. The example values below are the minimum values allowed for these maximums.
在 vertex Shader 中:
output 类型的内置变量:
变量 | 说明 | 单位 |
---|---|---|
highp vec4 gl_Position ; |
gl_Position 放置顶点坐标信息 | vec4 |
mediump float gl_PointSize ; |
gl_PointSize 需要绘制点的大小(只在 gl.POINTS 模式下有效) | float |
在 fragment Shader 中:
input 类型的内置变量:
变量 | 说明 | 单位 |
---|---|---|
mediump vec4 gl_FragCoord ; |
片元在 framebuffer 画面的相对位置 | vec4 |
bool gl_FrontFacing ; |
标志当前图元是不是正面图元的一部分 | bool |
mediump vec2 gl_PointCoord ; |
经过插值计算后的纹理坐标,点的范围是 0.0 到 1.0 | vec2 |
output 类型的内置变量:
变量 | 说明 | 单位 |
---|---|---|
mediump vec4 gl_FragColor ; |
设置当前片点的颜色 | vec4 RGBA color |
mediump vec4 gl_FragData[n] ; |
设置当前片点的颜色,使用 glDrawBuffers 数据数组 | vec4 RGBA color |
片段着色器中用来计算镜面光的代码:
float nDotL = dot(normal, light);
float rDotV = dot(viewDir, (2.0 * normal) * nDotL – light);
float specular = specularColor * pow(rDotV, specularPower);
对于纹理函数,返回类型的精度与采样器的类型相匹配。
uniform lowp sampler2D sampler;
highp vec2 coord;
...
lowp vec4 col = texture2D(sampler, coord); // texture2D returns lowp
对于纹理函数,返回类型的精度与采样器的类型相匹配。
uniform lowp sampler2D sampler;
highp vec2 coord;
...
lowp vec4 col = texture2D(sampler, coord); // texture2D returns lowp
Syntax & Description
方法 | 说明 |
---|---|
T pow(T x, T y) | 返回 x 的 y 次幂 xy |
T exp(T x) | 返回 x 的自然指数幂 ex |
T log(T x) | 返回 x 的自然对数 ln |
T exp2(T x) | 返回 2 的 x 次幂 2x |
T log2(T x) | 返回 2 为底的对数 log2 |
T sqrt(T x) | 开根号 √x |
T inversesqrt(T x) | 先开根号,在取倒数,就是 1/√x |
Syntax & Description
smoothstep 函数与 linstep 函数的工作方式类似,不过其将在最小值和最大值之间的中间值附近更快地增加值。该函数使用最小值和最大值之间的 hermite 插值。
float smoothstep(float start, float end, float parameter);
Syntax & Description
Syntax & Description
Syntax & Description
图像纹理有两种:一种是平面 2d 纹理,另一种是盒纹理。针对不同的纹理类型有不同访问方法。
以下函数只在顶点着色器中可用:
vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod);
vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod);
vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod);
vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod);
以下函数只在片段着色器中可用:
vec4 texture2D(sampler2D sampler, vec2 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias);
vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias);
vec4 textureCube(samplerCube sampler, vec3 coord, float bias);
在定点着色器和片段着色器中都可用:
vec4 texture2D(sampler2D sampler, vec2 coord);
vec4 texture2DProj(sampler2D sampler, vec3 coord);
vec4 texture2DProj(sampler2D sampler, vec4 coord);
vec4 textureCube(samplerCube sampler, vec3 coord);
Syntax & Description
A shader pair that applies diffuse and ambient lighting to a textured object.
这个例子是 OpenGL® ES 官方唯一的例子,能看懂了,估计就差不多了。
Vertex Shader:
// model-view-projection matrix
uniform mat4 mvp_matrix; // 透视矩阵 * 视图矩阵 * 模型变换矩阵
// normal matrix
uniform mat3 normal_matrix; // 法线变换矩阵(用于物体变换后法线跟着变换)
// light direction in eye coords
uniform vec3 ec_light_dir; // 光照方向
// vertex position
attribute vec4 a_vertex; // 顶点坐标
// vertex normal
attribute vec3 a_normal; // 顶点法线
// texture coordinates
attribute vec2 a_texcoord; // 纹理坐标
varying float v_diffuse; // 法线与入射光的夹角
varying vec2 v_texcoord; // 2d 纹理坐标
void main(void) {
// 归一化法线
// put vertex normal into eye coords
vec3 ec_normal = normalize(normal_matrix * a_normal);
// v_diffuse 是法线与光照的夹角。根据向量点乘法则,当两向量长度为 1 是乘积,即 cosθ 值。
// emit diffuse scale factor, texcoord, and position
v_diffuse = max(dot(ec_light_dir, ec_normal), 0.0);
v_texcoord = a_texcoord;
gl_Position = mvp_matrix * a_vertex;
}
Fragment Shader:
precision mediump float;
uniform sampler2D t_reflectance;
uniform vec4 i_ambient;
varying float v_diffuse;
varying vec2 v_texcoord;
void main (void) {
vec4 color = texture2D(t_reflectance, v_texcoord);
// 这里分解开来是 color*vec3(1,1,1)*v_diffuse + color*i_ambient
// 色 * 光 * 夹角 cos + 色 * 环境光
gl_FragColor = color*(vec4(v_diffuse) + i_ambient);
}