本篇接上文继续对着色器语言规范进行讲解,本文的内容包括:分支和循环、着色器内置变量、函数、存储限定字、精度限定字、预处理指令等,接OpenGL ES着色器语言(GLSL ES)规范 ——上篇。
GLSL ES也支持分支循环相关的方法,用法与javascript几乎一致,为数不多的区别是GLSL ES中没有switch关键字,多了discard关键字
if、if-else过多时会影响着色器执行速度,应合理使用。
// 顶点着色器
const vertex = `
attribute vec4 aPosition;
varying vec4 vCoord
void main() {
vCoord = aPosition;
gl_Position = aPosition;
gl_PointSize = 10.0;
}
`
// 片元着色器
const fragment = `
precision highp float;
varying vec4 vCoord
void main(){
if (vCoord.x > 0.5) {
gl_FragColor = vec4(1.0,1.0,1.0,1.0);
} else {
gl_FragColor = vec4(0.0,0.0,0.0,1.0);
}
}
`
`
void main() {
for (int i = 0; i < 3; i++) {
sum += i
}
}
`
webgl中内置了一些变量可以直接在着色器中使用:
webgl中的变量 | 类型 | 含义 |
---|---|---|
gl_PointSize | float | 绘制点时,正方形的边长 |
gl_Position | vec4 | 顶点裁剪坐标值 |
gl_FragColor | vec4 | 片元颜色值 |
gl_FragCoord | vec2 | 片元在屏幕上的像素坐标 |
gl_PointCoord | vec2 | 点渲染模式对应点像素坐标 |
函数的定义与C语言的样式很类似,注意除非定义函数时声明了void,否则函数必须有返回值,而且GLSL ES中的函数不允许递归调用(闭包)。
// 旋转函数
vec2 rotate(vec2 uv, float rotation, vec2 mid) {
return vec2(cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x, cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y);
}
// 使用该函数
vec2 rotateUv = rotate(vUv,3.14*0.25,vec2(0.5,0.5));
float rbga = 1.0-rotateUv.y;
gl_FragColor =vec4(rbga,rbga,rbga,1);
如果函数在其定义前就先被使用也是可以的,这时候要提前对函数进行规范声明
//旋转函数声明
vec2 rotate(vec2, float, vec2)
// 旋转函数使用
vec2 rotateUv = rotate(vUv,3.14*0.25,vec2(0.5,0.5));
float rbga = 1.0-rotateUv.y;
gl_FragColor =vec4(rbga,rbga,rbga,1);
// 旋转函数定义
vec2 rotate(vec2 uv, float rotation, vec2 mid) {
return vec2(cos(rotation) * (uv.x - mid.x) + sin(rotation) * (uv.y - mid.y) + mid.x, cos(rotation) * (uv.y - mid.y) - sin(rotation) * (uv.x - mid.x) + mid.y);
}
webgl内部将一些常用的函数开放了出来,函数的返回类型取决于参数类型。
名称 | 类型 | 含义 |
---|---|---|
radians() | float / vec | 角度转弧度 |
degrees() | float / vec | 弧度转角度 |
名称 | 类型 | 含义 |
---|---|---|
sin() | float / vec | 弧度的正弦值 |
cos() | float / vec | 弧度的余弦值 |
tan() | float / vec | 弧度的正切值 |
asin() | float / vec | 反正弦值 |
acos() | float / vec | 反余弦值 |
atan() | float / vec | 反正切值 |
名称 | 类型 | 含义 |
---|---|---|
pow(a,n) | float / vec | a的n次方 |
exp(n) | float / vec | 自然底数e的n次方 |
log(n) | float / vec | 以e为底n的对数 |
exp2(n) | float / vec | 2的n次方 |
log2(n) | float / vec | 以2为底的n的对数 |
sqrt(n) | float / vec | 对n开平方 |
inversesqrt(n) | float / vec | 对n开平方并取倒数 |
名称 | 类型 | 含义 |
---|---|---|
abs(x) | float / vec | x的绝对值 |
sign(x) | float / vec | x的绝对值,正数返回1.0,负数返回-1.0,否则返回0.0 |
floor(x) | float / vec | 向下取整 |
ceil(x) | float / vec | 向上取整 |
fract(x) | float / vec | 获取小数部分 |
mod(x,y) | float / vec | 返回x/y的模 |
max(x,y) | float / vec | 返回x和y中的较大值 |
clamp(x,minVal,maxVal) | float / vec | 将一个值限制在一个上限和下限之间,min(max(x, minVal), maxVal) |
mix(x,y,r) | float / vec | 线性内插,mix(x,y,r) = (1-r)*x +yr |
step(edge,a) | float / vec | 阶梯函数,a |
smoothstep(edge1,edge2,a) | float / vec | 艾米内内插步进,a |
名称 | 类型 | 含义 |
---|---|---|
length(v) | float | 矢量v的长度 |
distance(p0,p1) | float | 返回p0和p1之间的距离 |
dot(x,y) | float | 返回x和y之间的内积/点乘 |
cross(x,y) | vec | 返回x和y之间的外积/叉乘 |
normalize(x) | float/vec | 向量归一化为单位向量 |
faceforward(N,I,Nref) | float/vec | 法向量反向 |
reflect(I,N) | float/vec | 根据入射向量I和表面法向量N(单位向量)计算反射向量 |
refract(I,N,eta) | float/vec | 根据入射向量I和表面法向量N(单位向量),折射率eta计算折射向量 |
名称 | 类型 | 含义 |
---|---|---|
matrixCompMult(mat1, mat2) | mat2/mat3/mat4 | 矩阵对应位置上的元素相乘,返回新的矩阵 |
名称 | 类型 | 含义 |
---|---|---|
lessThan(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 < vec2 是否成立 |
lessThanEqual(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 <= vec2 是否成立 |
greaterThan(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 > vec2 是否成立 |
greaterThanEqual(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 >= vec2 是否成立 |
equal(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 = vec2 是否成立 |
notEqual(vec1, vec2) | bvec2/bvec3/bvec4 | 逐分量比较vec1 != vec2 是否成立 |
any(vec) | bool | vec任意分量为true,返回true |
all(vec) | bool | vec全部分量为true,返回true |
not(vec) | bool | 逐分量取补 |
sampler2D: 使用纹理坐标coord,从当前绑定到sampIer的二维纹理中读取相应的纹素。对于投影版本(带有Poj的),纹理坐标将从cood的最后一个分量中解析出来,而vec4类型的 coord的第3个分量将被忽略。参数bias只可在片元着色器中使用,它表示在sampIer是 MIPMAP纹理时,加在当前 lod上的值。如下:
名称 | 类型 |
---|---|
texture2D(sampler2D sampler,vec2 coord) | vec4 |
texture2D(sampler2D sampler,vec2 coord,float bias) | vec4 |
texture2DProj(sampler2D sampler, vec3 coord) | vec4 |
texture2DProj(sampler2D sampler,vec3 coord, float bias) | vec4 |
texture2DProj(sampler2D sampler,vec4 coord) | vec4 |
texture2DProj(sampler2D sampler,vec4 coord, float bias) | vec4 |
texture2DLod(sampler2D sampler,vec2 coord, float lod) | vec4 |
vec4 texture2DProj(sampler2D sampler,vec4 coord, float bias) | vec4 |
texture2DProjLod(sampler2D sampler,vec3 coord, float lod) | vec4 |
texture2DProjLod(sampler2D sampler,vec4 coord, float lod) | vec4 |
samplerCube: 使用纹理坐标coord,从绑定到sampler的立方体纹理中读取响应纹素。coord的方向可用来指定立方体纹理的表面。如下:
名称 | 类型 |
---|---|
textureCube(samplerCube sampler, vec3 coord) | vec4 |
textureCube(samplerCube sampler, vec3 coord, float bias) | vec4 |
textureCubeLod(samplerCube sampler,vec3 coord, float lod) | vec4 |
在GLSL ES中,会经常使用attribute、uniform、varying来传递变量
首先来讲一下const变量,const代表一个不能改变的值,使用const时,需要同时对变量进行声明和初始化
const float PI 3.14;
const vec4 red (1.0, 0.0, 0.0, 1.0);
attribute变量只能在顶点着色器中使用,它是一个全局变量,被用来表示接收顶点信息。
attribute vec3 position; // 存储坐标信息
attribute vec2 uv; // 贴图坐标
attribute vec4 a_Color // 颜色
uniform变量既可以在顶点着色器中使用,也可以在片元着色器中使用,它也是一个全局变量,可以是除了数组与结构体的任何类型,在顶点着色器和片元着色器定义了同名uniform变量时会被二者共享,即会被所有顶点和片元共用,它可以被用来存储变换矩阵,时间纹理这种影响所有顶点的信息。
uniform mat4 modelMatrix; // 模型转换矩阵
uniform mat4 viewMatrix; // 视图矩阵
uniform mat4 projectionMatrix; // 投影矩阵
uniform float uTime; // 时间
varying它也是一个全局变量,与uniform和attribute不同的是:他不是将javascript中的信息传给着色器,使用时必须同时在顶点着色器和片元着色器中定义同名同类型的varying变量,它的作用是把顶点着色器的数据传递给片元着色器,举例:
在顶点着色器,使用varying定义一个变量v_uv,它是二维向量类型,用它获取每个点的uv坐标
varying vec2 v_uv;
void main() {
v_uv = uv;
···
}
在片元着色器中接收来自顶点着色器中的uv信息,并把它作为rbga中的前俩个值
varying vec2 v_uv;
void main() {
gl_FragColor = vec4(v_uv, 0.0, 1.0);
}
在GLSL中需要指定精度以提高运行效率,减少内存损耗,顶点着色器默认指定高精度,片元着色器需手动指定。可以在开头指定以下三个之一。
precision lowp float;
precision mediump float;
precision highp float;
GLSL ES支持预处理指令。预处理指令用来在真正编译之前对代码进行预处理,都以井号(#)开始。GLSL ES提供了三种预处理指令。
#if 条件表达式
If 条件表达式为真,执行这里
#endif
#ifdef 某宏
如果定义了某宏,执行这里
#endif
#ifdef 某宏
如果没有定义某宏,执行这里
#endif
宏的定义使用关键字 #define,解除宏定义使用 #undef
举例:
#define PI 3.1415926
#ifdef PI
precision highp float
#endif
分支和循环
着色器内置变量
函数
存储限定字
精度限定字
预处理指令