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

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

相关推荐
皮皮陶3 天前
Unity WebGL交互通信
unity·交互·webgl
鸢_6 天前
【Threejs】相机控制器动画
javascript·实时互动·动画·webgl
_oP_i8 天前
Unity Addressables 系统处理 WebGL 打包本地资源的一种高效方式
unity·游戏引擎·webgl
新中地GIS开发老师8 天前
WebGIS和WebGL的基本概念介绍和差异对比
学习·arcgis·webgl
_oP_i9 天前
Unity 中使用 WebGL 构建并运行时使用的图片必须使用web服务器上的
前端·unity·webgl
flying robot12 天前
Three.js简化 WebGL 的使用
webgl
小彭努力中13 天前
114. 精灵模型标注场景(贴图)
前端·3d·webgl·贴图
小彭努力中13 天前
109. 工厂光源(环境贴图和环境光)
前端·深度学习·3d·webgl·贴图
小彭努力中13 天前
112. gui辅助调节光源阴影
前端·深度学习·3d·webgl
refineiks15 天前
three.js绘制宽度大于1的线,并动态新增顶点
3d·图形渲染·webgl