【UnityShader】Geometry Shader 基礎【2】 #75
前回の成果
Geometry Shaderの概要とMacでのグラフィックスAPIの変更を行った。
今回やること
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 ターゲットが持つすべてが含まれます。
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++) { // 省略 }
コンパイル後のコードの記述の仕方を変えるものになります。
詳しくはこちらに記載してあります。
ここのfor文で頂点シェーダーから貰った3つの頂点をそれぞれ射影変換しています。
そうすることにより、通常のレンダリングと同様にポリゴン位置を決めることが出来ます。
stream.Append(o)
stream.Append(o);
引数として渡した構造体を現在のストリームに出力しています。
stream.RestartStrip()
stream.RestartStrip();
出力するプリミティブが複数の場合に、それぞれのプリミティブの区切りを示す関数となります。
つまり、現在のストリームを終了させる関数です。
結果
_Colorの値に色が変われば成功です。
ソースコードにコメントを付与
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" }
今回は以上となります。
ここまでご視聴ありがとうございました。