Unity 绘制Catmull-Rom样线


化石原创文章,转载请注明来源并保留原文链接


曲线有很多种,有名的比如贝塞尔曲线。这里的Catmull-Rom样线(曲线)跟贝塞尔不同,贝塞尔曲线,控制点并不都在生成的曲线上。Catmull-Rom曲线则不同,它的特征就是生成的曲线一定会穿过所有的控制点。

Catmull-Rom样线

上图是一个典型的Catmull-Rom样线。这个图的意思是,要求出P1到P2之间的曲线上的点,则我们需要P1前面一个点,P2后面一个点。然后我们就可以根据插值的精度,求出P1到P2之间的所有的插值点。从而形成P1到P2之间的曲线线段。

对于每个插值点,数学公式如下:

Point = 0.5f * ((2 * P1) + (-P0 + P2) * t + (2 * P0 - 5 * P1 + 4 * P2 - P3) * t * t + (-P0 + 3 * P1 - 3 * P2 + P3) * t * t * t);

有了这个公式后,我们就可以曲线线段中的所有的插值数据。不过要注意的是:对于上图显示的P0到P1之间的线段,和P2到P3之间的线段。理论上点的数量是不够的。因为要得到P0到P1之间的值,必须P0前面有一个点。要得到P2到P3间的点,P3后必须有一个点。4个点才能求出一个线段。

在程序里,我们有两种方式处理:

1、求P0到P1之间的曲线线段,只要在P0前用最后一个点补齐即可(如图中,是P3、P0、P1、P2四个点依次排序)。P2到P3之间的点,用第一个点做最后一个点即可(如图中,是P1、P2、P3、P1排序)。

2、第一个点重复一次。P0、P0、P1、P2求第一个线段。最后一个点重复一次,P1、P2、P3、P3求最后一个线段。

核心代码如下:

public class CurvePath {

	// how many points interpolated between 2 control points
	public int resolution = 20;

	private List<Vector3> result;

	public void SetControlPoints (Vector3[] pts) {

		if (pts.Length < 4) {
			throw new UnityException ("Catmull-Rom curve need 4 points at least.");
		}

		result = new List<Vector3>();

		//first line segment
		Vector3 P0 = pts [0];
		Vector3 P1 = pts [0];
		Vector3 P2 = pts [1];
		Vector3 P3 = pts [2];
		ProcessSegment (P0, P1, P2, P3);

		//line segment between first and last
		for (int i = 0; i < pts.Length - 3; i++) {
			P0 = pts [i];
			P1 = pts [i + 1];
			P2 = pts [i + 2];
			P3 = pts [i + 3];

			ProcessSegment (P0, P1, P2, P3);
		}

		//last line segment
		P0 = pts [pts.Length - 3];
		P1 = pts [pts.Length - 2];
		P2 = pts [pts.Length - 1];
		P3 = pts [pts.Length - 1];

		ProcessSegment (P0, P1, P2, P3);
	}

	public Vector3[] GetResultPoints() {
		return result.ToArray();
	}

	private void ProcessSegment(Vector3 P0, Vector3 P1, Vector3 P2, Vector3 P3) {
		for (int step = 0; step < resolution; ++step) {
			float t = (float) step / (float) resolution;
			float t2 = t * t;
			float t3 = t2 * t;
			Vector3 point = 0.5f * ((2 * P1) + (-P0 + P2) * t + (2 * P0 - 5 * P1 + 4 * P2 - P3) * t2 + (-P0 + 3 * P1 - 3 * P2 + P3) * t3);
			result.Add (point);
		}
	}
}

我的github上的工程演示了更多控制点(必须大于4)的例子。结果大致如下:

Catmull-Rom示例

化石原创文章,转载请注明来源并保留原文链接


发表评论

电子邮件地址不会被公开。 必填项已用*标注