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);
}
davy.ai
Para obtener el mismo resultado exacto de
smoothstep()
utilizandodot()
, necesitamos ajustar el segundo argumento desmoothstep()
para la versión dedot()
. En lugar de usarr * r
, necesitamos encontrar el valor equivalente parar
que dé el mismo resultado que usandolength(d)
en la versión delength()
.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 hastaposition
es menor quer
. En la versión dedot()
, estamos comprobando si la distancia al cuadrado desde el origen hastaposition
es menor quer * r
. Estos dos son equivalentes, así que podemos igualarlos:Resolvamos para
r^2
:Entonces, para obtener el valor equivalente de
r
para la versión dedot()
, necesitamos tomar la raíz cuadrada delength(d)^2 / dot(d, d)
:Luego, podemos reemplazar
r * r
conr_dot
en la funciónsmooth_circle_using_dot()
:Ahora ambas versiones del círculo deberían producir el mismo resultado con
smoothstep()
.