これが必要となる場面はそんなに多くはないとは思いますが、元のモデルデータには手を加えず、一時的にメッシュを変更したいことがあったので作りました。
実現方法
Awake()
でメッシュをコピーして編集してセットし直し、 Destroy()
で削除するようにしました。
この方法だと当然プレイ中にのみ編集結果を確認できるということになります。
その分、コンポーネントを削除すれば元通りになるし、保存データも増えないので気軽に試せるのが良いです。
サンプル
処理内容
同じ座標にある頂点の法線の平均を算出し、法線を平均のものに設定し直すということを行いました。
ソースコード
Meshの法線を共通する頂点の平均の法線に書き替えるコンポーネント
#nullable enable
using System.Linq;
using UnityEngine;
/// <summary>
/// Meshの法線を共通する頂点の平均の法線に書き替えるコンポーネント
/// </summary>
public class AveragingNormalLines : MonoBehaviour
{
private Mesh? modifiedMesh;
private void Awake()
{
this.AverageNormals(this.GetComponent<MeshFilter>());
}
private void OnDestroy()
{
UnityEngine.Object.Destroy(this.modifiedMesh);
}
private void AverageNormals(MeshFilter meshFilter)
{
var baseMesh = meshFilter.mesh;
var normals = baseMesh.normals;
baseMesh.vertices
.Select((x, idx) => (Pos: x, Index: idx, Normal: baseMesh.normals[idx]))
.GroupBy(x => x.Pos)
.ToList()
.ForEach(x =>
{
var avg = x.Aggregate(Vector3.zero, (sum, next) => sum + next.Normal).normalized;
x.ToList().ForEach(y => normals[y.Index] = avg);
});
this.modifiedMesh = new Mesh();
this.modifiedMesh.SetVertices(baseMesh.vertices);
this.modifiedMesh.SetNormals(normals);
this.modifiedMesh.SetUVs(0, baseMesh.uv);
this.modifiedMesh.SetIndices(baseMesh.GetIndices(0), MeshTopology.Triangles, 0);
meshFilter.mesh = this.modifiedMesh;
}
}
実行結果
Cubeに上記コンポーネントを付与しました。
プレイ開始すると、法線が書き換わったメッシュが再設定され、ライティングが変化しました。
角丸四角のような法線になるので、角付近は隣に置いたSphereに近いライティングとなりました。
プレイ中にメッシュのインスペクタを開くと、そこでも変化を見ることができます。
コピーして何も編集していない場合 → 法線を書き換えた場合