上代码:
//获取基于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);
}
}
}