知識0からのUnityShader勉強

知識0からのUnityShader勉強

UnityのShaderをメインとして、0から学んでいくブログです。

【UnityShader】Geometry Shader 基礎【2】 #75

前回の成果

Geometry Shaderの概要とMacでのグラフィックスAPIの変更を行った。

soramamenatan.hatenablog.com


今回やること

Geometry Shaderの中身を理解していきます。


ソースコード

Shader "Unlit/GeometrySimple" {
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _Color;

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct g2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            appdata vert (appdata v) {
                return v;
            }

            [maxvertexcount(3)]
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                [unroll]
                for (int i = 0; i < 3; i++) {
                    appdata v = input[i];
                    g2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    stream.Append(o);
                }
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = _Color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

前回の記事でも添付しましたが、もう一度ソースコードを添付しておきます。
色を変化させるだけの簡易なShaderになります。


Shaderの解説

VertexやFragmentの時には見られなかったものについて解説していきます。


#pragma geometry

#pragma geometry geom

コンパイラに今からGeometry Shader を使用する命令を出すためのキーワードになります。
VertexやFragmentでの、

#pragma vertex vert
#pragma fragment frag

と同じ意味となります。

もう少し掘り下げます。
#pragma geometryを使用すると、コンパイルターゲットを4.0に設定します。
コンパイルターゲット4.0には以下の特徴があります。

  • DX11 シェーダーモデル4.0
  • DX11 9.x (WinPhone)、OpenGL ES 2.0/3.0/3.1、Metal ではサポートされない
  • DX11 以降、OpenGL 3.2 以降、OpenGL ES 3.1 以降のAEP、Vulkan, PS4/XB1 コンソールでサポートされる
  • ジオメトリシェーダーと es3.0 ターゲットが持つすべてが含まれます。

docs.unity3d.com


Vertex

appdata vert (appdata v) {
    return v;
}

Geometry Shaderで処理するため、頂点シェーダーへの入力をそのまま返しています。


geom関数

[maxvertexcount(3)]
void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
    [unroll]
    for (int i = 0; i < 3; i++) {
        appdata v = input[i];
        g2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        o.uv = v.uv;
        stream.Append(o);
    }
    stream.RestartStrip();
}


maxvertexcount

出力する頂点の最大数を指定するものになります。
今回の場合、ただの三角メッシュですので頂点が最大3つとなり、3を指定しています。


Geometry Shaderでは頂点の増減が出来るのですが、上限が存在しています。
基本的に、出力する構造体の(1024 / 要素数)となります。
例えば、

struct g2f {
    float4 vertex : POSITION;
    float2 uv : TEXCOORD0;
};

こちらのような構造体の場合、floatが6つあるので要素数は6となります。
つまり、(1024 / 6 )となるので170が上限値となります。


void geom (triangle appdata input[3], inout TriangleStream stream)

void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
    // 省略
}


triangle appdata input[3]

第一引数は入力となります。
これは頂点シェーダーでの返り値の値が配列として渡されています
triangle appdata input[3]の場合、三角形で3頂点のinputが必要であることを引数として伝えています。


また、こちらは

// triangle   appdata  input [3]
PrimitiveType DataType Name [ NumElements ]

と定義されています。
意味は以下となります。

名称 意味
PrimitiveType プリミティブデータの順序を決定する
DataType [in]入力データ型で任意のHLSLデータ型を指定することが出来る
Name ASCll文字列の引数名
NumElements 配列のサイズ


そしてプリミティブの種類には以下があります。

プリミティブの種類 意味
point ポイントリスト
line ラインリスト、ラインストリップ
triangle トライアングルリスト、トライアングルストリップ
lineadj 隣接性のあるラインリスト、隣接性のあるラインストリップ
triangleadj 隣接性のあるトライアングルリスト、隣接性のあるトライアングルストリップ


inout TriangleStream stream

第二引数は出力となります。
こちらはGeometry Shaderで処理したものを次のステージへと渡しています。

こちらの定義は以下となります。

// inout TriangleStream<g2f>       stream
inout StreamOutputObject<DataType> Name;

意味は以下となります。

名称 意味
StreamOutputObject ストリーム出力オブジェクト宣言(SO)
DataType [in]入力データ型で任意のHLSLデータ型を指定することが出来る
Name ASCll文字列の引数名

そして、ストリーム出力オブジェクトの型には以下があります。

ストリーム出力オブジェクトの型 意味
PointStream ポイントプリミティブのシーケンス
LineStream ラインプリミティブのシーケンス
TriangleStream トライアングルプリミティブのシーケンス


[unroll]

[unroll]
for (int i = 0; i < 3; i++) {
    // 省略
}

コンパイル後のコードの記述の仕方を変えるものになります。
詳しくはこちらに記載してあります。

soramamenatan.hatenablog.com

ここのfor文で頂点シェーダーから貰った3つの頂点をそれぞれ射影変換しています。
そうすることにより、通常のレンダリングと同様にポリゴン位置を決めることが出来ます。


stream.Append(o)

stream.Append(o);

引数として渡した構造体を現在のストリームに出力しています。


stream.RestartStrip()

stream.RestartStrip();

出力するプリミティブが複数の場合に、それぞれのプリミティブの区切りを示す関数となります。
つまり、現在のストリームを終了させる関数です。


結果

_Colorの値に色が変われば成功です。

f:id:soramamenatan:20201010150110p:plain


ソースコードにコメントを付与

Shader "Unlit/GeometrySimple" {
    Properties {
        _Color ("Color", Color) = (1, 1, 1, 1)
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass {
            CGPROGRAM
            #pragma vertex vert
            // Geometry Shaderを使用することを伝える
            #pragma geometry geom
            #pragma fragment frag

            #include "UnityCG.cginc"

            fixed4 _Color;

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct g2f {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            //Geometry Shaderで処理するため、そのまま返す
            appdata vert (appdata v) {
                return v;
            }

            // 出力する頂点の最大数を指定
            [maxvertexcount(3)]
            /// <summary>
            /// Geometry Shader
            /// </summary>
            /// <param name="input">頂点シェーダーでの返り値の値</param>
            /// <param name="stream">ストリーム</param>
            void geom (triangle appdata input[3], inout TriangleStream<g2f> stream) {
                [unroll]
                for (int i = 0; i < 3; i++) {
                    // 頂点シェーダーの頂点をそれぞれ射影変換
                    appdata v = input[i];
                    g2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = v.uv;
                    // 現在のストリームに出力
                    stream.Append(o);
                }
                // 現在のストリームを終了
                stream.RestartStrip();
            }

            fixed4 frag (g2f i) : SV_Target {
                fixed4 col = _Color;
                return col;
            }
            ENDCG
        }
    }
    FallBack "Unlit/Color"
}

今回は以上となります。
ここまでご視聴ありがとうございました。

参考サイト様

edom18.hateblo.jp

www.techry.net