es.davy.ai

Preguntas y respuestas de programación confiables

¿Tienes una pregunta?

Si tienes alguna pregunta, puedes hacerla a continuación o ingresar lo que estás buscando.

Dibujar un círculo utilizando smoothstep() con dot() o length() produce resultados diferentes.

Considere el simple shader a continuación (vaya a shadertoy.com/new y pegue el código para probarlo).

Básicamente, estoy tratando de averiguar si es posible ajustar la versión de dot() para obtener el mismo resultado exacto para estas dos llamadas de función:

smoothstep( 0.0, r * r, dot(d, d) )
smoothstep( 0.0, r,     length(d) )

Se dibujan dos círculos utilizando dos métodos conocidos. Al leer tutoriales en la red, aprendes que puedes usar la función length() para dibujar un círculo. También aprendes que es bastante costosa, por lo que se presenta una versión más optimizada donde se utiliza la función dot() en su lugar. (En mi mundo, una versión optimizada de algo debería producir el mismo resultado).

Genial. Pero no puedo encontrar una explicación para la relación entre los dos. A veces, el resultado de dot() es, por alguna razón, multiplicado por 4.0 (ver Book of Shaders), lo que produce una salida similar pero no idéntica.

Como puedes ver, step() da como resultado círculos idénticos, mientras que smoothstep() no.

¿Es posible obtener el mismo resultado exacto utilizando smoothstep() mediante algún cálculo?

Ejemplo de shader

float circle_using_length(vec2 position, float radius) {
    vec2 d = position - vec2(0.5);
    return 1.0 - step(radius, length(d));
}

float circle_using_dot(in vec2 position, in float radius) {
    vec2 d = position - vec2(0.5);
    return 1.0 - step(radius * radius, dot(d, d));
}

float smooth_circle_using_length(vec2 position, float radius) {
    vec2 d = position - vec2(0.5);
    return 1.0 - smoothstep(0.0, radius, length(d));
}

float smooth_circle_using_dot(in vec2 position, in float radius) {
    vec2 d = position - vec2(0.5);
    return 1.0 - smoothstep(0.0, radius * radius, dot(d, d) /* magia necesaria aquí */);
}

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord/iResolution.x;

    vec3 col = vec3(0.0);
    col += circle_using_length(uv + vec2(0.4, 0.05), 0.05);
    col += smooth_circle_using_length(uv + vec2(0.4, 0.2), 0.1);

    col += circle_using_dot(uv + vec2(0.2, 0.05), 0.05);
    col += smooth_circle_using_dot(uv + vec2(0.2, 0.2), 0.1);

    fragColor = vec4(col,1.0);
}
Tags:  , ,

Answer

  1. Avatar for davy.ai

    Para obtener el mismo resultado exacto de smoothstep() utilizando dot(), necesitamos ajustar el segundo argumento de smoothstep() para la versión de dot(). En lugar de usar r * r, necesitamos encontrar el valor equivalente para r que dé el mismo resultado que usando length(d) en la versión de length().

    Podemos usar el teorema de Pitágoras para encontrar la relación entre ambos. En la versión de length(), queremos comprobar si la distancia desde el origen hasta position es menor que r. En la versión de dot(), estamos comprobando si la distancia al cuadrado desde el origen hasta position es menor que r * r. Estos dos son equivalentes, así que podemos igualarlos:

    length(d)^2 = dot(d, d) 
    

    Resolvamos para r^2:

    r^2 = length(d)^2 / dot(d, d)
    

    Entonces, para obtener el valor equivalente de r para la versión de dot(), necesitamos tomar la raíz cuadrada de length(d)^2 / dot(d, d):

    r_dot = sqrt(length(d)^2 / dot(d, d))
    

    Luego, podemos reemplazar r * r con r_dot en la función smooth_circle_using_dot():

    float smooth_circle_using_dot(in vec2 position, in float radius) {
        vec2 d = position - vec2(0.5);
        float r_dot = sqrt(length(d)^2 / dot(d, d));
        return 1.0 - smoothstep(0.0, r_dot, dot(d, d));
    }
    

    Ahora ambas versiones del círculo deberían producir el mismo resultado con smoothstep().

Comments are closed.