Este artículo está participando en el "Proyecto Golden Stone. Comparte 60,000 premios en efectivo"
prefacio
Continuando con lo anterior, en el artículo Shader desde la entrada hasta el abandono - Introducción a la programación de sombreadores y al dibujo del sistema de coordenadas - Nuggets (juejin.cn) , presentamos brevemente qué es la programación de sombreadores y usamos shadertoy para la programación de sombreadores. En lo anterior, hemos completado el dibujo del sistema de coordenadas, utilizando principalmente estos puntos:
- Corrección de coordenadas uv
- Cómo dibujar la cuadrícula (multiplique las coordenadas uv por un número y tome la parte decimal como las nuevas coordenadas uv)
Hoy vamos a profundizar en el estudio. Vamos a aprender algunas funciones integradas de uso común en GLSL para sentar una base sólida para nuestro estudio posterior.
Funciones integradas comunes
Continuando con el código anterior, extraemos la función de dibujar la cuadrícula como una función separada:
vec3 grid(vec2 uv) {
vec3 color = vec3(1.0);
vec2 cell = 1.0 - 2.0 * abs(fract(uv) - 0.5);
// color = vec3(cell, 0.0);
if(abs(uv.x) <= 2. * fwidth(uv.x)) {
color = vec3(0.0, 1.0, 0.0);
} else if(abs(uv.y) <= 2. * fwidth(uv.y)) {
color = vec3(1.0, 0.0, 0.0);
} else if(abs(cell.x) <= 2. * fwidth(uv.x) || abs(cell.y) < 2. * fwidth(uv.y)) {
color = vec3(0.6);
}
return color;
}
复制代码
Hay una regla de oro en la programación de Shader:
Si puede usar si, no necesita si Por la razón específica, puede consultar este artículo: ¿Cómo funciona la GPU? - Pepitas (juejin.cn)
Entonces, lo que tenemos que hacer ahora es eliminar la función anterior if
.
Ahora introducimos la primera función incorporada:step(edge, x)
paso
step
genera una función de paso comparandox
conedge
. Para el elemento i del valor de retorno, se devuelve 0.0 six
[ i ] <edge
[ i ], y 1.0 de lo contrario.
Significa: si x es menor que cierto valor, devuelve 0, y si es mayor o igual a este valor, devuelve 1.
所以上面的if语句我们可以写成:
// if(abs(uv.x) <= 2. * fwidth(uv.x)) {
// color = vec3(0.0, 1.0, 0.0);
// }
// ===>
color = step(abs(uv.x), 2.0 * fwidth(uv.x)) * vec3(0.0, 1.0, 0.0);
复制代码
但是这样的话,好像我们之前画的坐标系的格子都看不见了。所以我们要想办法将他们融合起来。所以,现在我们介绍第二个函数:mix
mix
它的函数签名如下:
genType mix
(genType x
, genType y
, genType a
);
mix
performs a linear interpolation betweenx
andy
usinga
to weight between them. The return value is computed as follows: x⋅(1−a)+y⋅a.
该函数的实际上执行的就是一个简单的 线性差值。 简单的说就是:当a = 1时,返回的值为y, 但a = 0时,返回x的值,如果返回的值为0.5,则返回 0.5x + 0.5y
的值,同理,如果a = 0.2, 返回 0.8 * a + 0.2 * y
。
我们可以利用这个函数来做颜色叠加,颜色叠加的公式可以写为:
这刚好符合上面的 mix
函数的定义。所以可以写出下面的式子:
color = mix(color, vec3(0.0, 1.0, 0.0), step(abs(uv.x), 2.0 * fwidth(uv.x)));
复制代码
对于其他的坐标我们都可以同理写出:
color = mix(color, vec3(0.6), step(abs(_uv.x), 2.0 * fwidth(uv.x)));
color = mix(color, vec3(0.6), step(abs(_uv.y), 2.0 * fwidth(uv.x)));
color = mix(color, vec3(0.0, 1.0, 0.0), step(abs(uv.x), 2.0 * fwidth(uv.x)));
color = mix(color, vec3(1.0, 0.0, 0.0), step(abs(uv.y), 2.0 * fwidth(uv.y)));
复制代码
OK,现在我们就已经完成了if 分支的替换工作。但是使用 step
函数它有一个小小的缺陷,我们可以通过一张图看出来。
如上图所示,我们可以看出,当我们的坐标系发生了旋转的情况下,在线条的边缘会产生很多的锯齿。这是因为step
函数它不是返回0就是返回1,它发生了突变,没有一个平滑过渡的区间。
所以,现在我们介绍第三个函数:smoothstep
smoothstep
genType smoothstep
(genType edge0
, genType edge1
, genType x
)
smoothstep
performs smooth Hermite interpolation between 0 and 1 whenedge0
<x
<edge1
. This is useful in cases where a threshold function with a smooth transition is desired.smoothstep
is equivalent to:
genType t; /* Or genDType t; */
t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0);
return t * t * (3.0 - 2.0 * t);
复制代码
smoothstep
returns 0.0 ifx
≤edge0
and 1.0 ifx
≥edge1
.
其函数图像如下:
我们可以直观的看出:当x < 0时,返回0,当 x > 1时,返回1,如果 x 处于0~1之间时,则有一个类似于线性插值的过渡,但是请注意,这里并不是线性插值,因为在接近两端的值的时候,该函数有一个平滑的过渡。所以我们可以通过这一点来对 step
函数进行替换以解决锯齿的问题。
我们可以写出下面的代码:
color = mix(color, vec3(0.6), smoothstep(3.0 * fwidth(uv.x), 1.0 * fwidth(uv.x), abs(cell.x)));
color = mix(color, vec3(0.6), smoothstep(3.0 * fwidth(uv.x), 1.0 * fwidth(uv.x), abs(cell.y)));
复制代码
结果如下:
我们可以发现在格子的一侧的锯齿情况有明显的改善,但是另一侧还是锯齿比较严重。这是因为我们格子的坐标是 0~1之间,但是我们是对 abs(x)
进行差值,abs(x)
函数小于 3.0 * fwidth(uv.x)
的值始终都处于格子的一侧,所以我们需要对uv坐标进行一点修改。
修改如下:
vec2 cell = 1.0 - 2.0 * abs(fract(uv) - 0.5);
color = mix(color, vec3(0.6), smoothstep(3.0 * fwidth(uv.x), 1.0 * fwidth(uv.x), abs(cell.x)));
color = mix(color, vec3(0.6), smoothstep(3.0 * fwidth(uv.x), 1.0 * fwidth(uv.x), abs(cell.y)));
复制代码
结果如下:
通过对比红色和绿色XY轴与灰色的格子线,我们可以清晰的看出格子上的锯齿有了明显的改善,这就是smoothstep
的功劳!那么类似的,我们可以通过此方法来优化我们的XY轴。
最终优化效果如下:
总结
今天我们通过解决解决一系列的问题学习了以下三个内置函数:
- 为了解决掉
if
分支,我们使用了step
函数 - Para resolver el problema de la mezcla de colores, usamos la
mix
función , - Para resolver el problema de las líneas irregulares, usamos la
smoothstep
función.
Espero que los lectores utilicen mucho estas funciones integradas en el futuro para lograr la competencia. ¡Nos vemos en el próximo artículo!