Unity中的Bezier曲线与曲面上点的计算

24 篇文章 1 订阅

上代码:

//获取基于4个控制点的Bezier曲线上的点
Vector3 GetBezierPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
{
	t = Mathf.Clamp01(t);
	float s = 1f - t;
	return s * s * s * p0 + 3f * s * s * t * p1 + 3f * s * t * t * p2 + t * t * t * p3;
}

//获取基于12个控制点的曲面上的点,就是说ps是12个元素的数组
Vector3 GetSurfacePoint12(Vector3[] ps, float tu, float tv)
{
	Vector3 p0 = GetBezierPoint(ps[0], ps[1], ps[2], ps[3], tu);
	Vector3 p3 = GetBezierPoint(ps[9], ps[8], ps[7], ps[6], tu);
	Vector3 p1_11 = new Vector3(ps[1].x, (ps[1].y + ps[11].y) * 0.5f, ps[11].z);
	Vector3 p2_4 = new Vector3(ps[2].x, (ps[2].y + ps[4].y) * 0.5f, ps[4].z);
	Vector3 p1 = GetBezierPoint(ps[11], p1_11, p2_4, ps[4], tu);
	Vector3 p10_8 = new Vector3(ps[8].x, (ps[8].y + ps[10].y) * 0.5f, ps[10].z);
	Vector3 p5_7 = new Vector3(ps[7].x, (ps[5].y + ps[5].y) * 0.5f, ps[5].z);
	Vector3 p2 = GetBezierPoint(ps[10], p10_8, p5_7, ps[5], tu);
	return GetBezierPoint(p0, p1, p2, p3, tv);
}

//获取基于16个控制点的曲面上的点,就是说ps是16个元素的数组
Vector3 GetSurfacePoint16(Vector3[] ps, float tu, float tv)
{
	Vector3 p0 = GetBezierPoint(ps[0], ps[1], ps[2], ps[3], tu);
	Vector3 p1 = GetBezierPoint(ps[4], ps[5], ps[6], ps[7], tu);
	Vector3 p2 = GetBezierPoint(ps[8], ps[9], ps[10], ps[11], tu);
	Vector3 p3 = GetBezierPoint(ps[12], ps[13], ps[14], ps[15], tu);
	return GetBezierPoint(p0, p1, p2, p3, tv);
}

对于基于12个控制点的曲面,其点序号排布方式如下:

 对于基于16个控制点的曲面,其点序号排布方式如下:

添加一个应用案例吧,代码如下:

using UnityEngine;
using UnityEngine.Events;

public class BezierSurface : MonoBehaviour
{
	[SerializeField]
	NodePos nodePosPrefab;
	[SerializeField]
	Material mat;

	[SerializeField]
	float blockWidth = 10;
	[SerializeField]
	float blockLength = 10;
	[SerializeField]
	int countSurfX = 1;
	[SerializeField]
	int countSurfY = 1;

	Mesh mesh = null;
	int countUnitVert = 10;
	int countVertX = 0;
	int countVertY = 0;

