サーフェイスシェーダ ライティングの例
頂点シェーダとFragmentシェーダのプログラミング

サーフェイス シェーダとDX11テッセレーション

Suggest a change

Success!

Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable.

Close

Sumbission failed

For some reason your suggested change could not be submitted. Please try again in a few minutes. And thank you for taking the time to help us improve the quality of Unity Documentation.

Close

Cancel

Surface Shaders はDirectX 11 GPUテッセレーションを一部サポートします。要は:

  • テッセレーションはtessellate:FunctionName 修飾子により指定されます。この関数は三角形の辺とテッセレーション要素の内部を計算します。
  • テッセレーションが使用されている場合,“vertex modifier” (vertex:FunctionName) がテッセレーションの_後に_ ,ドメインシェーダの中の各々生成された頂点に対して,実行されます。通常はDisplacementマッピングです。
  • サーフェイスシェーダは任意でphong tessellation を計算し,モデルの表面を,Displacementマッピングなしに,均等にスムージングします。

現時点でのテッセレーションのサポートに関する制約:

  • 三角形ドメインのみサポートしていて,四角形や,等値線テッセレーション
  • テッセレーションが使用されている場合,シェーダは自動的にシェーダモデル5.0ターゲットにコンパイルされ,すなわちDX11でしか動作しません。

GPUテッセレーションなし,頂点モディファイアにDisplacementあり

