Shader Wave
- shadertoy: www.shadertoy.com/view/XcdGzX
- geogebra: www.geogebra.org/classic/hzn...
首先给上文的Circle加上一个漂亮的波纹, 用于显示 distance

f(x)=ex exp(x)可以将值贴近到1.0

f(x)=sin(n×x) sin产生了波

f(x)=sin(n×x−m∗uTime)在sin中添加时间,产生了移动
Segment
- geogebra: www.geogebra.org/classic/pnz...
- shadertoy: www.shadertoy.com/view/4ftGDj
假定有线段Segment(A, B), 空间中所有点到线段的最近距离分为三种情况。
- P1 在AB中间,距离为点到线的垂直线
- P2 在AB之外,距离B较近 距离为P2到B的距离
- P3 在AB之外,距离A较近,距离为P3到A的距离
一种复合直觉的求法,便是把三个值\overrightarrow{PB}\\overrightarrow{PA}\overrightarrow{PC}的长度都求出来。取最小的一个长度便可。\overrightarrow{PB}\\overrightarrow{PA}长度非常好求。 PC 的长度可以通过\\overrightarrow{PA}\overrightarrow{PB}的夹角 θ,然后求 sin(θ)×∣∣PA ∣∣。 θ可以通过向量点积求得。 注意上图的 hh参数,表示 P在 AB上投影长度在 AB长度的占比,如果这个值 (0,1)区间,就说明我们需要求 PC 转化成为代码
glsl
float sd_segment(vec2 pct, vec2 pa, vec2 pb) {
vec2 v1 = pct - pa;
vec2 v2 = pb - pa;
vec2 v3 = pct - pb;
float hh = dot(v1, v2) / length(v2) / length(v2);
if (0. < hh && hh < 1.0) {
float theta = acos(dot(v1 ,v2) /length(v1)/length(v2));
float d1 = sin(theta) * length(v1);
return d1;
} else {
return min(length(v1), length(v3)) ;
}
}
这里可以对代码进行优化, PC 的长度其实 PA −AC 。 AC 可以有 hh决定 ,我们已经知道 hh的大小。并且当 hh<0,距离就是 Length(PA ),当 hh>0,距离就是 Length(PB ). 于是代码可以优化为
glsl
float sd_segment2(vec2 pct, vec2 pa, vec2 pb) {
vec2 v1 = pct - pa;
vec2 v2 = pb - pa;
vec2 v3 = pct - pb;
float hh = dot(v1, v2) / length(v2) / length(v2);
if (hh < 0.0) {
return length(v1 - 0. * v2);
} else if (0. < hh && hh < 1.0) {
return length(v1 - hh * v2);
} else {
// v3 = v1 - v2 = pct -pb;
return length(v1 - v2);
}
}
通过上面的优化可以发现,三个判断条件的相似性。 通过引入clamp函数去掉if判断优化性能,通过dot(v2, v2)减少一次length的计算。 最后得到IQ大神最终结果
glsl
float sd_segment3(vec2 pct, vec2 pa, vec2 pb) {
vec2 v1 = pct - pa;
vec2 v2 = pb - pa;
float h = clamp(dot(v1,v2)/dot(v2,v2), 0.0, 1.0);
return length(v1-h*v2);
}
继续使用波纹,可以达到下面的效果 