本文主要解决两个问题:
- GLSL基本语法介绍
在前面第四讲中我们简单提及了GLSL语言,但是没有太深入,对于我们学习及后面编写的openGL GLSL来说,有些吃力,所以这里单独写一篇来介绍GLSL语言.
1. GLSL 编程概念
顶点着色器(.vs文件)与片元着色器(.fs文件)
一个着色器程序分为两大部分,即 “顶点着色器” 与 “片元着色器” 简单来说,前者多用于模型构建,后者用于在光栅化时表现出更多细节,一个着色器程序必须同时包含这两部分,程序会先通过 “顶点着色器” 处理再交与 “片元着色器” 渲染细节。
举个例子:比如你想绘制一个蓝色四面体,那就需要用到 8 个顶点 和 “蓝” 这两个参数。 其中 8 个顶点数据先传入 “顶点着色器” 这时一个四面体的模型便建立了,而 “蓝色” 这一参数属于纹理细节,将在 “片元着色器” 中被处理。
2. GLSL语法介绍
2.1 内置变量
GLSL 是一种面向过程的编程语言,有着与 C 语言类似的语法,但没有 C 语言复杂的指针概念。 常用基本的类型如下:
类型 | 说明 |
---|---|
void | 空类型,即不返回任何值 |
bool | 布尔类型 true,false |
int | 带符号的整数 signed integer |
float | 带符号的浮点数 floating scalar |
vec2, vec3, vec4 | n维浮点数向量 n-component floating point vector |
bvec2, bvec3, bvec4 | n维布尔向量 Boolean vector |
ivec2, ivec3, ivec4 | n维整数向量 signed integer vector |
mat2, mat3, mat4 | 2x2, 3x3, 4x4 浮点数矩阵 float matrix |
sampler2D | 2D纹理 a 2D texture |
samplerCube | 盒纹理 cube mapped texture |
2.2变量限定符:
下面来讲讲变量限定符,包含attribute, varying, uniform等, 其实这些都是 GLSL 的变量限定符,一般用来声明与其宿主程序沟通的接口,什么意思呢。 假设在 WebGL 环境中, GLSL 的宿主程序就是 javascript, 所有数据均由 javascript 通过事先定义好的变量限定符传入 GLSL。
除了 attribute 和 varying 还有其他修饰符,具体见下表:
修饰符 | 说明 |
---|---|
none | (默认的可省略)本地变量,可读可写,函数的输入参数既是这种类型 |
const | 声明变量或函数的参数为只读类型 |
attribute | 只能存在于vertex shader中,一般用于保存顶点或法线数据,它可以在数据缓冲区中读取数据 |
uniform | 在运行时shader无法改变uniform变量, 一般用来放置程序传递给shader的变换矩阵,材质,光照参数等等. |
varying | 主要负责在 vertex 和 fragment 之间传递变量 |
值得注意的是如果在 顶点着色器 与 片元着色器 中存在同名的 varying 变量,则其值可以由顶点着色器传递与片元着色器,如上例中都存在名为 vColor 的 varying 变量,执行 vColor=aColor 后在片元着色器中便可以取到 vColor 的值。
宿主程序(javascript)运行时, 会将四面体的顶点与颜色数据分别传给 aPosition 、 aColor。 一般来讲,给 gl_FragColor 赋值是整个着色器的最后一步工作,意为该点的最终颜色已确定。整体流程如下图所示:
2.3 函数参数限定符
GLSL 允许自定义函数,但参数默认是以值形式(in限定符)传入的,也就是说任何变量在传入时都会被拷贝一份,若想以引用方式传参,需要增加函数参数限定符。
限定符 | 说明 |
---|---|
< none: default > | 默认使用 in 限定符 |
in | 复制到函数中在函数中可读写 |
out | 返回时从函数中复制出来 (可写不可读) |
inout | 复制到函数中并在返回时复制出来 |
其中使用 inout 方式传递的参数便与其他 OOP 语言中的引用传递类似,参数可读写,函数内对参数的修改会影响到传入参数本身。
vec4 getPosition(out vec4 p)
{
p = vec4(0.,0.,0.,1.);
return v4;
}
void doubleSize(inout float size)
{
size= size*2.0 ;
}
2.4 流控制
在语法上,GLSL 与 C 非常相似, 但多了一种特殊的控制语句 discard,它会立即跳出片元着色器,并不在向下任何语句。
for (l = 0; l < numLights; l++)
{
if (!lightExists[l]);
continue;
color += light[l];
}
...
while (i < num)
{
sum += color[i];
i++;
}
...
do
{
color += light[lightNum];
lightNum--;
}while (lightNum > 0)
...
if (true)
discard;
2.4 内置函数库
类型 | 方法 | 说明 |
---|---|---|
通用函数 | T abs(T x) | 返回x的绝对值 |
通用函数 | T sign(T x) | 比较x与0的值,大于,等于,小于 分别返回 1.0 ,0.0,-1.0 |
通用函数 | T floor(T x) | 返回<=x的最大整数 |
通用函数 | T ceil(T x) | 返回>=等于x的最小整数 |
通用函数 | T fract(T x) | 获取x的小数部分 |
通用函数 | T mod(T x, T y) | 取x,y的余数 |
通用函数 | T mod(T x, float y) | 取x,y的余数 |
通用函数 | T min(T x, T y) | 取x,y的最小值 |
通用函数 | T min(T x, float y) | 取x,y的最小值 |
通用函数 | T max(T x, T y) | 取x,y的最大值 |
通用函数 | T max(T x, float y) | 取x,y的最大值 |
通用函数 | T clamp(T x, T minVal, T maxVal) | |
通用函数 | T clamp(T x, float minVal,float maxVal) min(max(x, minVal), maxVal), | 返回值被限定在 minVal,maxVal之间 |
通用函数 | T mix(T x, T y, T a) | 取x,y的线性混合,x*(1-a)+y*a |
通用函数 | T mix(T x, T y, float a) | 取x,y的线性混合,x*(1-a)+y*a |
通用函数 | T step(T edge, T x) | 如果 x<edge 返回 0.0 否则返回1.0 |
通用函数 | T step(float edge, T x) | 如果 x<edge 返回 0.0 否则返回1.0 |
通用函数 | T smoothstep(T edge0, T edge1, T x) | 如果xedge1返回1.0, 否则返回Hermite插值 |
通用函数 | T smoothstep(float edge0,float edge1, T x) | 如果xedge1返回1.0, 否则返回Hermite插值 |
角度&三角函数 | T radians(T degrees) | 角度转弧度 |
角度&三角函数 | T degrees(T radians) | 弧度转角度 |
角度&三角函数 | T sin(T angle) | 正弦函数,角度是弧度 |
角度&三角函数 | T cos(T angle) | 余弦函数,角度是弧度 |
角度&三角函数 | T tan(T angle) | 正切函数,角度是弧度 |
角度&三角函数 | T asin(T x) | 反正弦函数,返回值是弧度 |
角度&三角函数 | T acos(T x) | 反余弦函数,返回值是弧度 |
角度&三角函数 | T atan(T y, T x) | 反正切函数,返回值是弧度 |
角度&三角函数 | T atan(T y_over_x) | 反正切函数,返回值是弧度 |
指数函数 | 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 |
几何函数 | float length(T x) | 返回矢量x的长度 |
几何函数 | float distance(T p0, T p1) | 返回p0 p1两点的距离 |
几何函数 | float dot(T x, T y) | 返回x y的点积 |
几何函数 | vec3 cross(vec3 x, vec3 y) | 返回x y的叉积 |
几何函数 | T normalize(T x) | 对x进行归一化,保持向量方向不变但长度变为1 |
几何函数 | T faceforward(T N, T I, T Nref) | 根据 矢量 N 与Nref 调整法向量 |
几何函数 | T reflect(T I, T N) | 返回 I - 2 * dot(N,I) * N, 结果是入射矢量 I 关于法向量N的 镜面反射矢量 |
几何函数 | T refract(T I, T N, float eta) | 返回入射矢量I关于法向量N的折射矢量,折射率为eta |
矩阵函数 | mat matrixCompMult(mat x, mat y) | 将矩阵 x 和 y的元素逐分量相乘 |
向量函数 | bvec lessThan(T x, T y) | 逐分量比较x < y,将结果写入bvec对应位置 |
向量函数 | bvec lessThanEqual(T x, T y) | 逐分量比较 x <= y,将结果写入bvec对应位置 |
向量函数 | bvec greaterThan(T x, T y) | 逐分量比较 x > y,将结果写入bvec对应位置 |
向量函数 | bvec greaterThanEqual(T x, T y) | 逐分量比较 x >= y,将结果写入bvec对应位置 |
向量函数 | bvec equal(T x, T y) | 逐分量比较 x == y,将结果写入bvec对应位置 |
向量函数 | bvec equal(bvec x, bvec y) | 逐分量比较 x == y,将结果写入bvec对应位置 |
向量函数 | bvec notEqual(T x, T y) | 逐分量比较 x!= y,将结果写入bvec对应位置 |
向量函数 | bvec notEqual(bvec x, bvec y) | 逐分量比较 x!= y,将结果写入bvec对应位置 |
向量函数 | bool any(bvec x) | 如果x的任意一个分量是true,则结果为true |
向量函数 | bool all(bvec x) | 如果x的所有分量是true,则结果为true |
向量函数 | bvec not(bvec x) | bool矢量的逐分量取反 |
纹理查询函数 | vec4 texture2DLod(sampler2D sampler, vec2 coord, float lod) | 从sampler中提取指定坐标的颜色信息,只在vertex shader中可使用 |
纹理查询函数 | vec4 texture2DProjLod(sampler2D sampler, vec3 coord, float lod) | 从sampler中提取指定坐标的颜色信息,只在vertex shader中可使用 |
纹理查询函数 | vec4 texture2DProjLod(sampler2D sampler, vec4 coord, float lod) | 从sampler中提取指定坐标的颜色信息,只在vertex shader中可使用 |
纹理查询函数 | vec4 textureCubeLod(samplerCube sampler, vec3 coord, float lod) | 从sampler中提取指定坐标的颜色信息,只在vertex shader中可使用 |
纹理查询函数 | vec4 texture2D(sampler2D sampler, vec2 coord, float bias) | 第一个参数代表图片纹理,第二个参数代表纹理坐标点,通过GLSL的内建函数texture2D来获取对应位置纹理的颜色RGBA值,从sampler中提取指定坐标的颜色信息,只在fragment shader中可用 |
纹理查询函数 | vec4 texture2DProj(sampler2D sampler, vec3 coord, float bias) | 从sampler中提取指定坐标的颜色信息,只在fragment shader中可用 |
纹理查询函数 | vec4 texture2DProj(sampler2D sampler, vec4 coord, float bias) | 从sampler中提取指定坐标的颜色信息,只在fragment shader中可用 |
纹理查询函数 | vec4 textureCube(samplerCube sampler, vec3 coord, float bias) | 从sampler中提取指定坐标的颜色信息,只在fragment shader中可用 |
纹理查询函数 | vec4 texture2D(sampler2D sampler, vec2 coord) | 第一个参数代表图片纹理,第二个参数代表纹理坐标点,通过GLSL的内建函数texture2D来获取对应位置纹理的颜色RGBA值,在vertex shader与fragment shader中都可用 |
纹理查询函数 | vec4 texture2DProj(sampler2D sampler, vec3 coord) | 从sampler中提取指定坐标的颜色信息,在vertex shader与fragment shader中都可用 |
纹理查询函数 | vec4 texture2DProj(sampler2D sampler, vec4 coord) | 从sampler中提取指定坐标的颜色信息,在vertex shader与fragment shader中都可用 |
纹理查询函数 | vec4 textureCube(samplerCube sampler, vec3 coord) | 从sampler中提取指定坐标的颜色信息,在vertex shader与fragment shader中都可用 |
3. GLSL 示例程序
我们来看一个超简单的顶点着色器实例:
attribute vec3 aPosition;
attribute vec3 aColor;
varying vec4 vColor;
void main(void)
{
vColor = aColor;
gl_Position = aPosition;
}
一个简单的片元着色器实例
varying vec4 vColor;
void main(void)
{
gl_FragColor = vColor;
}
上例着色器中,gl_Position 、 gl_FragColor 等这些以 gl_ 开头的变量都是内置变量,通过给这些特殊的变量赋值,可以完成与硬件的通讯。其中 gl_Position 用于放置顶点坐标信息,gl_FragColor 用于设置当前片段的颜色。
参考:
博客文章: http://www.twinklingstar.cn/category/opengl/
博客文章: https://www.wangshaoxing.com/blog/2018-11-06-shader-lession-1.html
shader 赏析库: https://www.shadertoy.com/
着色器语言 GLSL 入门大全: https://github.com/wshxbqq/GLSL-Card