はじめに,テッセレーション なし でDisplacementマッピングを行う,サーフェイスシェーダを見ていきます。頂点を法線に沿って,Displacementマップから与えられた量に基づき,移動します。

    Shader "Tessellation Sample" {
        Properties {
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp nolightmap
            #pragma target 5.0

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

上のシェーダは比較的標準的で,注目すべき点は:

  • disp 頂点モディファイアはDisplacmentマップをサンプリングし,頂点を法線に沿って移動します。
  • カスタムの“頂点データ入力” 構造(appdata )を,デフォルトのappdata_full の代わりに,使用します。これはまだ必要ありませんが,テッセレーションでできるかぎり小さい構造を使用するのに効果的です。
  • 頂点データは二番目のUV座標がないため,nolightmap ディレクティブを追加してライトマップを除外します。

次はシンプルなオブジェクトがこのシェーダを用いてどのように映るか示します:

固定量のテッセレーション

固定量のテッセレーション,例えばメッシュ全体で同じテッセレーションレベル,を追加してみます。このアプローチは,モデルの面が画面でもおおよそ同じ大きである場合に適切です。テッセレーションレベルを,カメラからの距離に基づいて,別のスクリプトでコードから変更出来ます。

    Shader "Tessellation Sample" {
        Properties {
            _Tess ("Tessellation", Range(1,32)) = 4
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessFixed nolightmap
            #pragma target 5.0

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _Tess;

            float4 tessFixed()
            {
                return _Tess;
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

シェーダのテッセレーション関数,tessFixed は4つのテッセレーション要素をひとつのfloat4の値として戻します:三角形の辺ごとに3つの要素,そして1つの要素で三角形の内部です。次に,マテリアルプロパティにセットする,定数値を単に戻します。

距離にもとづいたテッセレーション

カメラからの距離にもとづいてテッセレーションのレベルを変更することが出来ます。例えば,二つの距離の値を定義するとします:テッセレーションが最大になる距離(例えば10メートル),そしてテッセレーションが徐々に減少していくまでの距離(例えば20メートル)。

    Shader "Tessellation Sample" {
        Properties {
            _Tess ("Tessellation", Range(1,32)) = 4
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessDistance nolightmap
            #pragma target 5.0
            #include "Tessellation.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _Tess;

            float4 tessDistance (appdata v0, appdata v1, appdata v2) {
                float minDist = 10.0;
                float maxDist = 25.0;
                return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

ここでテッセレーション関数は3つの引数を持ちます:テッセレーション前の,3つの三角形の角の頂点データです。これは,頂点の位置に依存するとここで仮定した,テッセレーションレベル,を計算するのに必要です。内蔵のヘルパーファイルTessellation.cginc をインクルードしてUnityDistanceBasedTess 関数を呼び出し,そこで必要な処理が行われます。関数は頂点からカメラまでの距離を計算し,最終的なテッセレーション要素を算出します。

辺の長さにもとづいたテッセレーション

純粋に距離にもとづいたテッセレーションは三角形のサイズが比較的同じである場合にのみ有効です。上の画像で,小さな三角形をもつオブジェクトのテッセレーションが多すぎであり,大きな三角形をもつオブジェクトはテッセレーションが少なすぎることが判ります。

その代わりに,テッセレーションレベルは画面上の三角形の辺の長さにもとづいて計算することが出来ます,辺が長いほど,大きなテッセレショーン要素が適用されます。

    Shader "Tessellation Sample" {
        Properties {
            _EdgeLength ("Edge length", Range(2,50)) = 15
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _DispTex ("Disp Texture", 2D) = "gray" {}
            _NormalMap ("Normalmap", 2D) = "bump" {}
            _Displacement ("Displacement", Range(0, 1.0)) = 0.3
            _Color ("Color", color) = (1,1,1,0)
            _SpecColor ("Spec color", color) = (0.5,0.5,0.5,0.5)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessEdge nolightmap
            #pragma target 5.0
            #include "Tessellation.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _EdgeLength;

            float4 tessEdge (appdata v0, appdata v1, appdata v2)
            {
                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
            }

            sampler2D _DispTex;
            float _Displacement;

            void disp (inout appdata v)
            {
                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;
                v.vertex.xyz += v.normal * d;
            }

            struct Input {
                float2 uv_MainTex;
            };

            sampler2D _MainTex;
            sampler2D _NormalMap;
            fixed4 _Color;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Specular = 0.2;
                o.Gloss = 1.0;
                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));
            }
            ENDCG
        }
        FallBack "Diffuse"
    }

ここではまた,UnityEdgeLengthBasedTess 関数をTessellation.cginc ファイルから呼び出すだけで必要な処理を行わせます。

パフォーマンス上の理由から,UnityEdgeLengthBasedTessCull を呼ぶことが推奨され,Patch Frustum カリングが行われます。これによりシェーダがより高価になりますが,カメラレビュー外にあるメッシュ部分のGPU処理を軽減します。

Phongテッセレーション

Phong Tessellation は,結果となる表面がメッシュの法線にある程度沿うように,再分割(subdivide)された面の位置を修正します。ローポリのメッシュについてスムージングするのにかなり効果的な方法です。

Unityのサーフェイスシェーダは,tessphong:VariableName ディレクティブを使用することで,Phongテッセレーションを自動計算します。次にシェーダでの例を示します:

    Shader "Phong Tessellation" {
        Properties {
            _EdgeLength ("Edge length", Range(2,50)) = 5
            _Phong ("Phong Strengh", Range(0,1)) = 0.5
            _MainTex ("Base (RGB)", 2D) = "white" {}
            _Color ("Color", color) = (1,1,1,0)
        }
        SubShader {
            Tags { "RenderType"="Opaque" }
            LOD 300
            
            CGPROGRAM
            #pragma surface surf Lambert vertex:dispNone tessellate:tessEdge tessphong:_Phong nolightmap
            #include "Tessellation.cginc"

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

            void dispNone (inout appdata v) { }

            float _Phong;
            float _EdgeLength;

            float4 tessEdge (appdata v0, appdata v1, appdata v2)
            {
                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
            }

            struct Input {
                float2 uv_MainTex;
            };

            fixed4 _Color;
            sampler2D _MainTex;

            void surf (Input IN, inout SurfaceOutput o) {
                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }

            ENDCG
        }
        FallBack "Diffuse"
    }

次に通常のシェーダ(上列)とPhongテッセレーションを使用したシェーダ(下列)の比較です。Displacementマッピングなしでも表面がより丸くなることが判ります。 the surface becomes more round.

サーフェイスシェーダ ライティングの例
頂点シェーダとFragmentシェーダのプログラミング