在上文中,针对SDF逻辑运算存在的元素内部距离有误的问题,IQ提到在2D图形中还有一种方法,便是任何2D图形都可以表示出 线,圆弧,贝塞尔曲线的组合。而这三个基本元素的SDF都是可以推导出来,一个闭合图形的内外边界是非常容易通过cross product来判断的。 通过这种方法便可以计算出所有2D图形的SDF函数。
在之前的系列中,已经推导了线段的SDF。接下来几篇文章将推导圆弧和贝塞尔曲线的SDF,并尝试解决上一篇文章中interior问题
圆弧
- geogebra: www.geogebra.org/classic/vda…
- shadertoy: www.shadertoy.com/view/lftXz4
假定arc的边的单位向量为 , 该向量与y轴的夹角为 , arc的半径为 。 根据 点在向量 的左右边,使用不同的计算距离的方法。左右判断通过cross product. 的坐标为
于是可以很轻松的写出下面的代码
float sd_arc( vec2 p, vec2 sc, float r )
{
p.x = abs(p.x);
float crossValue = p.x*sc.y - p.y*sc.x;
bool isRight = crossValue > 0.0;
if (isRight) {
return distance(p, sc*r);
} else {
return abs(length(p) - r);
}
}
二阶贝塞尔曲线绘制
- shdaertoy: www.shadertoy.com/view/4ccXRM
- geogebra: www.geogebra.org/classic/czy…
在推导二阶贝塞尔曲线之前,先花点时间了解贝塞尔曲线,并且用shader画出一个贝塞尔曲线。 贝塞尔曲线(Bézier Curve)是由法国工程师皮埃尔·贝塞尔(Pierre Bézier)在1960年代提出的,贝塞尔曲线可以是线性的、二次的、三次的或更高阶的。阶数越高,曲线的灵活性越高,但同时也更复杂。曲线是通过对控制点进行数学上的插值计算得到的。关于贝塞尔曲线更多特性可以参考这个youtube视频 www.youtube.com/watch?v=aVw…
对于一个二次贝塞尔曲线,给定三个控制点P0、P1和P2,曲线上任意一点P(t)的计算公式是:
其中t是从0到1的参数。上面的公式是经过展开优化的。在GLSL中用到的是mix线性插值函数
所以有函数
vec2 Bezier(vec2 a, vec2 b, vec2 c, float t) {
return mix(mix(a, c, t), mix(c,b,t), t);
}
在前面打下的基础之后,用shader在画面中画出画出圆和线段都是非常的简单
float Circle(vec2 p, vec2 c) {
// sdf for circle
float d = distance(p,c);
// stoke
return smoothstep(fwidth(d), 0., d-.05);
}
float Line(vec2 p, vec2 a, vec2 b) {
// sdf for segment
vec2 pa = p-a, ba = b-a;
float h = clamp(dot(pa,ba)/dot(ba,ba), 0., 1.0);
vec2 c = a + ba*h;
float d = length(c-p);
// stoke
return smoothstep(fwidth(d), 0., d-.01);
}
最终我们可以得到以下的曲线