1

Unity3D のシェーダー ラボで 2 つのパスを使用して単純なアウトライン シェーダーを作成しました。問題は、アウトライン パスのコードにあります。

Pass {
    Name "OUTLINE"

    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha // Normal

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

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

    struct v2f {
        float4 position : POSITION;
        float3 normal : NORMAL;
    };

    uniform float _OutlineWidth;
    uniform float4 _OutlineColor;

    v2f vert(appdata v) {
        v.vertex.xyz *=  _OutlineWidth;

        v2f o;
        o.position = UnityObjectToClipPos(v.vertex);
        return o;
    }

    half4 frag(v2f i) : COLOR {
        return _OutlineColor;
    }

    ENDCG
}

_OutlineWidth_OutlineColorは、それぞれ Range 型と Color 型です。このシェーダーを、プログラムで作成された「テトロミノのような」メッシュに適用しました。結果はこれです(リンクをクリックしてください):

画像 1 画像 2

ご覧のとおり、輪郭が作成されますが、輪郭はオブジェクトの外縁に沿って均一な幅ではありません。面の 1 つに沿って輪郭が大きくなり、形状の中心から最も遠い面に沿って輪郭が大きくなります。また、凸でない形状では問題が拡大します。アウトラインがシェイプをまったく包含していない場合もあります。

画像3

これは、頂点の位置が形状の中心に相対的であるためであり、線v.vertex.xyz *= _OutlineWidthはこの位置に一定量を掛けるだけであることを理解しています (オブジェクトの中心からさらに離します)。アウトライン ピクセルが形状の中心とは無関係に計算され、オブジェクトの真のアウトラインと完全に一致するように、コードを変更するにはどうすればよいですか?

4

1 に答える 1

2

@Gnietschow が言ったように、これらの頂点の「滑らかな法線」を使用して、アウトラインを展開する方向を知る必要があります。

ハード法線とスムーズ法線。 出典:フロストソフト

これを行う 1 つの方法は、C# でソフト法線を計算し、それらを頂点データとして割り当てることです。この例では、チャネルuv2uv3チャネルを使用してスムーズな法線コンポーネントを保持します。

Mesh mesh = GetComponent<MeshFilter>().mesh;

Vector3[] meshVertices = mesh.vertices;
//map vertex positions to the ids of all vertices at that position
Dictionary<Vector3, List<int>> vertexMerge = new Dictionary<Vector3, List<int>>();
for(int i = 0; i < mesh.vertexCount; i++) {
    Vector3 vectorPosition = meshVertices[i];

    if(!vertexMerge.ContainsKey(vectorPosition)) {
        //if not already in our collection as a key, add it as a key
        vertexMerge.Add(vectorPosition, new List<int>());
    }

    //add the vertex id to our collection
    vertexMerge[vectorPosition].Add(i);
}

//map vertexIDs to the averaged normal
Vector3[] meshNormals = mesh.normals;
Vector3[] vertexAveragedNormals = new Vector3[mesh.vertexCount];

foreach (List<int> duplicatedVertices in vertexMerge.Values) {
    //calculate average normal
    Vector3 sumOfNormals = Vector3.zero;
    foreach (int vertexIndex in duplicatedVertices) {
        sumOfNormals += meshNormals[vertexIndex];
    }

    Vector3 averagedNormal = (sumOfNormals /= duplicatedVertices.Count).normalized; //average is sum divided by the number of summed elements

    //write the result to our output
    foreach (int vertexIndex in duplicatedVertices) {
        vertexAveragedNormals[vertexIndex] = averagedNormal;
    }
}


//write the result to mesh.
//x and y components shoved into uv3, z component shoved into uv4, with w component of 1.
Vector2[] vertexAveragedNormalsXY = new Vector2[mesh.vertexCount];
Vector2[] vertexAveragedNormalsZW = new Vector2[mesh.vertexCount];
for(int i = 0; i < mesh.vertexCount; i++) {
    Vector3 normal = vertexAveragedNormals[i];
    vertexAveragedNormalsXY[i] = new Vector2(normal.x, normal.y);
    vertexAveragedNormalsZW[i] = new Vector2(normal.z, 1);
}

mesh.uv3 = vertexAveragedNormalsXY;
mesh.uv4 = vertexAveragedNormalsZW;

ソース: Reddeyfish-

次に、TEXCOORD2 と TEXCOORD3 を使用して、 でスムーズな法線を再構築しvertます。これらを使用して頂点の位置を適切に移動し、頂点法線をあらゆる照明目的で同じハード法線に保ちます。

Pass {
    Name "OUTLINE"

    ZWrite Off
    Blend SrcAlpha OneMinusSrcAlpha // Normal

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

    struct appdata {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float4 texcoord2 : TEXCOORD2;
        float4 texcoord3 : TEXCOORD3;
    };

    struct v2f {
        float4 position : POSITION;
        float3 normal : NORMAL;
    };

    uniform float _OutlineWidth;
    uniform float4 _OutlineColor;

    v2f vert(appdata v) {
        // add the outline width in the direction of the shared normal
        float3 sharedNormal = float3(v.texcoord2.xy, v.texcoord3.x);

        v.vertex.xyz +=  _OutlineWidth * sharedNormal;  

        v2f o;
        o.position = UnityObjectToClipPos(v.vertex);
        return o;
    }

    half4 frag(v2f i) : COLOR {
        return _OutlineColor;
    }

    ENDCG
}
于 2018-12-20T21:06:37.903 に答える