LoginSignup
24
18

More than 5 years have passed since last update.

[Unity] アウトライン Outline - 詳細篇 モデル拡大法

Last updated at Posted at 2017-04-30

モデル拡大法

概念篇で書いた方法:
モデルの点を法線の方向に沿って移動し、マテリアルは単色にして、背面描画すること。

これをShaderで実現するには:(Outline部分Only)

Pass{
    //背面描画する
    Cull Front

    CGPROGRAM
    #include "UnityCG.cginc"            
    #pragma vertex vert
    #pragma fragment frag

    float4 _OutlineColor;
    float _Outline;

    struct Input
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
    };

    struct Output
    {
        float4 pos : SV_POSITION;
        fixed4 color : TEXCOORD0;
    };

    fixed4 frag(Output o) : SV_Target
    {
        return o.color;
    }

    Output vert(Input i)
    {
        Output o;
        //モデルの点を法線の方向に沿って移動する
        i.vertex.xyz += normalize(i.normal) * _Outline;
        o.pos = UnityObjectToClipPos(i.vertex);
        //単色にする
        o.color = _OutlineColor;
        return o;
    }

    ENDCG
}

以上のShaderを使った物:
image

問題点と解決方法

概念篇でも書いたようにキューブの場合は問題がある。その理由は:
image
赤い頂点は複数の三角と関わり、それぞれの法線は違う。だから、法線の方向に沿って移動したら、三角たちが離れてしまう。では、同じ法線にすればいい:
image
こうして、全ての頂点の法線を変えて、移動する時は問題が起こらない。

 public static void MeshNormalAverage(Mesh mesh)
    {
        Dictionary<Vector3, List<int>> map = new Dictionary<Vector3, List<int>>();

        #region build the map of vertex and triangles' relation
        for (int v = 0; v < mesh.vertexCount; ++v)
        {
            if (!map.ContainsKey(mesh.vertices[v]))
            {
                map.Add(mesh.vertices[v], new List<int>());
            }

            map[mesh.vertices[v]].Add(v);
        }
        #endregion

        Vector3[] normals = mesh.normals;
        Vector3 normal;

        #region the same vertex use the same normal(average)
        foreach (var p in map)
        {
            normal = Vector3.zero;

            foreach (var n in p.Value)
            {
                normal += mesh.normals[n];
            }

            normal /= p.Value.Count;

            foreach (var n in p.Value)
            {
                normals[n] = normal;
            }
        }
        #endregion

        mesh.normals = normals;
    }

でも法線を変えるとライティングも変わる。だから、Outlineのモデルだけ法線を変え、元のモデルは変わらない。Outlineのためにモデルをクローンする:
Copy components at runtime
クローンするのはMeshRendererとMeshFilter、或いはSkinnedMeshRenderer。Meshをクローンする時ちょっと注意:

Mesh tmpMesh = (Mesh)Instantiate(GetComponent<MeshFilter>().sharedMesh);
outlineObj.GetComponent<MeshFilter>().sharedMesh = tmpMesh;

成果

image
基本は問題ないが、深度にはやはりまだ問題がある:
image

実際のキャラクターも試そう:
image
左はUnityエディターのOutline、右は自分のもの。左の方が正しいと思うが、右の方も悪くない。

深度に関してはまだ研究する必要がある。とりあえず、ここまでの結果:
Github:Unity Outline

24
18
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
24
18