[Unity]硬表面模型描边断裂问题解决过程记录

在中使用单独一个Pass渲染轮廓线是非常常见的做法 , 其原理是在该Pass的顶点着色器中将模型顶点加上沿法线方向的偏移是原本的模型扩大一圈并剔除正向面 , 从而实现轮廓线效果 。
但是使用该方法有一个要求就是模型的法线必须连续 , 也就是模型必须光滑表面 , 如果是硬表面的模型 , 由于转折处法线不连贯 , 会导致沿法线扩大的轮廓线模型断裂 , 如下图:
原因是转折处法线不连贯:
解决方案有二 , 但思路是一样的 , 就是将一个点光滑处理后的法线值存入该点的顶点色的RGB通道中 , A通道可以用来控制轮廓线的粗细 。需要注意的是 , 存入顶点色的法线必须是切线空间下的坐标 , 如果是模型空间下的坐标的话 , 一旦模型需要做动画 , 模型的轮廓线就会计算错误 。一开始想要在中通过脚本实现以上思路 , 结果发现Max脚本只能设置“控制点”的颜色 , 而不能分开设置同一顶点但是不同“顶点”的颜色(不知道怎样描述) , 反正我是看了半天Max文档也没找到方法 , 如果有方法的话希望大佬能够在评论区告知 , 万分感谢!下面说一下我实验成功的两种解决方案 。
(本人程序菜鸡 , 只会点基础 , 代码烂的一批 , 轻喷)
更新:才知道还有个“资产后处理()”这么个东西 , 感觉不错 , 可以看看这位大佬的文章:【Job/Toon】自动生成硬表面模型

[Unity]硬表面模型描边断裂问题解决过程记录

文章插图
方案一:在Unity中实现以上思路 , 并将处理好的模型存为新的.asset文件(一开始我还以为获取模型的时候使用().然后编辑该mesh就能就改资源文件 , 结果发现理解错了 , 这样并不行 , 而且Unity也不能保存Mesh为Fbx , 只能存为.asset文件 ) 。具体实现为 , Unity中新建r脚本 , 脚本内容如下:
【[Unity]硬表面模型描边断裂问题解决过程记录】using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEditor;public class SetNormalsInVertColor : MonoBehaviour{public string NewMeshPath = "Assets/";void Awake(){//获取MeshMesh mesh = new Mesh();if (GetComponent()){mesh = GetComponent().sharedMesh;}if (GetComponent()){mesh = GetComponent().sharedMesh;}Debug.Log(mesh.name);//声明一个Vector3数组 , 长度与mesh.normals一样 , 用于存放//与mesh.vertices中顶点一一对应的光滑处理后的法线值Vector3[] meshNormals = new Vector3[mesh.normals.Length];//开始一个循环 , 循环的次数 = mesh.normals.Length = mesh.vertices.Length = meshNormals.Lengthfor (int i = 0; i < meshNormals.Length; i++){//定义一个零值法线Vector3 Normal = new Vector3(0,0,0);//遍历mesh.vertices数组 , 如果遍历到的值与当前序号顶点值相同 , 则将其对应的法线与Normal相加for (int j = 0; j < meshNormals.Length; j++){if (mesh.vertices[j] == mesh.vertices[i]){Normal += mesh.normals[j];}}//归一化Normal并将meshNormals数列对应位置赋值为Normal,到此序号为i的顶点的对应法线光滑处理完成//此时求得的法线为模型空间下的法线Normal.Normalize();meshNormals[i] = Normal;}//构建模型空间→切线空间的转换矩阵ArrayList OtoTMatrixs = new ArrayList();for (int i = 0; i < mesh.normals.Length; i++){Vector3[] OtoTMatrix = new Vector3[3];OtoTMatrix[0] = new Vector3(mesh.tangents[i].x, mesh.tangents[i].y, mesh.tangents[i].z);OtoTMatrix[1] = Vector3.Cross(mesh.normals[i], OtoTMatrix[0]);OtoTMatrix[1] = new Vector3(OtoTMatrix[1].x * mesh.tangents[i].w, OtoTMatrix[1].y * mesh.tangents[i].w, OtoTMatrix[1].z * mesh.tangents[i].w);OtoTMatrix[2] = mesh.normals[i];OtoTMatrixs.Add(OtoTMatrix);}//将meshNormals数组中的法线值一一与矩阵相乘 , 求得切线空间下的法线值for (int i = 0; i