元ネタ様
コードはこちらのコードにuvとジオメトリシェーダを付与し、こちらの解説をコメントとして付与したものです
Shader "Unlit/SimpleTessellation" { Properties{ _TessFactor("Tess Factor",Vector) = (2,2,2,2) _MainTex("MainTex",2D) = "white"{} } SubShader{ Pass{ Cull Off CGPROGRAM #pragma vertex VS #pragma geometry geom #pragma fragment FS #pragma hull HS #pragma domain DS #define INPUT_PATCH_SIZE 3 #define OUTPUT_PATCH_SIZE 3 uniform vector _TessFactor; sampler2D _MainTex; struct appdata { float4 vertex:POSITION; float2 uv:TEXCOORD0; }; struct v2h { float4 pos:POS; float2 uv:TEXCOORD0; }; struct h2d_main { float3 pos:POS; float2 uv:TEXCOORD0; }; struct h2d_const { float tess_factor[3] : SV_TessFactor; float InsideTessFactor : SV_InsideTessFactor;//ドメインに指定する形状によってはエラーになる }; struct d2g { float4 lpos:SV_Position; float2 uv:TEXCOORD0; }; struct g2f { float4 pos:SV_POSITION; float2 uv:TEXCOORD0; }; struct f_input { float4 vertex:SV_Position; float4 color:COLOR0; }; v2h VS(appdata i) { v2h o = (v2h)0; o.pos = i.vertex; o.uv = i.uv; return o; } /* テッセレーションの流れ 頂点シェーダ=> ハルシェーダ・ステージ=> メインハルシェーダ、パッチ定数関数の2つが並列で実行される メインハルシェーダ-コントロールポイントごとに1回コール -頂点の分割度合いを操作 パッチ定数関数-パッチごとに1回コールする制御点をセット テッセレーションステージ=> 自動実行,実際に頂点が分割される ドメインシェーダステージ=> *用語説明 パッチーポリゴン分割処理を行う際に使用するコントロールポイントの集合 コントロールポイント・制御点ー頂点分割で使う制御点 ドメインー分割に利用するプリミティブ形状 テッセレーション係数ーパッチごとに指定する頂点の分割係数 */ //パッチ定数関数-頂点の分割度合いを操作 //パッチごとに、どの程度頂点を分割するかを決める係数を構造体に入れる //この構造体データは次のTessellatorに渡される。 //h2d_constはテッセレーションステージを介して、ドメインシェーダに渡される h2d_const HSConst(InputPatch<v2h, INPUT_PATCH_SIZE> i) { h2d_const o = (h2d_const)0; o.tess_factor[0] = _TessFactor.x; o.tess_factor[1] = _TessFactor.y; o.tess_factor[2] = _TessFactor.z; o.InsideTessFactor = _TessFactor.w; return o; } /* メインハルシェーダ-制御点を入力 h2d_mainはドメインシェーダに渡される //INPUT_PATCH_SIZE=3:一つのパッチが持つ制御点の数 */ [domain("tri")]//(tri/quad/isoline)から選択 [partitioning("integer")]//分割方法。(integer/fractional_eve,fractional_odd/pow2)から選択。 [outputtopology("triangle_cw")]//出力された頂点が形成するトポロジー。(point/line/triangle_cw/triangle_ccw)から選択。 [outputcontrolpoints(OUTPUT_PATCH_SIZE)]//出力するパッチのサイズ(配列のサイズ的な意味の) [patchconstantfunc("HSConst")]//パッチ定数関数の関数名 h2d_main HS(InputPatch<v2h, INPUT_PATCH_SIZE> i, uint id:SV_OutputControlPointID) { h2d_main o = (h2d_main)0; o.pos = i[id].pos;//頂点シェーダから渡された値をそのまま入力している o.uv = i[id].uv; return o; } /* Tessellation Stageの出力をもとに、分割後の頂点を処理。 Geometry-Shaderを呼び出さない場合はここでWVP変換をする必要がある。 h2d_const hs_const_data パッチ固定関数の出力(頂点の分割度合い)を参照 const OutputPatch<h2d_main, OUTPUT_PATCH_SIZE> ハルシェーダの出力(制御点) float3 bary:SV_DomainLocation 分割後の頂点の位置を求めるためのパラメーター (分割後の頂点がそのまま出力されるわけではないので、計算する必要がある) ドメインにtri(三角形)を指定している場合、SV_DomainLocationは三角形の重心座標を意味する パラメータとなる。 ドメインが異なると、SV_DomainLocationが意味する値と型が変わる */ [domain("tri")] d2g DS(h2d_const hs_const_data, const OutputPatch<h2d_main, OUTPUT_PATCH_SIZE> i, float3 bary:SV_DomainLocation) { d2g o = (d2g)0; float3 pos = i[0].pos * bary.x + i[1].pos * bary.y + i[2].pos * bary.z; //o.pos = UnityObjectToClipPos(float4(pos, 1)); o.lpos = float4(pos,1); o.uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z; return o; } [maxvertexcount(3)] void geom(triangle d2g vg[3], inout LineStream<g2f>outStream){ [unroll] for (int i = 0; i < 3; i++){ d2g v = vg[i]; g2f o = (g2f)0; o.pos = UnityObjectToClipPos(v.lpos); o.uv = v.uv; outStream.Append(o); } outStream.RestartStrip(); } float4 FS(f_input i) : SV_Target { return float4(1, 0, 0, 1); } ENDCG } } }
Shader "Sample/Tesslation"{ Properties{ _Tess("Tess",Range(1,64)) = 10 _MainTex("MainTex",2D) = "white"{} } SubShader{ Pass{ Cull Off CGPROGRAM #pragma vertex vert #pragma geometry geom #pragma fragment frag #pragma hull HS #pragma domain DS #pragma target 5.0 #define INPUT_PATCH_SIZE 3 #define OUTPUT_PATCH_SIZE 3 int _Tess; sampler2D _MainTex; struct appdata { float4 vertex:POSITION; float2 uv:TEXCOORD0; }; struct v2h { float4 pos:POS; float2 uv:TEXCOORD0; }; struct h2d_main { float3 pos:POS; float2 uv:TEXCOORD0; }; struct h2d_const { float tess_factor[3] : SV_TessFactor; float InsideTessFactor : SV_InsideTessFactor; }; struct d2g { float4 lpos:SV_Position; float2 uv:TEXCOORD0; }; struct g2f { float4 pos:SV_POSITION; float2 uv:TEXCOORD0; }; v2h vert(appdata i) { v2h o = (v2h)0; o.pos = i.vertex; o.uv = i.uv; return o; } h2d_const HSConst(InputPatch<v2h, INPUT_PATCH_SIZE> i) { h2d_const o = (h2d_const)0; o.tess_factor[0] = 1; o.tess_factor[1] = 1; o.tess_factor[2] = 1; o.InsideTessFactor = _Tess; return o; } [domain("tri")] [partitioning("integer")] [outputtopology("triangle_cw")] [outputcontrolpoints(OUTPUT_PATCH_SIZE)] [patchconstantfunc("HSConst")] h2d_main HS(InputPatch<v2h, INPUT_PATCH_SIZE> i, uint id:SV_OutputControlPointID) { h2d_main o = (h2d_main)0; o.pos = i[id].pos; o.uv = i[id].uv; return o; } [domain("tri")] d2g DS(h2d_const hs_const_data, const OutputPatch<h2d_main, OUTPUT_PATCH_SIZE> i, float3 bary:SV_DomainLocation) { d2g o = (d2g)0; float3 pos = i[0].pos * bary.x + i[1].pos * bary.y + i[2].pos * bary.z; o.lpos = float4(pos,1); o.uv = i[0].uv * bary.x + i[1].uv * bary.y + i[2].uv * bary.z; return o; } [maxvertexcount(3)] void geom(triangle d2g vg[3], inout LineStreamoutStream){ [unroll] for (int i = 0; i < 3; i++){ d2g v = vg[i]; g2f o = (g2f)0; o.pos = UnityObjectToClipPos(v.lpos); o.uv = v.uv; outStream.Append(o); } outStream.RestartStrip(); } float4 frag(g2f i) : SV_Target { return float4(1, 0, 0, 1); } ENDCG } } }