问题背景
上次写了动态绘制立方体,这最近又来了新功能,绘制圆柱(风筒),要求是给了很多节点,根据节点去动态绘制风筒,风筒就是圆柱连接而成的,可以理解为管道,还有就是拐角处注意倒角,圆润过度过来。
实现原理
动态绘制圆柱mesh,注意,圆柱的mesh绘制远比立方体复杂得多,上节阐述过基本mesh创建立方体,有兴趣可以去看看https://www.cnblogs.com/answer-yj/p/11231247.html,顶点以及倒角需要你自己去插值出来,其中倒角是使用贝塞尔曲线插值过度出来的。
实现步骤
1.顶点坐标
2.uv坐标
3.构建索引
4.构建实体
具体过程
1.准备顶点数据
构建圆柱应当理解为这是多个圆插值出来的,首先设置圆的顶点坐标,首先说第一种方式,我先前考虑,在世界坐标空间中,创建一个Gamobject通过它的坐标作为圆上顶点坐标,不从本地创建坐标了(因为涉及坐标转换,矩阵变换),所以我用了这种方式
第一种方式:
具体代码:
/// <summary> /// 设置起中间点断面顶点数据 /// </summary> /// <param name="currPos">当前点坐标</param> /// <param name="curDirection">朝向</param> /// <param name="radius">半径</param> private void SetSectionVertexData(Vector3 currPos, Vector3 curDirection, float radius,float u) { // 首先计算导线点相对场景中心点的偏移坐标 Vector3 position = currPos - center; // 计算direction、right、up向量(注:Unity3d中使用左手坐标系) Vector3 direction = curDirection.normalized; Vector3 right = Vector3.Cross(Vector3.up, direction).normalized; Vector3 up = Vector3.Cross(direction, right).normalized; angleStep = 360.0f / (float)devide; GameObject gameObject = new GameObject(); gameObject.transform.position = position + radius * right;for (int i = 0; i < 360.0f; i += (int)angleStep) { gameObject.transform.RotateAround(currPos, direction, angleStep); vertices.Add(gameObject.transform.position); } Destroy(gameObject); }
代码中direction是方向向量(上一点指向下一点坐标),构建顶点也需要顺序,我这是在右侧90度逆时针创建的,gameObject.transform.position = position + radius * right;这是起点顶点,一共十个顶点。
第二种方式:
我已经用第一种方式功能都做完了,创建好了,完事领导说我们风筒可能要在子线程创建,哇,一瞬间透心凉,GameObject就意味着淘汰了,矩阵变换是躲不开了。从本地创建圆,这句话的意思是指模型本地坐标,即左手坐标系(0,0,0)点 找一点为(0,0,0)点去创建。
具体代码:
1 /// <summary> 2 /// 设置起点圆顶点数据 3 /// </summary> 4 /// <param name="startPos">起点坐标</param> 5 /// <param name="nextPos">下一点坐标</param> 6 private void SetStartVertexData(Vector3 startPos, Vector3 nextPos) 7 { 8 // 首先计算导线点相对场景中心点的偏移坐标 9 Vector3 position = startPos - center; 10 Vector3 nextPosition = nextPos - center; 11 12 // 计算direction、right、up向量(注:Unity3d中使用左手坐标系) 13 Vector3 direction = (nextPosition - position).normalized;//z轴 14 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;//x轴,右 15 Vector3 up = Vector3.Cross(direction, right).normalized;//x和z得到up 16 // 设起点圆定点数据 17 SetSectionVertexData(position, direction, up, right, uvPosU); 18 } 19 20 21 /// <summary> 22 /// 设置断面顶点数据 23 /// </summary> 24 /// <param name="currentPos">当前点坐标</param> 25 /// <param name="direction">朝向</param> 26 private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u) 27 { 28 // 构建旋转矩阵 29 Matrix4x4 m = Utils.MakeBasis(right, up, direction); 30 for (float i = 0f; i < 360.0f; i += 36.0f) 31 { 32 // 计算顶点 33 float rad = Mathf.Deg2Rad * i; 34 Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f)); 35 // 计算V坐标 36 float v = ((baseValue * 36.0f) / 360.0f); 37 // 保存顶点坐标&纹理坐标 38 vertices.Add(targetPos); 39 } 40 }
这里解释下,过程是这样的,在本地坐标构建圆,通过旋转矩阵变换到世界坐标。Vector3 direction = (nextPosition - position).normalized;,一般将指向方向向量为坐标系Z轴,假设y轴(0,1,0)为Up方向,那么Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;Z轴与假设Y轴的叉乘得到的是X轴的方向向量(即right方向),垂直与zy平面,但是接下来求方向向量(Z轴)与X轴的叉乘,指定是我们要的Y方向,因为确定的Z和X(垂直XZ平面)。
Utils.MakeBasis具体逻辑如下:
1 /// <summary> 2 /// 通过坐标轴构建矩阵 3 /// </summary> 4 /// <param name="xAxis">x轴</param> 5 /// <param name="yAxis">y轴</param> 6 /// <param name="zAxis">z轴</param> 7 /// <returns>4x4矩阵</returns> 8 public static Matrix4x4 MakeBasis(Vector3 xAxis, Vector3 yAxis, Vector3 zAxis) 9 { 10 Matrix4x4 mat = Matrix4x4.identity; 11 12 mat.m00 = xAxis.x; 13 mat.m10 = xAxis.y; 14 mat.m20 = xAxis.z; 15 16 mat.m01 = yAxis.x; 17 mat.m11 = yAxis.y; 18 mat.m21 = yAxis.z; 19 20 mat.m02 = zAxis.x; 21 mat.m12 = zAxis.y; 22 mat.m22 = zAxis.z; 23 24 return mat; 25 }
矩阵就不解释了,自己看吧。Matrix4x4 m = Utils.MakeBasis(right, up, direction);这是在得到所构建节点的坐标系即坐标走向。因为要把构建的圆放到该点坐标下。m.MultiplyPoint3x4是将构建圆的顶点转换到当前点坐标系下,new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f)圆上点坐标XY平面构建元,Z为0,到这里圆心始终是(0,0,0),那么将这个圆放到目标点下需要再加上目标点在当下坐标系的偏移量(即坐标值)。所以加currentPos。
到这就将两种设置顶点坐标方式说完了。还有就是拐角处圆的构建,需要用贝塞尔曲线差值出来。
代码:
1 /// <summary> 2 /// 设置中间点顶点数据 3 /// </summary> 4 /// <param name="currentPos"></param> 5 /// <param name="prevPos"></param> 6 /// <param name="nextPos"></param> 7 private void SetPrepareVertexData(Vector3 currentPos, Vector3 prevPos, Vector3 nextPos) 8 { 9 // 首先计算导线点相对场景中心点的偏移坐标 10 Vector3 prevPosition = prevPos - center; 11 Vector3 position = currentPos - center; 12 Vector3 anitherposition = nextPos - center; 13 14 // 计算前一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系) 15 Vector3 prevDirection = (position - prevPosition).normalized; 16 Vector3 prevRight = Vector3.Cross(Vector3.up, prevDirection).normalized; 17 Vector3 prevUp = Vector3.Cross(prevDirection, prevRight).normalized; 18 19 // 计算后一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系) 20 Vector3 anitherDirection = (anitherposition - position).normalized; 21 Vector3 anitherlRight = Vector3.Cross(Vector3.up, anitherDirection).normalized; 22 Vector3 anitherUp = Vector3.Cross(anitherDirection, anitherlRight).normalized; 23 24 float angle = Vector3.Angle(-prevDirection, anitherDirection); 25 26 if (angle >= 179.0) 27 { 28 //生成断面数据不倒角处理 29 uvPosU += (GetPointDistance(position, prevPosition) / textureSizeL); 30 SetSectionVertexData(position, prevDirection, prevUp, prevRight, uvPosU); 31 return; 32 } 33 //倒角处理 34 35 //前后两段风筒长度 36 float PrevLength = Vector3.Distance(position, prevPosition); 37 float anithorLength = Vector3.Distance(position, anitherposition); 38 39 indentationValue = PrevLength > anithorLength ? (anithorLength * 0.25f) : (PrevLength * 0.25f);//缩进为短风筒的1/4 40 41 // 计算缩进后的位置 42 Vector3 prevEnd = position - prevDirection * indentationValue;//左 43 Vector3 behindStart = position + anitherDirection * indentationValue;// 右 44 45 uvPosU += (GetPointDistance(prevPosition, prevEnd) / textureSizeL); 46 // 生成前段结束断面顶点数据 47 SetSectionVertexData(prevEnd, prevDirection, prevUp, prevRight, uvPosU); 48 49 // 生成中间倒角断面顶点数据 50 //插值0-1 51 float timer = 0.1f; 52 Vector3 prevpos = prevEnd; 53 for (float i = 1.0f; i <= 9.0f; i++) 54 { 55 Vector3 pos = Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart); 56 // 计算断面方向 57 Vector3 direction = (pos - prevpos).normalized; 58 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized; 59 Vector3 up = Vector3.Cross(direction, right).normalized; 60 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL); 61 // 生成断面顶点数据 62 SetSectionVertexData(pos, direction, up, right, uvPosU); 63 //递增插值时间 64 timer += 0.1f; 65 //更新前一个点坐标 66 prevpos = pos; 67 } 68 // 生成后段起始断面顶点数据 69 SetSectionVertexData(behindStart, anitherDirection, anitherUp, anitherlRight, ++uvPosU); 70 }
1到9插10个圆。需要找拐角处前后两个点分别为开始和结束插值点,Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart);timer为每次插值(可理解为时刻)。此函数(三阶贝塞尔曲线)具体逻辑:
1 /// <summary> 2 /// 计算三阶贝塞尔曲线点坐标 3 /// </summary> 4 /// <param name="t">时刻(0.0~1.0)</param> 5 /// <param name="p0">起点坐标</param> 6 /// <param name="p1">中间点坐标</param> 7 /// <param name="p2">终点坐标</param> 8 /// <returns>坐标点</returns> 9 public static Vector3 CalculateCubicBezierPoint(float t, Vector3 p0, Vector3 p1, Vector3 p2) 10 { 11 float u = 1.0f - t; 12 float tt = t * t; 13 float uu = u * u; 14 15 return (p0 * uu + p1 * 2 * u * t + p2 * tt); 16 }
这样准备顶点坐标就结束了。
2.uv坐标
UV坐标是与顶点一一对应的,所以在创建顶点时最好一起计算出来,我这里的要求是,上下面贴想同纹理,而且是重复帖,所以V向基本就贴1次上下两面贴,就要从圆上10个点分成两半,或者按角度角度分,一般从0-1后一半从1-0这样就可以实现,重点是U方向坐标值的计算,每个横向重复帖很多次纹理,需要根据贴图比例以及长度去设置,但是U的值应该是累加的。一次从起点到终点贴。知道这一点就好办了。
代码:
//横向代码,U是累加的,根据圆柱长度去设置计算。 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL); //V方向按上述规律贴 /// <summary> /// 设置断面顶点数据 /// </summary> /// <param name="currentPos">当前点坐标</param> /// <param name="direction">朝向</param> private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u) { // 构建旋转矩阵 Matrix4x4 m = Utils.MakeBasis(right, up, direction); int baseValue = 2; for (float i = 0f; i < 360.0f; i += 36.0f) { // 计算顶点 float rad = Mathf.Deg2Rad * i; Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f)); // 计算V坐标 float v = ((baseValue * 36.0f) / 360.0f); // 保存顶点坐标&纹理坐标 vertices.Add(targetPos); uvs.Add(new Vector2(u, v)); if (i > 108) { baseValue -= 2; } else { baseValue += 2; } } }
这float v = ((baseValue * 36.0f) / 360.0f);算法需要按要求设置。
3.构建索引
构建索引就要按照定点顺序去构建,连接圆与圆之间的曲面,索引值也是累加的比如这里十个点,第一个圆是0-9/第二个10-19/以此类推。
1 /// <summary> 2 /// 设置索引数据 3 /// </summary> 4 private void SetIndexData() 5 { 6 int faceCount = (vertices.Count / devide) - 1; 7 int baseValue; 8 for (int i = 0; i < faceCount; i++) 9 { 10 baseValue = 0; 11 for (int j = 1; j <= 10; j++) 12 { 13 if (j < 10) 14 { 15 triangles.Add(i * 10 + baseValue); 16 triangles.Add(i * 10 + 11 + baseValue); 17 triangles.Add(i * 10 + baseValue + 10); 18 19 triangles.Add(i * 10 + baseValue); 20 triangles.Add(i * 10 + baseValue + 1); 21 triangles.Add(i * 10 + 11 + baseValue); 22 } 23 else 24 { 25 triangles.Add(i * 10 + baseValue); 26 triangles.Add(i * 10 + 10); 27 triangles.Add(i * 10 + baseValue + 10); 28 29 triangles.Add(i * 10 + baseValue); 30 triangles.Add(i * 10); 31 triangles.Add(i * 10 + 10); 32 } 33 baseValue++; 34 } 35 } 36 }
注意构建顺序不然顺逆时针容易反了。
4.构建实体
这里解释最后一步了,也是最简单的一步了,不解释上代码:
1 /// <summary> 2 /// 绘制风筒实体 3 /// </summary> 4 /// <param name="posList">风筒节点数据集合</param> 5 /// <param name="radius">风筒半径</param> 6 public void DrawAirDuct(List<Vector3> posList, float radius) 7 { 8 this.radius = radius; 9 PrepareVertexData(posList); 10 SetIndexData(); 11 Mesh mesh = new Mesh 12 { 13 vertices = vertices.ToArray(), 14 triangles = triangles.ToArray(), 15 uv = uvs.ToArray(), 16 }; 17 mesh.RecalculateNormals(); 18 GameObject airDuctObj = new GameObject(name); 19 airDuctObj.AddComponent<MeshFilter>().mesh = mesh; 20 airDuctObj.AddComponent<MeshRenderer>().material = new Material(Resources.Load<Material>("Materials/Mine/LanewayColor")); 21 22 // 添加碰撞器MeshCollider 23 airDuctObj.AddComponent<MeshCollider>(); 24 }
切记别忘了重新计算法线mesh.RecalculateNormals();这才出预期效果。
完整代码:
1 using System; 2 using System.Collections.Generic; 3 using UnityEngine; 4 5 namespace Tx3d.Framework 6 { 7 /// <summary> 8 /// 绘制风筒实体 9 /// </summary> 10 public class AirDuct : Entity 11 { 12 #region Fields 13 14 //计算圆角的缩进值 15 private float indentationValue; 16 17 //圆划分为多少等份 18 private int devide = 10; 19 20 //中心点坐标 21 private Vector3 center = Vector3.zero; 22 23 //顶点坐标集合 24 private List<Vector3> vertices = new List<Vector3>(); 25 26 //uv坐标集合 27 private List<Vector2> uvs = new List<Vector2>(); 28 29 //索引集合 30 private List<int> triangles = new List<int>(); 31 32 //半径 33 private float radius; 34 35 //贴图长度缩放尺寸 36 private float textureSizeL = 10.24f; 37 38 //uv坐标的u坐标值 39 private float uvPosU = 0; 40 41 #endregion 42 43 #region Public Methods 44 45 /// <summary> 46 /// 风筒实体 47 /// </summary> 48 /// <param name="name">实体名</param> 49 public AirDuct(string name) : base(name) 50 { 51 52 } 53 54 /// <summary> 55 /// 绘制风筒实体 56 /// </summary> 57 /// <param name="posList">风筒节点数据集合</param> 58 /// <param name="radius">风筒半径</param> 59 public void DrawAirDuct(List<Vector3> posList, float radius) 60 { 61 this.radius = radius; 62 PrepareVertexData(posList); 63 SetIndexData(); 64 Mesh mesh = new Mesh 65 { 66 vertices = vertices.ToArray(), 67 triangles = triangles.ToArray(), 68 uv = uvs.ToArray(), 69 }; 70 mesh.RecalculateNormals(); 71 GameObject airDuctObj = new GameObject(name); 72 airDuctObj.AddComponent<MeshFilter>().mesh = mesh; 73 airDuctObj.AddComponent<MeshRenderer>().material = new Material(Resources.Load<Material>("Materials/Mine/LanewayColor")); 74 75 // 添加碰撞器MeshCollider 76 airDuctObj.AddComponent<MeshCollider>(); 77 } 78 79 /// <summary> 80 /// 释放风筒实体 81 /// </summary> 82 public override void Dispose() 83 { 84 85 } 86 87 /// <summary> 88 /// 射线查询 89 /// </summary> 90 /// <param name="ray">射线</param> 91 /// <param name="hit">拾取结果</param> 92 /// <param name="maxDistance">最大拾取距离</param> 93 /// <returns>拾取成功返回true,否则返回false</returns> 94 public override bool Raycast(Ray ray, out RaycastHit hit, float maxDistance) 95 { 96 var collider = gameObject.GetComponent<MeshCollider>(); 97 return collider.Raycast(ray, out hit, maxDistance); 98 } 99 100 /// <summary> 101 /// 体积查询 <see cref="Entity.VolumeQuery(PlaneBoundedVolume)"/> 102 /// </summary> 103 /// <param name="volume">查询体</param> 104 /// <returns>查询成功返回true,否则返回false</returns> 105 public override bool VolumeQuery(PlaneBoundedVolume volume) 106 { 107 throw new NotImplementedException(); 108 } 109 110 #endregion 111 112 #region Private Methods 113 114 /// <summary> 115 /// 准备顶点数据 116 /// </summary> 117 private void PrepareVertexData(List<Vector3> posList) 118 { 119 for (int i = 0; i < posList.Count; i++) 120 { 121 //起点圆面 122 if (i == 0) 123 { 124 SetStartVertexData(posList[i], posList[i + 1]); 125 } 126 else if (i != posList.Count - 1) 127 { 128 ////中间点(求缩进点设置圆角) 129 SetPrepareVertexData(posList[i], posList[i - 1], posList[i + 1]); 130 } 131 else 132 { 133 //终点圆面 134 SetEndVertexData(posList[i], posList[i - 1]); 135 } 136 } 137 } 138 139 /// <summary> 140 /// 设置起点圆顶点数据 141 /// </summary> 142 /// <param name="startPos">起点坐标</param> 143 /// <param name="nextPos">下一点坐标</param> 144 private void SetStartVertexData(Vector3 startPos, Vector3 nextPos) 145 { 146 // 首先计算导线点相对场景中心点的偏移坐标 147 Vector3 position = startPos - center; 148 Vector3 nextPosition = nextPos - center; 149 150 // 计算direction、right、up向量(注:Unity3d中使用左手坐标系) 151 Vector3 direction = (nextPosition - position).normalized;//z轴 152 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized;//x轴,右 153 Vector3 up = Vector3.Cross(direction, right).normalized;//x和z得到up 154 // 设起点圆定点数据 155 SetSectionVertexData(position, direction, up, right, uvPosU); 156 } 157 158 /// <summary> 159 /// 设置终点圆顶点数据 160 /// </summary> 161 /// <param name="endPos">终点坐标</param> 162 /// <param name="previousPos">上一点坐标</param> 163 private void SetEndVertexData(Vector3 endPos, Vector3 previousPos) 164 { 165 // 首先计算导线点相对场景中心点的偏移坐标 166 Vector3 position = endPos - center; 167 Vector3 PreviousPosition = previousPos - center; 168 169 // 计算direction、right、up向量(注:Unity3d中使用左手坐标系) 170 Vector3 direction = (position - PreviousPosition).normalized;//指向下一点(结束)方向向量 171 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized; 172 Vector3 up = Vector3.Cross(direction, right).normalized; 173 //计算U值 174 uvPosU += (GetPointDistance(position, PreviousPosition) / textureSizeL); 175 SetSectionVertexData(position, direction, up, right, uvPosU); 176 } 177 178 /// <summary> 179 /// 设置断面顶点数据 180 /// </summary> 181 /// <param name="currentPos">当前点坐标</param> 182 /// <param name="direction">朝向</param> 183 private void SetSectionVertexData(Vector3 currentPos, Vector3 direction, Vector3 up, Vector3 right, float u) 184 { 185 // 构建旋转矩阵 186 Matrix4x4 m = Utils.MakeBasis(right, up, direction); 187 int baseValue = 2; 188 for (float i = 0f; i < 360.0f; i += 36.0f) 189 { 190 // 计算顶点 191 float rad = Mathf.Deg2Rad * i; 192 Vector3 targetPos = currentPos + m.MultiplyPoint3x4(new Vector3(Mathf.Cos(rad) * radius, Mathf.Sin(rad) * radius, 0.0f)); 193 // 计算V坐标 194 float v = ((baseValue * 36.0f) / 360.0f); 195 // 保存顶点坐标&纹理坐标 196 vertices.Add(targetPos); 197 uvs.Add(new Vector2(u, v)); 198 if (i > 108) 199 { 200 baseValue -= 2; 201 } 202 else 203 { 204 baseValue += 2; 205 } 206 } 207 } 208 209 /// <summary> 210 /// 设置中间点顶点数据 211 /// </summary> 212 /// <param name="currentPos"></param> 213 /// <param name="prevPos"></param> 214 /// <param name="nextPos"></param> 215 private void SetPrepareVertexData(Vector3 currentPos, Vector3 prevPos, Vector3 nextPos) 216 { 217 // 首先计算导线点相对场景中心点的偏移坐标 218 Vector3 prevPosition = prevPos - center; 219 Vector3 position = currentPos - center; 220 Vector3 anitherposition = nextPos - center; 221 222 // 计算前一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系) 223 Vector3 prevDirection = (position - prevPosition).normalized; 224 Vector3 prevRight = Vector3.Cross(Vector3.up, prevDirection).normalized; 225 Vector3 prevUp = Vector3.Cross(prevDirection, prevRight).normalized; 226 227 // 计算后一段巷道的direction、right、up向量(注:Unity3d中使用左手坐标系) 228 Vector3 anitherDirection = (anitherposition - position).normalized; 229 Vector3 anitherlRight = Vector3.Cross(Vector3.up, anitherDirection).normalized; 230 Vector3 anitherUp = Vector3.Cross(anitherDirection, anitherlRight).normalized; 231 232 float angle = Vector3.Angle(-prevDirection, anitherDirection); 233 234 if (angle >= 179.0) 235 { 236 //生成断面数据不倒角处理 237 uvPosU += (GetPointDistance(position, prevPosition) / textureSizeL); 238 SetSectionVertexData(position, prevDirection, prevUp, prevRight, uvPosU); 239 return; 240 } 241 //倒角处理 242 243 //前后两段风筒长度 244 float PrevLength = Vector3.Distance(position, prevPosition); 245 float anithorLength = Vector3.Distance(position, anitherposition); 246 247 indentationValue = PrevLength > anithorLength ? (anithorLength * 0.25f) : (PrevLength * 0.25f);//缩进为短风筒的1/4 248 249 // 计算缩进后的位置 250 Vector3 prevEnd = position - prevDirection * indentationValue;//左 251 Vector3 behindStart = position + anitherDirection * indentationValue;// 右 252 253 uvPosU += (GetPointDistance(prevPosition, prevEnd) / textureSizeL); 254 // 生成前段结束断面顶点数据 255 SetSectionVertexData(prevEnd, prevDirection, prevUp, prevRight, uvPosU); 256 257 // 生成中间倒角断面顶点数据 258 //插值0-1 259 float timer = 0.1f; 260 Vector3 prevpos = prevEnd; 261 for (float i = 1.0f; i <= 9.0f; i++) 262 { 263 Vector3 pos = Utils.CalculateCubicBezierPoint(timer, prevEnd, position, behindStart); 264 // 计算断面方向 265 Vector3 direction = (pos - prevpos).normalized; 266 Vector3 right = Vector3.Cross(Vector3.up, direction).normalized; 267 Vector3 up = Vector3.Cross(direction, right).normalized; 268 uvPosU += (GetPointDistance(pos, prevpos) / textureSizeL); 269 // 生成断面顶点数据 270 SetSectionVertexData(pos, direction, up, right, uvPosU); 271 //递增插值时间 272 timer += 0.1f; 273 //更新前一个点坐标 274 prevpos = pos; 275 } 276 // 生成后段起始断面顶点数据 277 SetSectionVertexData(behindStart, anitherDirection, anitherUp, anitherlRight, ++uvPosU); 278 } 279 280 /// <summary> 281 /// 设置索引数据 282 /// </summary> 283 private void SetIndexData() 284 { 285 int faceCount = (vertices.Count / devide) - 1; 286 int baseValue; 287 for (int i = 0; i < faceCount; i++) 288 { 289 baseValue = 0; 290 for (int j = 1; j <= 10; j++) 291 { 292 if (j < 10) 293 { 294 triangles.Add(i * 10 + baseValue); 295 triangles.Add(i * 10 + 11 + baseValue); 296 triangles.Add(i * 10 + baseValue + 10); 297 298 triangles.Add(i * 10 + baseValue); 299 triangles.Add(i * 10 + baseValue + 1); 300 triangles.Add(i * 10 + 11 + baseValue); 301 } 302 else 303 { 304 triangles.Add(i * 10 + baseValue); 305 triangles.Add(i * 10 + 10); 306 triangles.Add(i * 10 + baseValue + 10); 307 308 triangles.Add(i * 10 + baseValue); 309 triangles.Add(i * 10); 310 triangles.Add(i * 10 + 10); 311 } 312 baseValue++; 313 } 314 } 315 } 316 317 /// <summary> 318 /// 求两点距离 319 /// </summary> 320 /// <param name="prevPos">前一点坐标</param> 321 /// <param name="nextPos">后一点坐标</param> 322 /// <returns></returns> 323 private float GetPointDistance(Vector3 prevPos, Vector3 nextPos) 324 { 325 return Vector3.Distance(prevPos, nextPos); 326 } 327 328 #endregion 329 } 330 }