2D SDF推导8: 2阶贝塞尔曲线

在上文中,针对SDF逻辑运算存在的元素内部距离有误的问题,IQ提到在2D图形中还有一种方法,便是任何2D图形都可以表示出 线,圆弧,贝塞尔曲线的组合。而这三个基本元素的SDF都是可以推导出来,一个闭合图形的内外边界是非常容易通过cross product来判断的。 通过这种方法便可以计算出所有2D图形的SDF函数。 在之前的系列中,已经推导了线段的SDF。接下来几篇文章将推导圆弧和贝塞尔曲线的SDF,并尝试解决上一篇文章中interior问题

圆弧

假定arc的边的单位向量为 <math xmlns="http://www.w3.org/1998/Math/MathML"> v ⃗ \vec{v} </math>v , 该向量与y轴的夹角为 <math xmlns="http://www.w3.org/1998/Math/MathML"> θ \theta </math>θ , arc的半径为 <math xmlns="http://www.w3.org/1998/Math/MathML"> r r </math>r 。 根据 <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P 点在向量 <math xmlns="http://www.w3.org/1998/Math/MathML"> v ⃗ \vec{v} </math>v 的左右边,使用不同的计算距离的方法。左右判断通过cross product. <math xmlns="http://www.w3.org/1998/Math/MathML"> v ⃗ \vec{v} </math>v 的坐标为 <math xmlns="http://www.w3.org/1998/Math/MathML"> ( s i n ( θ ) , c o s ( θ ) ( sin(\theta), cos(\theta) </math>(sin(θ),cos(θ)

于是可以很轻松的写出下面的代码

glsl 复制代码
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);
    }
}

二阶贝塞尔曲线绘制

在推导二阶贝塞尔曲线之前,先花点时间了解贝塞尔曲线,并且用shader画出一个贝塞尔曲线。 贝塞尔曲线(Bézier Curve)是由法国工程师皮埃尔·贝塞尔(Pierre Bézier)在1960年代提出的,贝塞尔曲线可以是线性的、二次的、三次的或更高阶的。阶数越高,曲线的灵活性越高,但同时也更复杂。曲线是通过对控制点进行数学上的插值计算得到的。关于贝塞尔曲线更多特性可以参考这个youtube视频 www.youtube.com/watch?v=aVw...

对于一个二次贝塞尔曲线,给定三个控制点P0、P1和P2,曲线上任意一点P(t)的计算公式是: <math xmlns="http://www.w3.org/1998/Math/MathML"> P ( t ) = ( 1 − t ) 2 ⋅ P 0 + 2 ⋅ ( 1 − t ) ⋅ t ⋅ P 1 + t 2 ⋅ P 2 P(t) = (1 - t)^2 \cdot P0 + 2 \cdot (1 - t) \cdot t \cdot P1 + t^2 \cdot P2 </math>P(t)=(1−t)2⋅P0+2⋅(1−t)⋅t⋅P1+t2⋅P2

其中t是从0到1的参数。上面的公式是经过展开优化的。在GLSL中用到的是mix线性插值函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> m i x ( x , y , a ) = x ⋅ ( 1 − a ) + y ⋅ a mix(x, y, a) = x\cdot(1-a) + y\cdot a </math>mix(x,y,a)=x⋅(1−a)+y⋅a

所以有函数

glsl 复制代码
vec2 Bezier(vec2 a, vec2 b, vec2 c, float t) {
    return mix(mix(a, c, t), mix(c,b,t), t);
}

在前面打下的基础之后,用shader在画面中画出画出圆和线段都是非常的简单

glsl 复制代码
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); 
}

最终我们可以得到以下的曲线

相关推荐
linweidong6 小时前
让低端机也能飞:Canvas/WebGL/Viz 分层、降级渲染与数据抽样策略
前端框架·webgl·canvas·前端动画·前端面经·css渲染·动画优化
CAD老兵2 天前
打造高性能二维图纸渲染引擎系列(一):Batched Geometry 助你轻松渲染百万实体
前端·webgl·three.js
CAD老兵2 天前
打造高性能二维图纸渲染引擎系列(三):高性能 CAD 文本渲染背后的隐藏工程
前端·webgl·three.js
CAD老兵2 天前
打造高性能二维图纸渲染引擎系列(二):创建结构化和可扩展的渲染场景
前端·webgl·three.js
王维志4 天前
使用Asp.Net WebApi(.net 8)托管Unity WebGL
unity·游戏引擎·webgl
Zuckjet_4 天前
第 7 篇:交互的乐趣 - 响应用户输入
前端·javascript·webgl
xhload3d5 天前
智慧钢厂高炉冶炼仿真分析 | 图扑数字孪生
3d·智慧城市·html5·webgl·数字孪生·可视化·热力图·智慧工厂·工业互联网·工业组态·高炉炼铁·数字工厂·高炉炉体·智慧高炉·高炉
猪哥帅过吴彦祖5 天前
第 8 篇:更广阔的世界 - 加载 3D 模型
前端·javascript·webgl
猪哥帅过吴彦祖5 天前
第 7 篇:交互的乐趣 - 响应用户输入
前端·webgl