前言
阅读 The Book of Shaders 的全过程,以及非常主观的一些总结。
Shader 着色器
并行运算,像一系列像素点同时着色。
GLSL
像 C 语言一样的着色语言。
Hello World
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
void main() {
gl_FragColor = vec4(0.0,0.4,0.6,1.0);
// gl_FragColor = vec4(0,1,1,0); 错误,不能自动转化类型
}
- 宏,定义了 GL_ES 才执行包裹着的
precision mediump float代表设定中精度。 - 精度越高质量越高运行开销越大。
gl_FragColor代表颜色,相当于全局变量。- 填浮点,因为并不能保证有自动类型转换。
Uniforms
统一只读的输入值。
比如有
#ifdef GL_ES
precision mediump float;
#endif
uniform vec2 u_resolution; // 画布尺寸(宽,高)
uniform vec2 u_mouse; // 鼠标位置(在屏幕上哪个像素)
uniform float u_time; // 时间(加载后的秒数)
这样就可以做一个随着时间变化的颜色了。
#ifdef GL_ES
precision mediump float;
#endif
uniform float u_time;
void main() {
gl_FragColor = vec4(abs(sin(u_time)),0.0,0.0,1.0);
}
gl_FragCoord
用来处理画布上每一个像素点的颜色。
比方说希望越靠左边颜色越红反之越黑。
再比方说希望越靠上面越蓝否则越白。
组合起来是这样的。
可以发现每次都要vec2 st = gl_FragCoord.xy / u_resolution,这是为什么?
因为要获取规范化值,说人话就是把具体数值转化为比例值。
u_resolution代表宽高,gl_FragCoord.xy代表像素点的位置。两者每一维各自相除之后就转化为了比例。
算法绘图
一次函数
什么叫多看一眼就会爆炸,乍一看没看懂。。
但是只是因为没见过
smoothstep是什么东西,所以才看不懂。在这里,+ 可以当作是两个着色器重叠(不一定对,但最好先理解)。可以大胆猜pct * vec3(0.0, 1.0, 0.0)是用来画绿线的,所以首先关注这部分。
简单来说,看代码。
float smoothstep(float t1, float t2, float x) {
x = clamp((x - t1) / (t2 - t1), 0.0, 1.0);
return x * x * (3 - 2x);
}
其中 clamp 代表提供两个最值一小一大,然后检测第三个数,输出的数为这个数字夹在里面的值(小于最小则最小,大于最大则最大,其余在之内)。
最终smoothstep获取的值为:x 越靠 t1 (<= t1)则越接近 0(最小为 0),越靠 t2 (>= t2)则越接近 1(最大为 1)。
具体讲解推荐 Shader实验室: smoothstep函数 - 知乎 (zhihu.com)。
所以上图中的 plot 函数获得的东西的理解如下:
- 为什么检测值要传入的是
abs(st.y - st.x),首先可以想到他在y = x的曲线上越接近,那这个值就越小,结果就是越来越接近 0,再看smoothstep(0.01, 0.0, t),可以看到它越接近t2,也就是smoothstep(...)获取的值越接近 1,因此pct * vec3(0.0, 1.0, 0.0)越来越能显现绿色。 - 然后我们再看看这个
abs(st.y - st.x)越来越大,也就是离y = x的曲线越来越远的情况。这时候x远远超过了0.01,但他最大还是只能为0.01,因此放他属于越来越接近或者已经等于t1,因此经过smoothstep处理就越来越接近0,因此别的地方就画不出绿线了(因为0 * vec3(...) = vec3(0))。 - 在这里我们还可以尝试调大调小这个
t1的值观察它的变化,相信能够得到更好的理解。
总感觉有点说复杂了。。不知大伙还能不能理解,总的来说绿线就是这么画出来的。
顺便说一下这个float y = st.x,就代表 y 随 st.x,然后因为color就是从左往右越来越白,至于为什么还要乘上这个(1 - pct),你可以去掉这个绿线试试看显示了什么东西,理解了自然明白了。(答案是让这个渐变背景不会影响到绿线上)
高次幂函数
当你好像理解了上面所说的之后,看到这个你又懵了。。
我们再尝试梳理一下。
看第一项,f(x) = smoothstep(pct - 0.02, pct, st.y),在 y 越来越大的时候,代表越来越接近 甚至超过t2,这个时候f(x)基本上是 1。在示例代码中plot(st, y)代表你的 st.y 越接近 y 则plot就越基本是1。因此假设只保留这一项,那么显现出来的图形是这样的:
这就是 y 超过了 st.y 之后都染上绿色了。
那么再看这个公式的第二部分smoothstep(pct, pct + 2, st.y)。
原理是一样的,只不过做了相差,就是将所有 st.y 大于 pct + 2 的部分全部的 1 抹掉,做减法嘛(第一项是 st.y 大于pct 的部分 全设为 1 了)。
这里面我为什么pct * vec3(1.0, -1.0, 0.0),我为什么要在 G 值上设为 -1 ,可以思考一下。
大概就是这么个过程,想讲明白发现很费劲。。
step 函数
尝试改变 step 中第一个参数。如注释所述,在step的第二个参数小于第一个参数(阈值)时为0.0,否则为1.0,与smoothstep的区别在于它是离散的,smoothstep是平滑连续的。