Shader desde la entrada hasta el abandono (2): funciones integradas comunes de GLSL

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:

  1. Corrección de coordenadas uv
  2. 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 comparando  x con  edge. Para el elemento  i  del valor de retorno, se devuelve 0.0 si  x[ 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);
复制代码

sombra.png

但是这样的话,好像我们之前画的坐标系的格子都看不见了。所以我们要想办法将他们融合起来。所以,现在我们介绍第二个函数:mix

mix

它的函数签名如下:

genType mix(genType x, genType y, genType a);

mix performs a linear interpolation between x and y using a 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

我们可以利用这个函数来做颜色叠加,颜色叠加的公式可以写为:

C O yo O r = S r C . C O yo O r ( 1 D s t . a ) + D s t . C O yo O r D s t . a Color = Src.color * (1 - Dst.a) + Dst.color * Dst.a

这刚好符合上面的 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函数它有一个小小的缺陷,我们可以通过一张图看出来。

sombra.png

如上图所示,我们可以看出,当我们的坐标系发生了旋转的情况下,在线条的边缘会产生很多的锯齿。这是因为step 函数它不是返回0就是返回1,它发生了突变,没有一个平滑过渡的区间。

imagen.png

所以,现在我们介绍第三个函数:smoothstep

smoothstep

genType smoothstep(genType edge0, genType edge1, genType x)

smoothstep performs smooth Hermite interpolation between 0 and 1 when edge0 < 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 if x ≤ edge0 and 1.0 if x ≥ edge1.

其函数图像如下:

imagen.png

我们可以直观的看出:当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)));
复制代码

结果如下:

sombra.png

我们可以发现在格子的一侧的锯齿情况有明显的改善,但是另一侧还是锯齿比较严重。这是因为我们格子的坐标是 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)));
复制代码

结果如下:

sombra.png

通过对比红色和绿色XY轴与灰色的格子线,我们可以清晰的看出格子上的锯齿有了明显的改善,这就是smoothstep的功劳!那么类似的,我们可以通过此方法来优化我们的XY轴。

最终优化效果如下:

sombra.png

总结

今天我们通过解决解决一系列的问题学习了以下三个内置函数:

  1. 为了解决掉if分支,我们使用了step 函数
  2. Para resolver el problema de la mezcla de colores, usamos la mixfunción ,
  3. Para resolver el problema de las líneas irregulares, usamos la smoothstepfunció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!

Supongo que te gusta

Origin juejin.im/post/7166939417599279112
Recomendado
Clasificación