使用带有 dot() 或 length() 的 smoothstep() 绘制圆会产生不同的结果

Drawing a circle using smoothstep() with dot() or length() produces different results

考虑下面的简单着色器(header 到 shadertoy.com/new 并粘贴代码进行尝试)。

基本上,我想弄清楚是否可以调整 dot() 版本以获得 完全相同的结果 这两个函数调用:

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

使用两种well-known方法绘制了两个圆。阅读网上的教程,您了解到可以使用 length() 函数绘制圆。您还了解到它非常昂贵,因此提供了一个更优化的版本,其中使用了 dot() 函数。 (在我的世界里,某物的优化版本应该产生相同的结果。)

太棒了。但是我找不到对两者之间关系的解释。有时 dot() 的结果出于某种原因乘以 4.0(参见 Book of Shaders),得到相似但不相同的输出。

如您所见,step() 产生相同的圆,而 smoothstep() 则不。

是否可以使用一些数学运算从 smoothstep() 获得完全相同的输出?

着色器示例

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);
}

它们不同是因为 x2 与 x 不是线性关系。

假设 x 是圆的半径。 (x/2) 在圆的中途。那么,(x/2)2 就是 (x2)/4。这意味着当距离是从中心到边缘的一半时,dot(d, d) 版本只会表现得像是从中心到边缘的一个 四分之一

使用距离的平方(用 dot 得到的值)仅在您尝试测试一个点是否在圆内时才有效,而不是 where在圆圈内

smoothstep(0.0, radius, length(d));

returns 等同于

smoothstep(0.0, radius, sqrt(dot(d, d)));

然而不等于

smoothstep(0.0, radius * radius, dot(d, d));

也就是说,因为 smoothstep 不是线性函数,因此 smoothstep(0, a, b) 不等于 smoothstep(0, a*a, b*b)

参见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) 不等于 (a - 0) / (b - 0).