	BezierNode[] nodes;
	void Start()
	{
		//添加渲染组件
		MeshFilter filter = GetComponent<MeshFilter>();
		if (!filter) filter = gameObject.AddComponent<MeshFilter>();
		if (!mesh) mesh = new Mesh();
		filter.mesh = mesh;

		MeshRenderer render = GetComponent<MeshRenderer>();
		if (!render) render = gameObject.AddComponent<MeshRenderer>();
		render.material = mat;

		nodes = new BezierNode[(countSurfX + 1) * (countSurfY + 1)];
		Debug.Log(nodes.Length);
		for (int i = 0; i <= countSurfX; i++)
		{
			for (int j = 0; j <= countSurfY; j++)
			{
				int index = i + (countSurfX + 1) * j;
				NodePos nodePos = Instantiate(nodePosPrefab);
				nodePos.name = "Node" + index.ToString("d3");
				nodePos.transform.position = new Vector3(i * blockWidth, 0, j * blockLength);
				//
				NodePos nodePosL = Instantiate(nodePosPrefab);
				nodePosL.transform.SetParent(nodePos.transform);
				nodePosL.transform.localPosition = Vector3.left * blockWidth * 0.333f;
				//
				NodePos nodePosT = Instantiate(nodePosPrefab);
				nodePosT.transform.SetParent(nodePos.transform);
				nodePosT.transform.localPosition = Vector3.forward * blockLength * 0.333f;
				//
				NodePos nodePosR = Instantiate(nodePosPrefab);
				nodePosR.transform.SetParent(nodePos.transform);
				nodePosR.transform.localPosition = Vector3.right * blockWidth * 0.333f;
				//
				NodePos nodePosB = Instantiate(nodePosPrefab);
				nodePosB.transform.SetParent(nodePos.transform);
				nodePosB.transform.localPosition = Vector3.back * blockLength * 0.333f;
				//
				BezierNode node = new BezierNode();
				node.position = nodePos.transform.position;
				node.localPosL = nodePosL.transform.localPosition;
				node.localPosT = nodePosT.transform.localPosition;
				nodePos.onPosChanged += delegate (Vector3 pos) { node.position = pos; };
				nodePosL.onPosChanged += delegate (Vector3 pos) { node.localPosL = pos; };
				nodePosT.onPosChanged += delegate (Vector3 pos) { node.localPosT = pos; };
				nodePosR.onPosChanged += delegate (Vector3 pos) { node.localPosR = pos; };
				nodePosB.onPosChanged += delegate (Vector3 pos) { node.localPosB = pos; };
				//
				node.onChangePos += delegate (Vector3 pos) { nodePos.transform.localPosition = pos; };
				node.onChangePosL += delegate (Vector3 pos) { nodePosL.transform.localPosition = pos; };
				node.onChangePosR += delegate (Vector3 pos) { nodePosR.transform.localPosition = pos; };
				node.onChangePosT += delegate (Vector3 pos) { nodePosT.transform.localPosition = pos; };
				node.onChangePosB += delegate (Vector3 pos) { nodePosB.transform.localPosition = pos; };
				//
				nodes[index] = node;
			}
		}
		//获取模型点位置
		countVertX = countSurfX * countUnitVert + 1;
		countVertY = countSurfY * countUnitVert + 1;

		mesh.vertices = GetVerts();

		int countTriX = countVertX - 1;
		int countTriY = countVertY - 1;
		int[] triangles = new int[countTriX * countTriY * 6];

		for (int i = 0; i < countTriX; i++)
		{
			for (int j = 0; j < countTriY; j++)
			{
				int index = (i + countTriX * j) * 6;
				triangles[index] = i + countVertX * j;
				triangles[index + 1] = i + countVertX * (j + 1);
				triangles[index + 2] = triangles[index] + 1;
				triangles[index + 3] = triangles[index + 2];
				triangles[index + 4] = triangles[index + 1];
				triangles[index + 5] = triangles[index + 1] + 1;
			}
		}

		mesh.triangles = triangles;
		mesh.RecalculateNormals();
	}

	void Update()
	{
		UpdateMesh();
	}

	void UpdateMesh()
	{
		bool updateEnabled = false;
		for (int i = 0; i < nodes.Length; i++)
		{
			if (nodes[i].isDirty)
			{
				nodes[i].UpdatePre();
				updateEnabled = true;
				break;
			}
		}

		if (!updateEnabled) return;

		mesh.vertices = GetVerts();
		mesh.RecalculateNormals();
	}

	Vector3[] GetVerts()
	{
		Vector3[] verts = new Vector3[countVertX * countVertY];
		for (int i = 0; i < countSurfX; i++)
		{
			for (int j = 0; j < countSurfY; j++)
			{
				//获取单个Surface的控制点数组
				Vector3[] ps = new Vector3[12];
				int idLB = i + (countSurfX + 1) * j;
				int idRB = idLB + 1;
				int idLT = idLB + countSurfX + 1;
				int idRT = idLT + 1;
				ps[0] = nodes[idLB].position;
				ps[1] = nodes[idLB].posR;
				ps[2] = nodes[idRB].posL;
				ps[3] = nodes[idRB].position;
				ps[4] = nodes[idRB].posT;
				ps[5] = nodes[idRT].posB;
				ps[6] = nodes[idRT].position;
				ps[7] = nodes[idRT].posL;
				ps[8] = nodes[idLT].posR;
				ps[9] = nodes[idLT].position;
				ps[10] = nodes[idLT].posB;
				ps[11] = nodes[idLB].posT;
				//通过控制点数组获取surface上点的位置
				int indexVertStart = countVertX * j * countUnitVert + i * countUnitVert;
				for (int m = 0; m <= countUnitVert; m++)
				{
					for (int n = 0; n <= countUnitVert; n++)
					{
						float tu = m * (1f / countUnitVert);
						float tv = n * (1f / countUnitVert);
						int index = indexVertStart + n * countVertX + m;
						verts[index] = GetSurfacePoint12(ps, tu, tv);
					}
				}
			}
		}
		return verts;
	}

