使用带有 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)
.
考虑下面的简单着色器(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)
.