如何在Unity里翻转网格法线

2022-1-22 17:10cgsd 78 0

将 .fbx 文件导入 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
效果如下图:

鲜花

握手

雷人

路过

鸡蛋

最新评论

QQ|手机版|小黑屋|九艺游戏动画论坛 ( 津ICP备2022000452号-1 )

GMT+8, 2024-4-19 19:26 , Processed in 0.099407 second(s), 18 queries .

Powered by Discuz! X3.4  © 2001-2017 Discuz Team.