OpenGL ES着色器语言(GLSL ES)规范 ——下篇

Source


前言

本篇接上文继续对着色器语言规范进行讲解,本文的内容包括:分支和循环、着色器内置变量、函数、存储限定字、精度限定字、预处理指令等,接OpenGL ES着色器语言(GLSL ES)规范 ——上篇


分支和循环

GLSL ES也支持分支循环相关的方法,用法与javascript几乎一致,为数不多的区别是GLSL ES中没有switch关键字,多了discard关键字

if、if-else

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);
			}		
		}
	`

for

	 `
		void main() {
			for (int i = 0; i < 3; i++) {
				sum += i
			}
		}
	`

continue、break、discard

  • continue表示终止当前循环并跳到下一个循环
  • break表示终止当前循环并跳出整个循环
  • discard只能出现在片元着色器中,表示终止处理当前片元跳到下一个片元

着色器内置变量

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内置函数

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<edge返回0.0,否则返回1.0
smoothstep(edge1,edge2,a) float / vec 艾米内内插步进,a<edge1返回0.0,a>edge2返回1.0,edge2>a>edge1返回艾米内插值结果
  • 几何函数
名称 类型 含义
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时,需要同时对变量进行声明和初始化

const float PI 3.14; 
const vec4 red  (1.0, 0.0, 0.0, 1.0); 

attribute

attribute变量只能在顶点着色器中使用,它是一个全局变量,被用来表示接收顶点信息

attribute vec3 position; // 存储坐标信息
attribute vec2 uv; // 贴图坐标
attribute vec4 a_Color // 颜色

uniform

uniform变量既可以在顶点着色器中使用,也可以在片元着色器中使用,它也是一个全局变量,可以是除了数组与结构体的任何类型,在顶点着色器和片元着色器定义了同名uniform变量时会被二者共享,即会被所有顶点和片元共用,它可以被用来存储变换矩阵,时间纹理这种影响所有顶点的信息。

uniform mat4 modelMatrix; // 模型转换矩阵
uniform mat4 viewMatrix; // 视图矩阵
uniform mat4 projectionMatrix; // 投影矩阵
uniform float uTime; // 时间

varying

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 条件表达式
If 条件表达式为真,执行这里
#endif

  • #ifdef

#ifdef 某宏
如果定义了某宏,执行这里
#endif

  • #ifndef

#ifdef 某宏
如果没有定义某宏,执行这里
#endif

宏的定义使用关键字 #define,解除宏定义使用 #undef
举例:

	#define PI 3.1415926
	#ifdef PI
		precision highp float
	#endif

总结

分支和循环

  • if、if-else
  • for
  • continue、break、discard

着色器内置变量

函数

  • 函数定义
  • 规范声明
  • webgl内置函数

存储限定字

  • const
  • attribute
  • uniform
  • varying

精度限定字

预处理指令