淘宝网网站建设,如何做网站卡密,做网站备案须知,干零活一天一结的平台Catmull-Rom在奇异点处的扭曲问题
引言
在计算机图形学和动画中#xff0c;我们经常需要在已知点之间创建平滑的过渡。Catmull-Rom样条是一种流行的插值方法#xff0c;它以简单直观的方式生成经过所有控制点的平滑曲线。本文将深入探讨Catmull-Rom插值的原理、实现和应用。…Catmull-Rom在奇异点处的扭曲问题
引言
在计算机图形学和动画中我们经常需要在已知点之间创建平滑的过渡。Catmull-Rom样条是一种流行的插值方法它以简单直观的方式生成经过所有控制点的平滑曲线。本文将深入探讨Catmull-Rom插值的原理、实现和应用。
什么是Catmull-Rom样条
Catmull-Rom样条是一种三次插值样条由Edwin Catmull和Raphael Rom在1974年提出。它具有以下特点
曲线经过所有控制点插值性只需要四个点即可定义一段曲线C¹连续一阶导数连续局部控制性移动一个点只影响相邻曲线段
数学原理
基本公式
给定四个控制点P₀, P₁, P₂, P₃在P₁和P₂之间的曲线段由以下参数方程定义 P ( t ) [ 1 t t 2 t 3 ] [ 0 1 0 0 − τ 0 τ 0 2 τ τ − 3 3 − 2 τ − τ − τ 2 − τ τ − 2 τ ] [ P 0 P 1 P 2 P 3 ] \mathbf{P}(t) \begin{bmatrix} 1 t t^2 t^3 \end{bmatrix} \begin{bmatrix} 0 1 0 0 \\ -\tau 0 \tau 0 \\ 2\tau \tau-3 3-2\tau -\tau \\ -\tau 2-\tau \tau-2 \tau \end{bmatrix} \begin{bmatrix} \mathbf{P}_0 \\ \mathbf{P}_1 \\ \mathbf{P}_2 \\ \mathbf{P}_3 \end{bmatrix} P(t)[1tt2t3] 0−τ2τ−τ10τ−32−τ0τ3−2ττ−200−ττ P0P1P2P3
其中
t ∈ [0,1] 是插值参数τ (tau) 是张力参数通常取0.5默认值
简化形式
展开后可以得到更直观的表达式 P ( t ) 1 2 [ − t 3 2 t 2 − t 3 t 3 − 5 t 2 2 − 3 t 3 4 t 2 t t 3 − t 2 ] T [ P 0 P 1 P 2 P 3 ] \mathbf{P}(t) \frac{1}{2} \left[ \begin{matrix} -t^3 2t^2 - t \\ 3t^3 - 5t^2 2 \\ -3t^3 4t^2 t \\ t^3 - t^2 \end{matrix} \right]^T \begin{bmatrix} \mathbf{P}_0 \\ \mathbf{P}_1 \\ \mathbf{P}_2 \\ \mathbf{P}_3 \end{bmatrix} P(t)21 −t32t2−t3t3−5t22−3t34t2tt3−t2 T P0P1P2P3
C#代码实现
public static Vector2 CatmullRomInterpolate(Vector2 p0,Vector2 p1,Vector2 p2,Vector2 p3,double t)
{double t2 t * t;double t3 t2 * t;double x 0.5 * ((-p0.X 3 * p1.X - 3 * p2.X p3.X) * t3 (2 * p0.X - 5 * p1.X 4 * p2.X - p3.X) * t2 (-p0.X p2.X) * t (2 * p1.X));double y 0.5 * ((-p0.Y 3 * p1.Y - 3 * p2.Y p3.Y) * t3 (2 * p0.Y - 5 * p1.Y 4 * p2.Y - p3.Y) * t2 (-p0.Y p2.Y) * t (2 * p1.Y));return new Vector2((float)x, (float)y);
}public static ListVector2 CatmullRomSmooth(ListVector2 controlPoints,int segmentPerInterval 10)
{ListVector2 result new ListVector2();if (controlPoints.Count 4){return result;}Vector2 virtualFirst new Vector2(2 * controlPoints[0].X - controlPoints[1].X,2 * controlPoints[0].Y - controlPoints[1].Y);Vector2 virtualLast new Vector2(2 * controlPoints[controlPoints.Count - 1].X - controlPoints[controlPoints.Count - 2].X,2 * controlPoints[controlPoints.Count - 1].Y - controlPoints[controlPoints.Count - 2].Y);ListVector2 extendedPoints new ListVector2{virtualFirst,controlPoints[0]};extendedPoints.AddRange(controlPoints);extendedPoints.Add(virtualLast);for (int i 0; i extendedPoints.Count - 3; i){Vector2 p0 extendedPoints[i];Vector2 p1 extendedPoints[i 1];Vector2 p2 extendedPoints[i 2];Vector2 p3 extendedPoints[i 3];for (int j 0; j segmentPerInterval; j){double t (double)j / segmentPerInterval;Vector2 interpolatedPoint CatmullRomInterpolate(p0, p1, p2, p3, t);result.Add(interpolatedPoint);}}return result;
}以一个DXF图像作为示例: 可以看出黄色部分做完处理平滑了很多,但是作为代价在多段线导数变化剧烈的地方出现了扭曲的现象,这是因为Catmull-Rom 样条作为三次插值样条在控制点处保证 C1 连续性位置和切线连续但在尖锐拐角处
算法会平滑地连接前后线段切线方向在拐点处突然变化导致曲线在拐点附近产生过冲现象
下一篇博客会着重介绍如何消除这种现象