RhinoFreak 发表于 2022-1-12 08:35

将镜像模型导入Unity后法线翻转的问题

封面PID:#942524671
一:问题概述

一般在3dmax行业内通常老手会少用“镜像”这个操作,因为3dmax本身的镜像是从发布到现在的3dmax 2021一直存在BUG的。(Blender和C4D似乎不存在这个问题)。
模型镜像后,法线也跟着反转了(3dmax本身默认开启了双面显示的功能,所以很难发现这个问题,除非给模型勾选了“背面消隐”),就像下图这样 ↓ (已开启背面消隐)



可以很明显地看到Utah Teapot的法线是反的,能够看到内部

正因为把镜像的模型导入进Unity3d或UE里总会出现各种各样的法线问题,所以老手也会用一些方案来解决这个问题,包括

[*]Reset XForm(外网也推荐的方案)
[*]把已经镜像的模型添加法线修改器翻转再塌陷(但这样在3dmax里就很难直观地观看模型了,属于是模型导出之前的最后一步放弃治疗的操作hhh)
[*]塌陷成可编辑网格而不是可编辑多边形(蜜汁操作?)
[*]Unity中使用微调过的Shader,例如加一句Cull Off/Front/Back等(不建议瞎使用,浪费性能)



4种解决方案

二:脚本解决

我测试了似乎高版本的Unity已经解决了这个镜像模型导入后的问题?不需要以上的解决方案导进去显示也是正常的(我使用的untiy 2020.2.4 URP管线)。
但无奈的是楼底下程序部门还一直使用旧版本的Unity 5.2,让他们换SRP得重构之前写的代码,我嘴皮子都磨烂了还是不肯换2333,换用SRP这个东西估计一时半会儿是没法在新项目上落实了。(悲)
基于这个eggpain的问题我写了一个unity脚本供参考,似乎也可以解决这个问题,hhh,不多说了代码奉上。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FlipNormal : MonoBehaviour
{
    public GameObject InvertObject;
    //选择一个要反转的物体

    void Awake()
    {
      InvertSphere();//瞎取一个函数名
    }

    void InvertSphere()
    {
      //==============================================
      Vector3[] normals = InvertObject.GetComponent<MeshFilter>().sharedMesh.normals;
      //这一段把模型的每一个法线反转了
      for(int i = 0; i < normals.Length; i++)
      {
            normals = -normals;
      }
      InvertObject.GetComponent<MeshFilter>().sharedMesh.normals = normals;
      //更新反转后的法线
      //==============================================

      //仅仅翻转法线还不够,必须根据三角形遍历顺序把1-2-3改变成3-2-1,这样渲染管线应用阶段才会读翻转后的法线(在obj里表现为同时反转v和vn);
      //三角形换序的同时别忘了submesh也要继承,不然submesh对应的子材质就会变成仅有一个(Cuz Every material has a separate tri list)
      Material[] materials = InvertObject.GetComponent<Renderer>().materials;
      int SubmeshNum =materials.Length;//统计子材质数量
      for (int j = 0; j < SubmeshNum; j++)
      {
            int[] triangles = InvertObject.GetComponent<MeshFilter>().sharedMesh.GetTriangles(j);//按每个submesh遍历三角形
            for (int index = 0; index < triangles.Length; index += 3)//submesh的每个三角形换序
            {
                int intermediate = triangles;
                triangles = triangles;
                triangles = intermediate;
            }
            InvertObject.GetComponent<MeshFilter>().sharedMesh.SetTriangles(triangles, j);//更新反转后的submesh三角形
      }
      //==============================================

    }
}

这个脚本直接拖到模型上面就能用了,并支持包含子材质的模型,不用一个个子材质换双面shader也算是优势之一?



三:参考资料

1:Unity法线翻转(flip normal)
2:SubMeshDescriptor
页: [1]
查看完整版本: 将镜像模型导入Unity后法线翻转的问题