网站开发费用是无形资产,建筑公司企业简介范文,液体硅胶 技术支持 东莞网站建设,想自己做个网站Unity中的网格创建和曲线变形 3D贝塞尔曲线变形贝塞尔曲线基础线性公式二次方公式三次方公式 Unity 实现3D贝塞尔曲线变形准备工作脚本概述变量定义 变量解析函数解析 获取所有子节点GetAllChildren 获取所有子节点UpdateBezierBend 控制点更新CalculateBezier Bezier 曲线公式… Unity中的网格创建和曲线变形 3D贝塞尔曲线变形贝塞尔曲线基础线性公式二次方公式三次方公式 Unity 实现3D贝塞尔曲线变形准备工作脚本概述变量定义 变量解析函数解析 获取所有子节点GetAllChildren 获取所有子节点UpdateBezierBend 控制点更新CalculateBezier Bezier 曲线公式GetBendNormalVector 获取指定点上的法线向量偏移UpdateControlPoint 更新控制点的旋转角度CalculateBezierTangent 曲线求导切线向量 BesselCurveDeformation_ZH 完整代码 自定义网格创建GridCreation_ZH 完整代码GridCreation_ZH 搭载自定义网格创建 运行效果 网格创建和曲线变形 运行情况 在本篇博客中我们将探讨如何使用Unity实现3D贝塞尔曲线变形效果。
3D贝塞尔曲线变形
贝塞尔曲线是一种常用的数学曲线通过控制点和曲线影响力物体我们可以实现对网格的弯曲和变形。
在示例代码中我们可以看到通过设置控制点数组、曲线影响力物体和控制物体曲线施加力等变量。
实现了对网格进行贝塞尔曲线变形的效果。这种技术在游戏中常用于创建动态的形变效果。
如弯曲的绳索、变形的角色模型等。贝塞尔曲线基础
贝塞尔曲线是一种由控制点定义的数学曲线。
在3D空间中我们通常使用3次贝塞尔曲线它由四个控制点起始点、两个中间点和结束点组成。贝塞尔曲线的形状受控制点的位置和权重影响。
控制点的位置决定了曲线经过的路径而权重控制了曲线在控制点之间的弯曲程度。线性公式
给定点P0、P1线性贝兹曲线只是一条两点之间的直线。二次方公式
二次方贝兹曲线的路径由给定点P0、P1、P2的函数Bt追踪
TrueType字型就运用了以贝兹样条组成的二次贝兹曲线。三次方公式
P0、P1、P2、P3四个点在平面或在三维空间中定义了三次方贝兹曲线。
曲线起始于P0走向P1并从P2的方向来到P3。一般不会经过P1或P2。
这两个点只是在那里提供方向资讯。P0和P1之间的间距决定了曲线在转而趋进P3之前
走向P2方向的“长度有多长”。曲线的参数形式为 现代的成象系统如PostScript、Asymptote和Metafont
运用了以贝兹样条组成的三次贝兹曲线用来描绘曲线轮廓。Unity 实现3D贝塞尔曲线变形
准备工作
首先我们需要在Unity中创建一个空的GameObject并将该脚本附加到该GameObject上。
接下来我们需要创建一个网格可以通过导入一个3D模型或者使用Unity内置的网格创建工具来完成。脚本概述
让我们先来看一下脚本的整体结构1.变量定义2.Start()函数2.Update()函数3.初始化函数4.获取所有子节点函数5.贝塞尔曲线Mesh计算相关函数变量定义 [Header(存储贝塞尔曲线控制点数组)]public ListTransform _CpArr;[Header(曲线影响力物体)]public Transform _ForceItem;[Range(0, 100f)][Header(控制物体曲线施加力)]public float _Force 10;[Header(原始顶点位置)]private Vector3[] _OriVertices;[Header(网格数据)]private Mesh _Mesh;[Header(最后一个控制点位置)]//最后一个控制点的位置。用来计算mesh高度来计算tprivate Vector3 _TopPos;变量解析
_CpArr: 存储贝塞尔曲线控制点数组的列表。
_ForceItem: 曲线影响力物体的Transform组件。
_Force: 控制物体曲线施加力的大小范围在0到100之间。
_OriVertices: 原始顶点位置的数组。
_Mesh: 网格数据的Mesh组件。
_TopPos: 最后一个控制点的位置用于计算mesh高度来计算t。函数解析 获取所有子节点
Start(): 在脚本启动时调用用于初始化操作。
Update(): 在每一帧更新时调用用于更新控制点和网格。
Initialize(): 初始化函数用于设置控制点数组和作用力变量等。GetAllChildren 获取所有子节点
GetAllChildren: 获取所有子节点的函数用于递归收集父节点下的所有子节点。/// summary/// 获取所有子节点/// /summary/// param 父节点_Parent/param/// returns/returnsprivate ListTransform GetAllChildren(Transform _Parent){ListTransform _ListTra new ListTransform();foreach (Transform _Child in _Parent){//添加直接子节点_ListTra.Add(_Child);//递归收集后代节点ListTransform _Descendants GetAllChildren(_Child);_ListTra.AddRange(_Descendants);}return _ListTra;}UpdateBezierBend 控制点更新
UpdateBezierBend: 控制点更新函数根据施加在曲线上的力计算并更新控制点的旋转角度。/// summary/// 控制点更新/// 根据施加在曲线上的力计算并更新控制点的旋转角度/// /summaryprivate void UpdateBezierBend(){//曲线弯曲方向Vector3 _BendVector new Vector3(0, 0, 0);//弯曲布尔 true 无弯曲 false 弯曲bool _IsVertical true;for (int i 1; i _CpArr.Count; i){//对于每个控制点将其位置转换为第一个控制点 _CpArr[0] 的局部空间Vector3 _Pos _CpArr[i].TransformPoint(new Vector3(0, 0, 0));_Pos _CpArr[0].InverseTransformPoint(_Pos);//判断当前控制点 x z 是否在非零//如果非零 就证明曲线在一个非垂直方向上弯曲if (IsEqualZero(_Pos.x) false || IsEqualZero(_Pos.z) false){//临时弯曲变量存储_BendVector.x _Pos.x;_BendVector.z _Pos.z;//弯曲布尔_IsVertical false;break;}}//原始网格顶点数组Vector3[] _Temp (Vector3[])_Mesh.vertices.Clone();//遍历所有顶点for (int i 0; i _OriVertices.Length; i){//获取顶点坐标,计算t值Vector3 _OriPos _OriVertices[i];Vector3 _BendPos;//没有弯曲if (_IsVertical true){//当前顶点位置不变_BendPos _OriPos;}//发生弯曲else{//将顶点的原始位置 _OriPos 的 y 分量除以顶部控制点 _TopPos 的 y 分量计算参数 _CurvePositionfloat _T _OriPos.y / _TopPos.y;//获取顶点在贝塞尔曲线上对应的坐标Vector3 _BezierPos CalculateBezier(_T);//获取顶点在曲线上应有的法线偏移向量Vector3 _NormalVector GetBendNormalVector(_T, _OriPos, _BendVector);//获取顶点在曲线上应有的垂直偏移向量Vector3 _VerticalVector new Vector3(_OriPos.x, 0, _OriPos.z) - Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector);//获取顶点最终弯曲位置_BendPos _BezierPos _NormalVector _VerticalVector;}//转换回 mesh 本地坐标系_BendPos _CpArr[0].TransformPoint(_BendPos);_BendPos transform.InverseTransformPoint(_BendPos);_Temp[i] _BendPos;}_Mesh.vertices _Temp;}CalculateBezier Bezier 曲线公式
CalculateBezier: Bezier曲线公式计算函数根据给定的t值计算贝塞尔曲线上的点坐标。/// summary/// Bezier 曲线公式/// 计算贝塞尔曲线上给定参数值 _CurvePosition 对应的位置/// /summary/// param 曲线位置_CurvePosition/param/// returns/returnsprivate Vector3 CalculateBezier(float _CurvePosition){//存储坐标Vector3 _Ret new Vector3(0, 0, 0);//控制点数量int _Number _CpArr.Count - 1;for (int i 0; i _Number; i){//获取第 i 个控制点的世界坐标通过将其位置从控制点的局部坐标系转换到世界坐标Vector3 _Pi _CpArr[i].TransformPoint(new Vector3(0, 0, 0));//将转换后的世界坐标再次转换回第一个控制点的局部坐标系用于与其他控制点进行计算_Pi _CpArr[0].InverseTransformPoint(_Pi);//根据贝塞尔曲线的定义计算贝塞尔基函数的乘积项并与控制点的局部坐标相乘//Cn_m(_Number, i) 是组合数函数 表示从 _Number 个控制点中选择 i 个的组合数_Ret _Ret Mathf.Pow(1 - _CurvePosition, _Number - i) * Mathf.Pow(_CurvePosition, i) * Cn_m(_Number, i) * _Pi;}return _Ret;}GetBendNormalVector 获取指定点上的法线向量偏移
GetBendNormalVector: 获取顶点在曲线上应有的法线偏移向量的函数。/// summary/// 获取指定点上的法线向量偏移/// 计算在贝塞尔曲线上给定参数值 _CurvePosition 对应位置的弯曲法向量/// /summary/// param 曲线位置_CurvePosition/param/// param 原始位置_OriPos/param/// param 弯曲方向_BendVector/param/// returns/returnsprivate Vector3 GetBendNormalVector(float _CurvePosition, Vector3 _OriPos, Vector3 _BendVector){//切线斜率Vector3 _TangentVector CalculateBezierTangent(_CurvePosition);//切线竖直时顶点在在弯曲向量上的投影向量即为法线向量if (IsEqualZero(_TangentVector.x) true IsEqualZero(_TangentVector.z) true){//直接返回 原始位置 _OriPos 投影到弯曲向量 _BendVector 上的向量作为法线向量return Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector);}//存储计算得到的法线向量Vector3 _NormalVector new Vector3(0, 0, 0);//计算切线向量与原始位置向量的点乘float _DirectFlag Vector3.Dot(_BendVector, _OriPos);//判断法线向量朝向法线向量有两个方向//大于零 顶点坐标与弯曲方向同向if (_DirectFlag 0){//切线水平法线向量竖直向下if (IsEqualZero(_TangentVector.y) true){_NormalVector.y -1;}else{//切线朝上法线向量与切线水平同向if (_TangentVector.y 0){_NormalVector.x _TangentVector.x;_NormalVector.z _TangentVector.z;}//切线朝下法线向量与切线水平反向else{_NormalVector.x -_TangentVector.x;_NormalVector.z -_TangentVector.z;}//使法线向量与切线向量水平且垂直于切线向量_NormalVector.y -(_TangentVector.x * _NormalVector.x _TangentVector.z * _NormalVector.z) / _TangentVector.y;}}//小于零 顶点坐标与弯曲方向反向else{//切线水平法线向量竖直向上if (IsEqualZero(_TangentVector.y) true){_NormalVector.y 1;}else{//切线朝上法线向量与切线水平反向if (_TangentVector.y 0){_NormalVector.x -_TangentVector.x;_NormalVector.z -_TangentVector.z;}//切线朝下法线向量与切线水平同向else{_NormalVector.x _TangentVector.x;_NormalVector.z _TangentVector.z;}//使得法线向量与切线向量水平且垂直于切线向量_NormalVector.y -(_TangentVector.x * _NormalVector.x _TangentVector.z * _NormalVector.z) / _TangentVector.y;}}//计算法线向量的模//法线向量的模应为到投影到弯曲面后到中心点的距离float _Magnitude Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector).magnitude;//将法线向量按照该模进行缩放使其长度与距离一致_NormalVector _NormalVector.normalized * _Magnitude;//返回法线向量return _NormalVector;}UpdateControlPoint 更新控制点的旋转角度
UpdateControlPoint: 控制点更新函数根据施加在曲线上的力计算并更新控制点的位置。/// summary/// 更新控制点的旋转角度/// 根据受力计算各个控制点的旋转角度/// /summaryprivate void UpdateControlPoint(){//受力强度float _HandleForce _Force;//根据受力计算各个控制点旋转角度for (int i 1; i _CpArr.Count - 2; i){//计算最大弯曲方向Vector3 _ForcePos _ForceItem.transform.TransformPoint(new Vector3(0, 0, 0));_ForcePos _CpArr[i - 1].InverseTransformPoint(_ForcePos);//计算从控制点到受力位置的向量Vector3 _ToVector _ForcePos - _CpArr[i].localPosition;//得到该控制点的旋转角度Quaternion _MaxRotation Quaternion.FromToRotation(Vector3.up, _ToVector);//获取控制点组件ControlPoint_ZH _Cp _CpArr[i].gameObject.GetComponentControlPoint_ZH();//计算弯曲比例 float _RotateRate Mathf.Clamp(_HandleForce / _Cp._BendForce, 0f, 1.0f);//设置旋转角度_CpArr[i].localRotation Quaternion.Lerp(Quaternion.Euler(0, 0, 0), _MaxRotation, _RotateRate);}}CalculateBezierTangent 曲线求导切线向量
CalculateBezierTangent: 计算贝塞尔曲线上指定t值处的切线向量。/// summary/// 曲线求导切线向量/// /summary/// param 曲线位置_CurvePosition/param/// returns/returnsprivate Vector3 CalculateBezierTangent(float _CurvePosition){//存储计算得到的切线向量Vector3 _Ret new Vector3(0, 0, 0);//控制点数量int _Number _CpArr.Count - 1;for (int i 0; i _Number; i){Vector3 _Pi _CpArr[i].TransformPoint(new Vector3(0, 0, 0));_Pi _CpArr[0].InverseTransformPoint(_Pi);//根据贝塞尔曲线的定义计算贝塞尔基函数的导数项并与控制点的局部坐标相乘//每次循环迭代都将计算得到的导数项加到 _Ret 向量上_Ret _Ret (-1 * (_Number - i) * Mathf.Pow(1 - _CurvePosition, _Number - i - 1) * Mathf.Pow(_CurvePosition, i) * Cn_m(_Number, i) * _Pi i * Mathf.Pow(1 - _CurvePosition, _Number - i) * Mathf.Pow(_CurvePosition, i - 1) * Cn_m(_Number, i) * _Pi);}//返回计算得到的切线向量 _Ret表示贝塞尔曲线上给定参数值 _CurvePosition 对应的切线向量return _Ret;}BesselCurveDeformation_ZH 完整代码
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;/// summary
/// 3D贝塞尔曲线变形
/// /summary
public class BesselCurveDeformation_ZH : MonoBehaviour
{[Header(存储贝塞尔曲线控制点数组)]public ListTransform _CpArr;[Header(曲线影响力物体)]public Transform _ForceItem;[Range(0, 100f)][Header(控制物体曲线施加力)]public float _Force 10;[Header(原始顶点位置)]private Vector3[] _OriVertices;[Header(网格数据)]private Mesh _Mesh;[Header(最后一个控制点位置)]//最后一个控制点的位置。用来计算mesh高度来计算tprivate Vector3 _TopPos;void Start(){Initialize();}void Update(){//根据受力修改控制点UpdateControlPoint();//更新meshUpdateBezierBend();}/// summary/// 初始化/// /summaryprivate void Initialize(){//初始化 控制点数组_CpArr new ListTransform();//if (GameObject.Find(Node1).transform ! null)//{// //数组清空// _CpArr.Clear();// //获取所有控制端点// _CpArr GetAllChildren(GameObject.Find(Node).transform);//}//else{GameObject _EmptyObject0 new GameObject(P0);GameObject _EmptyObject1 new GameObject(P1);GameObject _EmptyObject2 new GameObject(P2);GameObject _EmptyObject3 new GameObject(P3);GameObject _EmptyObject4 new GameObject(P4);GameObject _EmptyObject5 new GameObject(P5);_EmptyObject0.transform.SetParent(transform);_EmptyObject0.transform.localPositionnew Vector3(GridCreation_ZH._Instance._MeshHeight/2*-1, 0,0);_EmptyObject1.transform.SetParent(_EmptyObject0.transform);_EmptyObject1.transform.localPosition new Vector3(0, GridCreation_ZH._Instance._MeshHeight/6, 0);_EmptyObject2.transform.SetParent(_EmptyObject1.transform);_EmptyObject2.transform.localPosition new Vector3(0, GridCreation_ZH._Instance._MeshHeight / 6, 0);_EmptyObject3.transform.SetParent(_EmptyObject2.transform);_EmptyObject3.transform.localPosition new Vector3(0, GridCreation_ZH._Instance._MeshHeight / 6, 0);_EmptyObject4.transform.SetParent(_EmptyObject3.transform);_EmptyObject4.transform.localPosition new Vector3(0, GridCreation_ZH._Instance._MeshHeight / 6, 0);_EmptyObject5.transform.position new Vector3(0, GridCreation_ZH._Instance._MeshHeight, 0);//_EmptyObject5.transform.eulerAnglesnew Vector3(0, 0, 90);_EmptyObject5.transform.SetParent(_EmptyObject4.transform);_CpArr.Add(_EmptyObject0.transform);_CpArr.Add(_EmptyObject1.transform);_CpArr.Add(_EmptyObject2.transform);_CpArr.Add(_EmptyObject3.transform);_CpArr.Add(_EmptyObject4.transform);_CpArr.Add(_EmptyObject5.transform);for (int i 0; i _CpArr.Count; i){if (_CpArr[i].GetComponentControlPoint_ZH() null){//弯曲端点 受力组件添加_CpArr[i].gameObject.AddComponentControlPoint_ZH();}}}//作用力变量float _CurrentNumber 90;if (_CpArr.Count 0){//6个控制点的情况下//其他 自己调节一下for (int i 0; i _CpArr.Count; i){//作用力赋值_CpArr[i].GetComponentControlPoint_ZH()._BendForce _CurrentNumber; _CurrentNumber / 2;}}if (_ForceItemnull){_ForceItem GameObject.Find(ForceCube).transform;}//顶部坐标 获取_TopPos _CpArr[_CpArr.Count - 1].TransformPoint(new Vector3(0, 0, 0));//相对P0坐标 也就是第一个控制点坐标_TopPos _CpArr[0].InverseTransformPoint(_TopPos);//网格获取_Mesh GetComponentMeshFilter().mesh;//网格顶点位置获取_OriVertices (Vector3[])_Mesh.vertices.Clone();//转换成p0的相对坐标for (int i 0; i _OriVertices.Length; i){//世界坐标 局部转世界_OriVertices[i] transform.TransformPoint(_OriVertices[i]);//相对P0坐标 世界转局部_OriVertices[i] _CpArr[0].InverseTransformPoint(_OriVertices[i]);}}/// summary/// 获取所有子节点/// /summary/// param 父节点_Parent/param/// returns/returnsprivate ListTransform GetAllChildren(Transform _Parent){ListTransform _ListTra new ListTransform();foreach (Transform _Child in _Parent){//添加直接子节点_ListTra.Add(_Child);//递归收集后代节点ListTransform _Descendants GetAllChildren(_Child);_ListTra.AddRange(_Descendants);}return _ListTra;}/********************************贝塞尔曲线Mesh计算相关*********************************/// 对原来的顶点做贝塞尔曲线变换得到弯曲变换后对应的点位置/// summary/// 控制点更新/// 根据施加在曲线上的力计算并更新控制点的旋转角度/// /summaryprivate void UpdateBezierBend(){//曲线弯曲方向Vector3 _BendVector new Vector3(0, 0, 0);//弯曲布尔 true 无弯曲 false 弯曲bool _IsVertical true;for (int i 1; i _CpArr.Count; i){//对于每个控制点将其位置转换为第一个控制点 _CpArr[0] 的局部空间Vector3 _Pos _CpArr[i].TransformPoint(new Vector3(0, 0, 0));_Pos _CpArr[0].InverseTransformPoint(_Pos);//判断当前控制点 x z 是否在非零//如果非零 就证明曲线在一个非垂直方向上弯曲if (IsEqualZero(_Pos.x) false || IsEqualZero(_Pos.z) false){//临时弯曲变量存储_BendVector.x _Pos.x;_BendVector.z _Pos.z;//弯曲布尔_IsVertical false;break;}}//原始网格顶点数组Vector3[] _Temp (Vector3[])_Mesh.vertices.Clone();//遍历所有顶点for (int i 0; i _OriVertices.Length; i){//获取顶点坐标,计算t值Vector3 _OriPos _OriVertices[i];Vector3 _BendPos;//没有弯曲if (_IsVertical true){//当前顶点位置不变_BendPos _OriPos;}//发生弯曲else{//将顶点的原始位置 _OriPos 的 y 分量除以顶部控制点 _TopPos 的 y 分量计算参数 _CurvePositionfloat _T _OriPos.y / _TopPos.y;//获取顶点在贝塞尔曲线上对应的坐标Vector3 _BezierPos CalculateBezier(_T);//获取顶点在曲线上应有的法线偏移向量Vector3 _NormalVector GetBendNormalVector(_T, _OriPos, _BendVector);//获取顶点在曲线上应有的垂直偏移向量Vector3 _VerticalVector new Vector3(_OriPos.x, 0, _OriPos.z) - Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector);//获取顶点最终弯曲位置_BendPos _BezierPos _NormalVector _VerticalVector;}//转换回 mesh 本地坐标系_BendPos _CpArr[0].TransformPoint(_BendPos);_BendPos transform.InverseTransformPoint(_BendPos);_Temp[i] _BendPos;}_Mesh.vertices _Temp;}/// summary/// Bezier 曲线公式/// 计算贝塞尔曲线上给定参数值 _CurvePosition 对应的位置/// /summary/// param 曲线位置_CurvePosition/param/// returns/returnsprivate Vector3 CalculateBezier(float _CurvePosition){//存储坐标Vector3 _Ret new Vector3(0, 0, 0);//控制点数量int _Number _CpArr.Count - 1;for (int i 0; i _Number; i){//获取第 i 个控制点的世界坐标通过将其位置从控制点的局部坐标系转换到世界坐标Vector3 _Pi _CpArr[i].TransformPoint(new Vector3(0, 0, 0));//将转换后的世界坐标再次转换回第一个控制点的局部坐标系用于与其他控制点进行计算_Pi _CpArr[0].InverseTransformPoint(_Pi);//根据贝塞尔曲线的定义计算贝塞尔基函数的乘积项并与控制点的局部坐标相乘//Cn_m(_Number, i) 是组合数函数 表示从 _Number 个控制点中选择 i 个的组合数_Ret _Ret Mathf.Pow(1 - _CurvePosition, _Number - i) * Mathf.Pow(_CurvePosition, i) * Cn_m(_Number, i) * _Pi;}return _Ret;}/// summary/// 曲线求导切线向量/// /summary/// param 曲线位置_CurvePosition/param/// returns/returnsprivate Vector3 CalculateBezierTangent(float _CurvePosition){//存储计算得到的切线向量Vector3 _Ret new Vector3(0, 0, 0);//控制点数量int _Number _CpArr.Count - 1;for (int i 0; i _Number; i){Vector3 _Pi _CpArr[i].TransformPoint(new Vector3(0, 0, 0));_Pi _CpArr[0].InverseTransformPoint(_Pi);//根据贝塞尔曲线的定义计算贝塞尔基函数的导数项并与控制点的局部坐标相乘//每次循环迭代都将计算得到的导数项加到 _Ret 向量上_Ret _Ret (-1 * (_Number - i) * Mathf.Pow(1 - _CurvePosition, _Number - i - 1) * Mathf.Pow(_CurvePosition, i) * Cn_m(_Number, i) * _Pi i * Mathf.Pow(1 - _CurvePosition, _Number - i) * Mathf.Pow(_CurvePosition, i - 1) * Cn_m(_Number, i) * _Pi);}//返回计算得到的切线向量 _Ret表示贝塞尔曲线上给定参数值 _CurvePosition 对应的切线向量return _Ret;}/// summary/// 获取指定点上的法线向量偏移/// 计算在贝塞尔曲线上给定参数值 _CurvePosition 对应位置的弯曲法向量/// /summary/// param 曲线位置_CurvePosition/param/// param 原始位置_OriPos/param/// param 弯曲方向_BendVector/param/// returns/returnsprivate Vector3 GetBendNormalVector(float _CurvePosition, Vector3 _OriPos, Vector3 _BendVector){//切线斜率Vector3 _TangentVector CalculateBezierTangent(_CurvePosition);//切线竖直时顶点在在弯曲向量上的投影向量即为法线向量if (IsEqualZero(_TangentVector.x) true IsEqualZero(_TangentVector.z) true){//直接返回 原始位置 _OriPos 投影到弯曲向量 _BendVector 上的向量作为法线向量return Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector);}//存储计算得到的法线向量Vector3 _NormalVector new Vector3(0, 0, 0);//计算切线向量与原始位置向量的点乘float _DirectFlag Vector3.Dot(_BendVector, _OriPos);//判断法线向量朝向法线向量有两个方向//大于零 顶点坐标与弯曲方向同向if (_DirectFlag 0){//切线水平法线向量竖直向下if (IsEqualZero(_TangentVector.y) true){_NormalVector.y -1;}else{//切线朝上法线向量与切线水平同向if (_TangentVector.y 0){_NormalVector.x _TangentVector.x;_NormalVector.z _TangentVector.z;}//切线朝下法线向量与切线水平反向else{_NormalVector.x -_TangentVector.x;_NormalVector.z -_TangentVector.z;}//使法线向量与切线向量水平且垂直于切线向量_NormalVector.y -(_TangentVector.x * _NormalVector.x _TangentVector.z * _NormalVector.z) / _TangentVector.y;}}//小于零 顶点坐标与弯曲方向反向else{//切线水平法线向量竖直向上if (IsEqualZero(_TangentVector.y) true){_NormalVector.y 1;}else{//切线朝上法线向量与切线水平反向if (_TangentVector.y 0){_NormalVector.x -_TangentVector.x;_NormalVector.z -_TangentVector.z;}//切线朝下法线向量与切线水平同向else{_NormalVector.x _TangentVector.x;_NormalVector.z _TangentVector.z;}//使得法线向量与切线向量水平且垂直于切线向量_NormalVector.y -(_TangentVector.x * _NormalVector.x _TangentVector.z * _NormalVector.z) / _TangentVector.y;}}//计算法线向量的模//法线向量的模应为到投影到弯曲面后到中心点的距离float _Magnitude Vector3.Project(new Vector3(_OriPos.x, 0, _OriPos.z), _BendVector).magnitude;//将法线向量按照该模进行缩放使其长度与距离一致_NormalVector _NormalVector.normalized * _Magnitude;//返回法线向量return _NormalVector;}/// summary/// 浮点判断是否为零/// /summary/// param 值_Value/param/// returns/returnsprivate bool IsEqualZero(float _Value){return Mathf.Abs(_Value) 1e-5;}/// summary/// 组合数函数/// 表示从 _Number 个控制点中选择 i 个的组合数/// /summary/// param 控制点_Number/param/// param 组合数m/param/// returns/returnsprivate int Cn_m(int n, int m){int _Ret 1;for (int i 0; i m; i){_Ret _Ret * (n - i) / (i 1);}return _Ret;}/************************************根据受力情况计算控制点坐标(旋转)*****************************//// summary/// 更新控制点的旋转角度/// 根据受力计算各个控制点的旋转角度/// /summaryprivate void UpdateControlPoint(){//受力强度float _HandleForce _Force;//根据受力计算各个控制点旋转角度for (int i 1; i _CpArr.Count - 2; i){//计算最大弯曲方向Vector3 _ForcePos _ForceItem.transform.TransformPoint(new Vector3(0, 0, 0));_ForcePos _CpArr[i - 1].InverseTransformPoint(_ForcePos);//计算从控制点到受力位置的向量Vector3 _ToVector _ForcePos - _CpArr[i].localPosition;//得到该控制点的旋转角度Quaternion _MaxRotation Quaternion.FromToRotation(Vector3.up, _ToVector);//获取控制点组件ControlPoint_ZH _Cp _CpArr[i].gameObject.GetComponentControlPoint_ZH();//计算弯曲比例 float _RotateRate Mathf.Clamp(_HandleForce / _Cp._BendForce, 0f, 1.0f);//设置旋转角度_CpArr[i].localRotation Quaternion.Lerp(Quaternion.Euler(0, 0, 0), _MaxRotation, _RotateRate);}}
}
自定义网格创建
在Unity中网格是由一系列顶点、三角形面和纹理坐标组成的。
通过创建网格我们可以实现各种形状和模型的生成。
在示例代码中我们可以看到通过使用Unity的Mesh和MeshFilter组件。
以及C#脚本中的数据结构和方法实现了一个具有多个相位的简单网格的动态生成。
该网格可以用于游戏场景、建筑模型等各种应用。使用Unity的 Mesh 和 MeshFilter 组件这些组件允许我们创建和管理网格。
Mesh 用于存储网格的几何数据而 MeshFilter 用于将Mesh附加到游戏对象上。GridCreation_ZH 完整代码
using System.Collections;
using System.Collections.Generic;
using UnityEngine;/// summary
/// 网格创建
/// 动态生成一个具有多个相位的简单网格可以根据需求调整网格的宽度、高度、相位数量和末端宽度
/// 以获得不同形状和细分程度的网格
/// /summary
public class GridCreation_ZH : MonoBehaviour
{//单例public static GridCreation_ZH _Instance;[Header(网格宽度)]public float _MeshWidth 1f;[Header(网格高度)]public float _MeshHeight 10f;[Header(网格分段数)]public int _PhaseCount 20;[Header(网格末端宽度)]public float _EndWidth 0.1f;void Awake(){_Instance this;//计算每个相位的高度float _PhaseHeight _MeshHeight / (_PhaseCount - 1);//计算每个相位的宽度float _DecreaseWidth (_MeshWidth - _EndWidth) / (_PhaseCount - 1);//顶点数组//长度为 _PhaseCount * 6每个相位有 6 个顶点Vector3[] _Vertices new Vector3[_PhaseCount * 6];float _BottomY -_MeshHeight * 0.5f;for (int i 0; i _PhaseCount; i){//根据当前相位的索引计算每个顶点的位置//并将其存储在 _Vertices 数组中float _CurWidth _MeshWidth - _DecreaseWidth * i;_Vertices[i * 6 0] new Vector3(_BottomY i * _PhaseHeight, (_CurWidth / 2), 0);_Vertices[i * 6 1] new Vector3(_BottomY i * _PhaseHeight, (_CurWidth / 4), -Mathf.Sqrt(3) * _CurWidth / 4);_Vertices[i * 6 2] new Vector3(_BottomY i * _PhaseHeight, -(_CurWidth / 4), -Mathf.Sqrt(3) * _CurWidth / 4);_Vertices[i * 6 3] new Vector3(_BottomY i * _PhaseHeight, -(_CurWidth / 2), 0);_Vertices[i * 6 4] new Vector3(_BottomY i * _PhaseHeight, -(_CurWidth / 4), Mathf.Sqrt(3) * _CurWidth / 4);_Vertices[i * 6 5] new Vector3(_BottomY i * _PhaseHeight, (_CurWidth / 4), Mathf.Sqrt(3) * _CurWidth / 4);}//法线数组//长度也为 _PhaseCount * 6与顶点数组相对应Vector3[] _Normals new Vector3[_PhaseCount * 6];for (int i 0; i _PhaseCount; i){//根据当前相位的索引计算每个顶点的法线方向并将其存储在 _Normals 数组中float _CurWidth _MeshWidth - _DecreaseWidth * i;_Normals[i * 6 0] new Vector3(0, (_CurWidth / 2), 0).normalized;_Normals[i * 6 1] new Vector3(0, (_CurWidth / 4), -Mathf.Sqrt(3) * _CurWidth / 4).normalized;_Normals[i * 6 2] new Vector3(0, -(_CurWidth / 4), -Mathf.Sqrt(3) * _CurWidth / 4).normalized;_Normals[i * 6 3] new Vector3(0, -(_CurWidth / 2), 0).normalized;_Normals[i * 6 4] new Vector3(0, -(_CurWidth / 4), Mathf.Sqrt(3) * _CurWidth / 4).normalized;_Normals[i * 6 5] new Vector3(0, (_CurWidth / 4), Mathf.Sqrt(3) * _CurWidth / 4).normalized;}//三角形索引数组//长度为 (_PhaseCount - 1) * 6 * 6每个相位之间的相邻顶点构成一个三角形int[] _Indices new int[(_PhaseCount - 1) * 6 * 6];for (int i 1; i _PhaseCount; i){for (int j 0; j 6; j){//嵌套循环遍历根据相邻顶点的索引构建三角形的索引并将其存储在 _Indices 数组中int nextIndex (j 1) % 6;_Indices[(i - 1) * 6 * 6 j * 6 0] (i - 1) * 6 j;_Indices[(i - 1) * 6 * 6 j * 6 1] i * 6 nextIndex;_Indices[(i - 1) * 6 * 6 j * 6 2] (i - 1) * 6 nextIndex;_Indices[(i - 1) * 6 * 6 j * 6 3] (i - 1) * 6 j;_Indices[(i - 1) * 6 * 6 j * 6 4] i * 6 j;_Indices[(i - 1) * 6 * 6 j * 6 5] i * 6 nextIndex;}}//将之前计算得到的顶点、法线和三角形索引分别赋值给网格的对应属性创建了一个简单的网格Mesh _Mesh GetComponentMeshFilter().mesh;_Mesh.vertices _Vertices;_Mesh.normals _Normals;_Mesh.triangles _Indices;transform.position Vector3.up * 5;transform.eulerAngles new Vector3(0, 0, 90);}
}
GridCreation_ZH 搭载
记得在空物体上添加Mesh Renderer、Mesh Filter 组件。Hierarchy 窗口自定义网格创建 运行效果 网格创建和曲线变形 运行情况
Hierarchy 窗口运行效果除了以上示例代码还有其他相关的技术和工具可以扩展和优化网格创建和曲线变形效果。
例如使用Shader进行高级的顶点变形和着色效果、使用插件或库来简化曲线绘制和变形操作等。通过深入学习和实践这些技术您可以在Unity中创建出更加生动和富有创意的场景和模型。
掌握网格创建和曲线变形的原理和方法将为您的游戏开发和视觉效果设计带来更多的可能性和灵活性。希望这些信息能够进一步满足您对Unity中 网格创建和曲线变形的需求。 如果您有任何特定的问题或需要更深入的讨论请随时提出。
路漫漫其修远兮与君共勉。