	Vector3 GetSurfacePoint12(Vector3[] ps, float tu, float tv)
	{
		Vector3 p0 = GetBezierPoint(ps[0], ps[1], ps[2], ps[3], tu);
		Vector3 p3 = GetBezierPoint(ps[9], ps[8], ps[7], ps[6], tu);
		Vector3 p1_11 = new Vector3(ps[1].x, (ps[1].y + ps[11].y) * 0.5f, ps[11].z);
		Vector3 p2_4 = new Vector3(ps[2].x, (ps[2].y + ps[4].y) * 0.5f, ps[4].z);
		Vector3 p1 = GetBezierPoint(ps[11], p1_11, p2_4, ps[4], tu);
		Vector3 p10_8 = new Vector3(ps[8].x, (ps[8].y + ps[10].y) * 0.5f, ps[10].z);
		Vector3 p5_7 = new Vector3(ps[7].x, (ps[5].y + ps[5].y) * 0.5f, ps[5].z);
		Vector3 p2 = GetBezierPoint(ps[10], p10_8, p5_7, ps[5], tu);
		return GetBezierPoint(p0, p1, p2, p3, tv);
	}

	Vector3 GetBezierPoint(Vector3 p0, Vector3 p1, Vector3 p2, Vector3 p3, float t)
	{
		t = Mathf.Clamp01(t);
		float s = 1f - t;
		return s * s * s * p0 + 3f * s * s * t * p1 + 3f * s * t * t * p2 + t * t * t * p3;
	}

	public class BezierNode
	{
		Vector3 posPre;
		public Vector3 position;

		Vector3 posLPre;
		public Vector3 localPosL;

		public Vector3 localPosR
		{
			get { return -localPosL; }
			set { localPosL = -value; }
		}

		Vector3 posTPre;
		public Vector3 localPosT;

		public Vector3 localPosB
		{
			get { return -localPosT; }
			set { localPosT = -value; }
		}

		public BezierNode()
		{
			UpdatePre();
		}

		public UnityAction<Vector3> onChangePos;
		public UnityAction<Vector3> onChangePosL;
		public UnityAction<Vector3> onChangePosR;
		public UnityAction<Vector3> onChangePosT;
		public UnityAction<Vector3> onChangePosB;
		public void UpdatePre()
		{
			if (posPre != position)
			{
				posPre = position;
				onChangePos?.Invoke(position);
			}
			if (posLPre != localPosL)
			{
				posLPre = localPosL;
				onChangePosL?.Invoke(localPosL);
				onChangePosR?.Invoke(localPosR);
			}
			if (posTPre != localPosT)
			{
				posTPre = localPosT;
				onChangePosT?.Invoke(localPosT);
				onChangePosB?.Invoke(localPosB);
			}
		}

		public bool isDirty
		{
			get
			{
				return posPre != position || posLPre != localPosL || posTPre != localPosT;
			}
		}

		public Vector3 posL { get { return position + localPosL; } }
		public Vector3 posT { get { return position + localPosT; } }
		public Vector3 posR { get { return position + localPosR; } }
		public Vector3 posB { get { return position + localPosB; } }
	}
}

补一个NodePos类的代码:

using UnityEngine;
using UnityEngine.Events;

public class NodePos : MonoBehaviour
{
	Vector3 posPre;
	void Start()
	{
		posPre = transform.localPosition;
	}

	public UnityAction<Vector3> onPosChanged;
	void Update()
	{
		if (posPre != transform.localPosition)
		{
			posPre = transform.localPosition;
			onPosChanged?.Invoke(transform.localPosition);
		}
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值