Consider the simple shader below (head over to shadertoy.com/new and paste the code to try it out).
Basically, I'm trying to figure out if it is possible to tweak the dot()
version to get the exact same result for these two function calls:
smoothstep( 0.0, r * r, dot(d, d) )
smoothstep( 0.0, r, length(d) )
Two circles are drawn using two well-known methods. Reading tutorials on the net, you learn that you can use the length()
function to draw a circle. You also learn that it is quite expensive, so a more optimized version is presented where the dot()
function is used instead. (In my world, an optimized version of something should produce the same result.)
Great. But I cannot find an explanation for the relationship between the two. Sometimes the result from dot()
is, for some reason, multiplied by 4.0
(see Book of Shaders), giving similar but not identical output.
As you can see, step()
yields identical circles whereas smoothstep()
does not.
Is it possible to get the exact same output from smoothstep()
using some math?
Shader example
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) /* magic needed here */);
}
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);
}
smoothstep(0.0, radius, length(d));
returns the same as
smoothstep(0.0, radius, sqrt(dot(d, d)));
However it is not equal
smoothstep(0.0, radius * radius, dot(d, d));
That is, because smoothstep is not a linear function and therefore smoothstep(0, a, b)
is not equal to smoothstep(0, a*a, b*b)
.
See smoothstep
:
t = clamp((x - edge0) / (edge1 - edge0), 0.0, 1.0); return t * t * (3.0 - 2.0 * t);
(a*a - 0) / (b*b - 0)
is not equal (a - 0) / (b - 0)
.