如何在Unity里翻转网格法线

将 .fbx 文件导入 Unity 后,偶尔会发现模型正面是透明的,背面却能看到,这是由于模型网格中的三角形顶点的排列顺序相反造成的,解决此问题可能到 Maya、3DMax 等 3D 建模软件中将模型中有问题的部分进行翻转法线操作,也可以在 Unity 中将三角形顶点的排列顺序进行反转,避免使用 3D 建模软件重新导出又导入到 Unity 的麻烦,如下代码:
    
    
SkinnedMeshRenderer skinnedMeshRenderer = gameObject . GetComponent < SkinnedMeshRenderer > ( ) ; Mesh mesh = skinnedMeshRenderer . sharedMesh ; int [ ] triangles = mesh . triangles ; for ( int i = 0 , len = triangles . Length ; i < len ; i += 3 ) { // 交换三角形的首尾索引 int t = triangles [ i ] ; triangles [ i ] = triangles [ i + 2 ] ; triangles [ i + 2 ] = t ; } mesh . triangles = triangles ;
为了方便可以在 Hierarchy 面板中给 GameObject 的快捷菜单添加 Flip Mesh Normals 选项,快速对拥有网格的对象进行法线翻转操作。以下代码放置在名称 Editor 的文件夹下
    
    
public class EditorFlipMeshNormals : Editor { [ MenuItem ( "GameObject/Flip Mesh Normals" , false , 11 ) ] private static void FlipMeshNormalsOnGameObject ( ) { } }
为了不修改 Unity 内置对象的共享网格(如 Cube、Sphere等),还可以设置是否启动 Flip Mesh Normals 快捷菜单项,此处我们只想处理我们自己的资源,只需要判断共享网格的资源路径是否在 Assets文件下就行了(内置对象的资源路径都在项目根目录的 Library 文件夹下)。
    
    
[ MenuItem ( "GameObject/Flip Mesh Normals" , true ) ] private static bool ValidateFlipMeshNormalsOnGameObject ( ) { //Mesh mesh = ... string path = AssetDatabase . GetAssetPath ( mesh ) ; // 是否为 Assets 文件夹下的资源 bool isAssetFolder = path . IndexOf ( "Assets/" ) > - 1 ; if ( isAssetFolder ) { return true ; // 在 Assets 文件夹下,启用 Flip Mesh Normals 菜单项 } return false ; }
最后完整的代码如下(须放置在名为 Editor 的文件夹下):
    
    
# if UNITY_EDITOR using UnityEditor ; using UnityEngine ; /// <summary> /// 在 Hierarchy 视图对象的快捷菜单中增加 Flip Mesh Normals(反转网格法线)项 /// </summary> public class EditorFlipMeshNormals : Editor { /// <summary> 翻转网格的法线 </summary> private static void FlipMeshNormals ( Mesh mesh ) { int [ ] triangles = mesh . triangles ; for ( int i = 0 , len = triangles . Length ; i < len ; i += 3 ) { // 交换三角形的首尾索引 int t = triangles [ i ] ; triangles [ i ] = triangles [ i + 2 ] ; triangles [ i + 2 ] = t ; } mesh . triangles = triangles ; } /// <summary> 翻转多个游戏对象网格的法线 </summary> private static void FlipMeshNormals ( GameObject [ ] gameObjects ) { for ( int i = 0 , len = gameObjects . Length ; i < len ; i ++ ) { GameObject go = gameObjects [ i ] ; Mesh mesh = null ; SkinnedMeshRenderer skinnedMeshRenderer = go . GetComponent < SkinnedMeshRenderer > ( ) ; if ( skinnedMeshRenderer ) { mesh = skinnedMeshRenderer . sharedMesh ; } else { MeshFilter meshFilter = go . GetComponent < MeshFilter > ( ) ; if ( meshFilter ) { mesh = meshFilter . sharedMesh ; } } if ( mesh ) { string path = AssetDatabase . GetAssetPath ( mesh ) ; // 是否为 Assets 文件夹下的资源(Assets 文件夹下的资源才能编辑,避免编辑到 Unity 的内置资源的网格) bool isAssetFolder = path . IndexOf ( "Assets/" ) > - 1 ; if ( isAssetFolder ) { FlipMeshNormals ( mesh ) ; } } } } /// <summary> 验证所选择的游戏对象有网格时菜单才可用(不计算子级) </summary> [ MenuItem ( "GameObject/Flip Mesh Normals" , true ) ] private static bool ValidateFlipMeshNormalsOnGameObject ( ) { bool isEnableMenuItem = false ; GameObject [ ] gameObjects = Selection . gameObjects ; for ( int i = 0 , len = gameObjects . Length ; i < len ; i ++ ) { GameObject go = gameObjects [ i ] ; Mesh mesh = null ; SkinnedMeshRenderer skinnedMeshRenderer = go . GetComponent < SkinnedMeshRenderer > ( ) ; if ( skinnedMeshRenderer ) { mesh = skinnedMeshRenderer . sharedMesh ; } else { MeshFilter meshFilter = go . GetComponent < MeshFilter > ( ) ; if ( meshFilter ) { mesh = meshFilter . sharedMesh ; } } if ( mesh ) { string path = AssetDatabase . GetAssetPath ( mesh ) ; // 是否为 Assets 文件夹下的资源(Assets 文件夹下的资源才能编辑,避免编辑到 Unity 的内置资源的网格) bool isAssetFolder = path . IndexOf ( "Assets/" ) > - 1 ; if ( isAssetFolder ) { isEnableMenuItem = true ; break ; } } } return isEnableMenuItem ; } [ MenuItem ( "GameObject/Flip Mesh Normals" , false , 11 ) ] private static void FlipMeshNormalsOnGameObject ( ) { FlipMeshNormals ( Selection . gameObjects ) ; } } # endif
效果如下图:
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
手动计算网格法线可以通过以下步骤实现: 1. 获取网格的顶点和三角形列表。可以使用 `Mesh.vertices` 和 `Mesh.triangles` 属性来获取它们。 ```csharp Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; ``` 2. 为每个顶点计算法线。遍历三角形列表,对于每个三角形,计算它的面法线,并将其添加到其三个顶点的法线总和中。 ```csharp Vector3[] normals = new Vector3[vertices.Length]; for (int i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; Vector3 v1 = vertices[i1]; Vector3 v2 = vertices[i2]; Vector3 v3 = vertices[i3]; Vector3 normal = Vector3.Cross(v2 - v1, v3 - v1).normalized; normals[i1] += normal; normals[i2] += normal; normals[i3] += normal; } ``` 3. 归一化每个顶点的法线。对于每个顶点,将其法线向量除以其长度,以使其长度为1。 ```csharp for (int i = 0; i < normals.Length; i++) { normals[i].Normalize(); } ``` 4. 将计算出的法线设置为网格法线。使用 `Mesh.normals` 属性将计算出的法线向量数组分配给网格。 ```csharp mesh.normals = normals; ``` 完整的代码示例: ```csharp void CalculateNormals(Mesh mesh) { Vector3[] vertices = mesh.vertices; int[] triangles = mesh.triangles; Vector3[] normals = new Vector3[vertices.Length]; for (int i = 0; i < triangles.Length; i += 3) { int i1 = triangles[i]; int i2 = triangles[i + 1]; int i3 = triangles[i + 2]; Vector3 v1 = vertices[i1]; Vector3 v2 = vertices[i2]; Vector3 v3 = vertices[i3]; Vector3 normal = Vector3.Cross(v2 - v1, v3 - v1).normalized; normals[i1] += normal; normals[i2] += normal; normals[i3] += normal; } for (int i = 0; i < normals.Length; i++) { normals[i].Normalize(); } mesh.normals = normals; } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值