前言
上一篇文章中, 主要分析了渲染管线与着色器, 为了更好的控制顶点和纹理着色器工作, 这里学习一下 着色语言(Shading Language) 相关的知识
一. 什么是着色语言?
OpenGL 2.0 ES 的 Shading Language 是一种高级的图形编程语言, 用于配合顶点着色器和纹理着色器绘制出更加炫酷的场景与特效
二. 特点
- 基于 C/C++ 的语法及流程控制
- 完美支持向量和矩阵的操作
- 通过限定符来管理输入和输出
- 拥有大量的内置函数来提供丰富的功能
三. 语法基础
与 C 语言类似, 着色语言中有许多内建的原生数据类型和构建数据类型, 其主要数据类型如下
一) 数据类型
1. 标量
标量也被称为 “无向量”, 其值只有大小, 不具方向性, 支持的标量如下
- bool
- int
- float
2. 向量
向量可以看做是用同样的类型的标量组成, 其基本类型也是 bool, int 和 float 三种, 每个向量可以由 2 个, 3 个或者 4 个相同的标量组成
向量类型 | 说明 |
---|---|
vec2 | 包含了 2 个浮点数的向量 |
vec3 | 包含了 3 个浮点数的向量 |
vec4 | 包含了 4 个浮点数的向量 |
ivec2 | 包含了 2 个整数的向量 |
ivec3 | 包含了 3 个整数的向量 |
ivec4 | 包含了 4 个整数的向量 |
bvec2 | 包含了 2 个布尔数的向量 |
bvec3 | 包含了 3 个布尔数的向量 |
bvec4 | 包含了 4 个布尔数的向量 |
向量在主色器代码的开发中有着十分重要的作用, 可以很方便的存储以及操作颜色, 位置, 纹理坐标等
将向量看做颜色
// 将向量看做颜色
vec4 aColor;
aColor.r = 0.6;
aColor.g = 0.6;
aColor.b = 0.6;
aColor.a = 0.6;
向量操作位置
// 将向量看做位置
vec4 aPosition;
aPosition.x = 67.2;
aPosition.y = 67.2;
aPosition.z = 67.2;
aPosition.w = 67.2;
向量操作纹理坐标
// 将向量看做纹理坐标
vec4 aTexCoor;
aTexCoor.s = 0.65;
aTexCoor.t = 0.65;
aTexCoor.p = 0.65;
aTexCoor.q = 0.65;
3. 矩阵
GL 中的坐标系有五种, 分别为 局部坐标->世界坐标->观察坐标->裁剪坐标->屏幕坐标, 每个坐标系之间的转换则需要通过 Matrix 矩阵进行
矩阵类型 | 说明 |
---|---|
mat2 | 2 x 2 的浮点数矩阵 |
mat3 | 3 x 3 的浮点数矩阵 |
mat4 | 4 x 4 的浮点数矩阵 |
4. 采样器
采样器是 Shading Language 中不同于 C 语言的一种特殊的基本数据类型, 专门用来处理纹理采样的相关操作, 一个采样器变量代表一幅或一套纹理贴图
采样器类型 | 说明 |
---|---|
sampler2D | 用于访问二维纹理 |
sampler3D | 用于访问三维纹理 |
samplerCube | 用于访问立体贴图纹理 |
5. 结构体
Shading Language 提供了类似于 C 语言中的用户自定义结构体, 同样使用 struct 关键字声明
struct info {
vec3 color; // 颜色成员
vec3 position; // 位置成员
vec2 textureCoor; // 纹理坐标成员
}
6. 数组
开发过程中可以声明任何类型的数组
vec3 position[20]; // 声明了一个包含 20 个 vec3 的数组, 索引从 0 开始
vec3 position[] // 声明了一个大小不定的数组
7. 空类型
空类型使用 void 表示
void main() {
// .......
}
二) 精度
除了正常使用的数据类型之外, 还可以用精度来修饰数据类型
precision <精度> <类型>;
- 精度可以选择 lowp, mediump 以及 highp
- 类型一般为 float/vecx
三) 限定符
限定符 | 说明 |
---|---|
attribute | 一般用于每个顶点都各不相同的量, 如顶点的位置, 颜色…. |
uniform | 一般用于 对同一组顶点组成的单个 3D 物体中所有顶点都相同的量, 如当前光源位置 |
varying | 用于从顶点着色器传递到片元着色器 |
cosnt | 用于声明常量 |
1. attribute 限定符
属性限定符, 其修饰的变量用来接收渲染管线传递进顶点着色器的当前待处理顶点的各种属性值
- 这些属性值, 每个顶点各自拥有独立的副本, 用于描述顶点的各项特征
- 如: 顶点坐标, 法向量, 颜色, 纹理坐标…
- attribute 修饰的变量值由 Java 程序批量传入渲染管线, 管线进行基本处理后再传递给顶点着色器
- 数据中有多少个顶点, 管线便会调用多少次顶点着色器, 每次将一个顶点的各种属性数据传递给顶点着色器中的 attribute 变量
定义
attribute vec4 aVertexPosition; // 顶点坐标
attribute vec4 aTexturePosition; // 纹理坐标
传值
// 获取着色器中指定名称属性变量的引用值
int aPositionHandle = GLES20.glGetAttribLocation(mProgram, "aPosition");
// 启动顶点位置数据
GLES20.glEnableVertexAttribArray(aPositionHandle);
GLES20.glVertexAttribPointer(
aPositionHandle, // 顶点位置属性引用
VERTEX_DIMENSION, // 每个顶点一组的数据个数
GLES20.GL_FLOAT, // 顶点数据类型
false, // 是否归一化
VERTEX_DIMENSION * 4, // 每组数据的尺寸 即一个顶点占用的字节数
mVertexBuffer // 存放顶点数据的缓冲区
);
2. uniform 限定符
uniform 为一致变量限定符, 是指对同一组顶点组成的单个 3D 物体中所有顶点都相同的量
定义
uniform mat4 uMatrix; // 总变换矩阵一致变量
传值
// 获取着色器程序中总变换矩阵一致变量
int uMatrixHandler = GLES20.glGetUniformLocation(mProgram, "uMatrix");
GLES20.glUniformMatrix4fv(
uMatrixHandler,
1,
false,
mFinalMatrix,
0
);
3. varying 限定符
varying 限定用于将顶点着色器中的信息传入到片元着色器
定义
varying vec4 ambient; // 环境光易变变量
varying vec4 diffuse; // 散射光易变变量
varying vec4 specular; // 镜面放射光易变变量
传值
// 顶点着色器
varying vec2 ft_Position;
void main() {
ft_Position = f_Position;
......
}
// 片元着色器
varying vec2 ft_Position; // 接收从顶点着色器传递过来的易变变量(经过光栅化处理)
uniform sampler2D sTexture;// 2D 纹理
void main() {
// 取相应坐标点的范围转成 texture2D
gl_FragColor=texture2D(sTexture, ft_Position);
}
四) 流程控制
glsl 的流程控制与 C/C++ 基本一致, 如下所示
if(){}、if(){}else{}、if(){}else if(){}else{}
while(){}和do{}while()
for(;;){}
break 和 continue
四. 内建变量
一) 顶点着色器
- gl_Position(vec4)
- 通常 gl_Position = 原始顶点数据 * 映射矩阵
- gl_PointSize
- 用于描述一个点的大小(单位为像素)
- 若没有明确赋值, 默认为 1
- 只有采用了点绘制方式之后才有意义
二) 片元着色器(像素着色器)
1. 内建输入变量
- gl_FragCoord(vec4)
- 包含当前片元相对于窗口位置的坐标值(x, y, z, /1w)
- x, y 为片元相对于窗口的二维坐标
- glFrontFacing(bool)
- 用于判断正在处理的片元是否属于光栅化阶段生成此片元的对应图元的正面
2. 内建输出变量
- gl_FragColor(vec4)
- 由片元着色器写入计算完成的片元颜色值
- 可以通过它实现滤镜效果
- gl_FragData(vec4[])
- 通过写入信息供管线后继使用
五. 内建函数
一) 常见函数
函数 | 描述 |
---|---|
radians(x) | 角度转弧度 |
degrees(x) | 弧度转角度 |
sin(x) | 正弦函数,传入值为弧度。相同的还有cos余弦函数、tan正切函数、asin反正弦、acos反余弦、atan反正切 |
pow(x,y) | xy |
exp(x) | ex |
exp2(x) | 2x |
log(x) | logex |
log2(x) | log2x |
sqrt(x) | x√ |
inversesqr(x) | 1x√ |
abs(x) | 取x的绝对值 |
sign(x) | x>0返回1.0,x<0返回-1.0,否则返回0.0 |
ceil(x) | 返回大于或者等于x的整数 |
floor(x) | 返回小于或者等于x的整数 |
fract(x) | 返回x-floor(x)的值 |
mod(x,y) | 取模(求余) |
min(x,y) | 获取xy中小的那个 |
max(x,y) | 获取xy中大的那个 |
mix(x,y,a) | 返回x∗(1−a)+y∗a |
step(x,a) | x< a返回0.0,否则返回1.0 |
smoothstep(x,y,a) | a < x返回0.0,a>y返回1.0,否则返回0.0-1.0之间平滑的Hermite插值。 |
dFdx(p) | p在x方向上的偏导数 |
dFdy(p) | p在y方向上的偏导数 |
fwidth(p) | p在x和y方向上的偏导数的绝对值之和 |
二) 几何函数
函数 | 描述 |
---|---|
length(x) | 计算向量x的长度 |
distance(x,y) | 返回向量xy之间的距离 |
dot(x,y) | 返回向量xy的点积 |
cross(x,y) | 返回向量xy的差积 |
normalize(x) | 返回与x向量方向相同,长度为1的向量 |
三) 矩阵函数
函数 | 描述 |
---|---|
matrixCompMult(x,y) | 将矩阵相乘 |
lessThan(x,y) | 返回向量xy的各个分量执行x< y的结果,类似的有greaterThan,equal,notEqual |
lessThanEqual(x,y) | 返回向量xy的各个分量执行x<= y的结果,类似的有类似的有greaterThanEqual |
any(bvec x) | x有一个元素为true,则为true |
all(bvec x) | x所有元素为true,则返回true,否则返回false |
not(bvec x) | x所有分量执行逻辑非运算 |
四) 纹理采样函数
纹理采样函数有
- texture2D
- texture2DProj
- texture2DLod
- texture2DProjLod
- textureCube
- textureCubeLod
- texture3D
- texture3DProj
- texture3DLod
- texture3DProjLod
总结
可以看到 glsl 的编码方式与 C/C++ 还是非常相似的, 其中内置的 api 非常丰富, 需要在实践中慢慢积累