イラスト、モデリング、Unity、VR関連

unityとかblenderとかvr関連の作業メモ

shader テッセレーションサンプルコード

元ネタ様

コードはこちらのコードに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 LineStream 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 frag(g2f i) : SV_Target {
        return float4(1, 0, 0, 1);
			}
      ENDCG
    }
  }
}