14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

はじめての記事投稿

【Unity】モデルインポート時にアウトライン用の法線を生成する

Posted at

はじめに

こちらの動画を見て欲しいなと思ったので自分で作ってみました。

動画の方法では法線をUnity側で再計算してそれを格納していますが、
コードが長くなるので、モデルの法線を元にこちらで計算したものを格納しています。
なのでモデル側で法線を調整していた場合、モデルのImport設定で結果が変わります。

import.png

スクリプト

普通に書いたら遅かったので、Job Systemを使って高速化しています。
NativeHashMapを使用しているのでPackage ManagerでCollectionsを入れてください
スクリーンショット 2023-06-14 210700.png
内容についてはコメントを読めば分かると思います。
今回は計算した法線をUV1に入れてますが、お好みの場所に保存してください。

using Unity.Collections;
using Unity.Jobs;
using Unity.Burst;
using UnityEngine;
using UnityEditor;

public class NormalSmoothProcessor : AssetPostprocessor
{
    private void OnPostprocessModel(GameObject go)
    {
        //meshを取得
        var meshFilters = go.GetComponentsInChildren<MeshFilter>();
        var skinnedmeshs = go.GetComponentsInChildren<SkinnedMeshRenderer>();
        var meshs = new Mesh[meshFilters.Length + skinnedmeshs.Length];
        for (int i = 0; i < meshFilters.Length; i++)
            meshs[i] = meshFilters[i].sharedMesh;
        for (int i = 0; i < skinnedmeshs.Length; i++)
            meshs[i + meshFilters.Length] = skinnedmeshs[i].sharedMesh;

        //jobの入出力に使うデータを予め全メッシュ分用意
        var vertices = new NativeArray<Vector3>[meshs.Length];
        var normals = new NativeArray<Vector3>[meshs.Length];
        var smoothNormals = new NativeArray<Vector3>[meshs.Length];
        var handles = new JobHandle[meshs.Length];

        //全メッシュ分のjobを実行
        for (int i = 0; i < meshs.Length; i++)
        {
            //必要なデータを格納
            vertices[i] = new NativeArray<Vector3>(meshs[i].vertices, Allocator.TempJob);
            normals[i] = new NativeArray<Vector3>(meshs[i].normals, Allocator.TempJob);
            smoothNormals[i] = new NativeArray<Vector3>(meshs[i].vertexCount, Allocator.TempJob);

            var Job = new Job {
                vertices = vertices[i],
                normals = normals[i],
                smoothNormal = smoothNormals[i],
            };

            //全てのメッシュのjobを実行してから待つ
            handles[i] = Job.Schedule();
        }
        for (int i = 0; i < meshs.Length; i++)
        {
            handles[i].Complete();
            //お好みの場所に保存
            meshs[i].SetUVs(1, smoothNormals[i]);

            vertices[i].Dispose();
            normals[i].Dispose();
            smoothNormals[i].Dispose();
        }
    }
    [BurstCompile]
    struct Job : IJob
    {
        [ReadOnly] public NativeArray<Vector3> vertices;
        [ReadOnly] public NativeArray<Vector3> normals;
        [WriteOnly] public NativeArray<Vector3> smoothNormal;

        public void Execute()
        {
            var normalsum = new NativeHashMap<Vector3, Vector3>(vertices.Length,Allocator.TempJob);

            //同じposの頂点の法線を合算
            for (int i = 0; i < vertices.Length; i++)
                if (!normalsum.TryAdd(vertices[i], normals[i]))
                    normalsum[vertices[i]] += normals[i];

            //normalizeした法線を配列に格納
            for (int i = 0; i < vertices.Length; i++)
                smoothNormal[i] = normalsum[vertices[i]].normalized;

            normalsum.Dispose();
        }
    }
}

後はこれをコピペしてプロジェクト内に置いとけばimport時に勝手に実行されます。

アウトライン用ShaderGraph

一応サンプル乗せておきます。
UV1から取り出した法線を普通にその方向に頂点を押し出してるだけですね。
スクリーンショット 2023-06-14 212153.png

使ってみる

image.png
ちゃんと斜めに押し出されてます。
image.png
使用したモデル:こここや - ソラハ【オリジナル3Dモデル】
髪の毛とかも良くなってそうですね。

おわりに

今回初めて記事を書いてみたのと、JobSystemを始めて触ったので、
何かおかしいところがあっても温かい目で見てください。

14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?