Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
16
Help us understand the problem. What are the problem?

More than 3 years have passed since last update.

posted at

updated at

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

モデル拡大法

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

これを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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
16
Help us understand the problem. What are the problem?