简体   繁体   English

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

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

Consider the simple shader below (header over to shadertoy.com/new and paste the code to try it out).考虑下面的简单着色器(前往shadertoy.com/new并粘贴代码进行试用)。

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:基本上,我试图弄清楚是否可以调整dot()版本以获得与这两个 function 调用完全相同的结果

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.看了网上的教程,了解到可以使用length() function来画圆。 You also learn that it is quite expensive, so a more optimized version is presented where the dot() function is used instead.您还了解到它非常昂贵,因此提供了一个更优化的版本,其中使用dot() function 代替。 (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.有时,由于某种原因, dot()的结果乘以4.0 (请参阅着色器书),得出相似但不相同的 output。

As you can see, step() yields identical circles whereas smoothstep() does not.如您所见, step()产生相同的圆圈,而smoothstep()不会。

典型的 GLSL 圈子:)

Is it possible to get the exact same output from smoothstep() using some math?是否有可能使用一些数学从smoothstep()获得完全相同的 output ?

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

They're different because x 2 is not linear with respect to x.它们是不同的,因为 x 2关于 x 不是线性的。

Let's say that x is the radius of the circle.假设x是圆的半径。 (x/2) is halfway across the circle. (x/2) 在圆的中间。 Well, (x/2) 2 is (x 2 )/4.嗯,(x/2) 2是(x 2 )/4。 This means that when the distance is halfway from the center to the edge, the dot(d, d) version will only act like it is one quarter of the way from the center to the edge.这意味着当距离是从中心到边缘的一半时, dot(d, d)版本只会像从中心到边缘的四分之一一样。

Using the square of the distance (what you get with dot ) is only valid if you're trying to test if a point is within a circle, not where it is within the circle.仅当您尝试测试点是否在圆内而不是圆内的位置时,使用距离的平方(使用dot得到的)才有效。

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) .也就是说,因为 smoothstep 不是线性 function ,因此smoothstep(0, a, b)不等于smoothstep(0, a*a, b*b)

See smoothstep :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) . (a*a - 0) / (b*b - 0)不等于(a - 0) / (b - 0)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM