Aqoole_Hateenaの技術日記

vulkan+raytraceで色々描いてます

Shaderの基本についての調査

概要

Unity初心者の私がShaderの構造や関数のエントリーポイントについて調査したのでまとめる。

Shader

ShaderLabやHLSLの用語説明などは、ページによくまとまっているので、以降は個人的に気になったC# ScriptからShaderへのデータの渡し方や関数のエントリーポイント、Passについて知っておくべき知識などをまとめる。
Unity のシェーダの基礎を勉強してみたのでやる気出してまとめてみた - 凹みTips
Unity ShaderLab ノート - Qiita

構造

Shader クラス - Unity マニュアルには以下のように記述がある。

シェーダーオブジェクト
シェーダーオブジェクトには以下が含まれています。

名前など、それ自体の情報
フォールバックシェーダーオブジェクト。Unity がこのシェーダーオブジェクトを使用できない場合に、フォールバックシェーダーオブジェクトが使用されます。
1 つまたは複数のサブシェーダー
また、共有シェーダーコードや、カスタムエディター を使用するかどうかなどの追加情報を定義することもできます。シェーダーオブジェクトの定義については、ShaderLab: シェーダーオブジェクトの定義 を参照してください。

SubShader
SubShader (サブシェーダー) は、シェーダーオブジェクトを、異なるハードウェア、レンダーパイプライン、ランタイム設定に対応する部分に分離することができます。

SubShader には以下が含まれています。

このサブシェーダーが対応できるハードウェア、レンダーパイプライン、ランタイム設定についての情報。
サブシェーダー タグ (サブシェーダーに関する情報を提供するキーと値のペア)。
1 つまたは複数のパス
また、すべてのパスに共通するレンダー状態などの追加情報を定義することもできます。サブシェーダーで定義できるすべての内容については、ShaderLab: サブシェーダーの定義 を参照してください。

Pass
Pass (パス) には以下が含まれています。

Pass タグ (パスに関する情報を提供するキーと値のペア)。
シェーダープログラムを実行する前にレンダー状態を更新するための指示
1 つまたは複数のシェーダバリアントにまとめられたシェーダープログラム
また、名前などの追加情報を定義することもできます。パスで定義できるすべての内容については、ShaderLab: パスの定義 を参照してください。

Shader概観

SubShader

公式ドキュメント
ShaderLab: SubShader の定義 - Unity マニュアル
Shaderを、ハードウェアごとやパイプラインごとに分けて記述するときに、SubShaderにわけて記述できる。
つまりTagやPass, そのほかBlendなどの情報をSubShaderごとに分けて書く。
実際のUniversal Render Pipeline/Litを見ると、Tag情報の中でShaderModelを4.5と2.0でわけてSubShaderで記述しているようだった。

Tagについて

分けて記載するSubShaderの目的や特性をつける。
記述例

SubShader{
    Tag {"RenderPipeline" = "UniversalRenderPipeline"
           "RenderType" = "Opaque"
     }
}

調べてみたところ、以下のような設定値があるらしい。
ShaderLab: タグを SubShader に割り当てる - Unity マニュアル

Tag
RenderPipeline UniversalRenderPipeline
HighDefinitionRenderPipeline
Queue Background
Geometry
AlphaTest
Transparent
Overlay
RenderType Opaque
Transparent
TransparentCutout
Background
Overlay
TreeOpaque
TreeTransparentCutout
TreeBillboard
Grass
GrassBillboard
ForceNoShadowCasting True
False
DisableBatching True
False
LODFading
IgnoreProjector True
False
PreviewType Sphere
Plane
Skybox
CanUseSpriteAtlas True
False

Pass

Passとはグラフィックスのシェーダープログラム内で特定の描画処理を定義するためのセクションまたはブロックのこと。
Universal Render Pipeline/Litを見てみると、物体の描画や影の描画ごとにPassが作成されていた。
各Passにvertex関数とfragment関数があるので、どのPassも条件があえば実行される。

SubShader{
    Tags{"RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" "UniversalMaterialType" = "Lit" "IgnoreProjector" = "True" "ShaderModel"="4.5"}
    Pass{
        Name "ForwardLit"
        Tags{"LightMode" = "UniversalForward"}
        HLSLPROGRAM
        #pragma vertex LitPassVertex
        #pragma fragment LitPassFragment
        ***
        ENDHLSL
    }

    Pass{
        Name "ShadowCaster"
        Tags{"LightMode" = "ShadowCaster"}
        HLSLPROGRAM
        #pragma vertex ShadowPassVertex
        #pragma fragment ShadowPassFragment
        ***
        ENDHLSL
    }

    Pass{
        Name "GBuffer"
        Tags{"LightMode" = "UniversalGBuffer"}
        ***
    }

    Pass{
        Name "DepthOnly"
        Tags{"LightMode" = "DepthOnly"}
        ***
    }

    Pass{
        Name "DepthNormals"
        Tags{"LightMode" = "DepthNormals"}
        ***
    }

    Pass{
        Name "Meta"  //for lightmap baking
        Tags{"LightMode" = "Meta"}
        ***
    }

    Pass{
        Name "Universal2D"
        Tags{ "LightMode" = "Universal2D" }
        ***

    }
}


またHLSLでは、vertex shaderとfragment shaderのメイン関数のエントリーポイントはそれぞれ

#pragma vertex ShadowPassVertex
#pragma fragment ShadowPassFragment

のようにして指定する。
メイン関数のエントリーポイントを間違えると、たとえばFowardLit Passだと

did not find shader kernel LitPassVertex to comile

が表示されてコンパイルに失敗する。
#pragmaディレクティブは下記に一覧が表示されている。
HLSL のプラグマディレクティブ - Unity マニュアル

その他のshaderの文法については以下に詳細が記載されている。
例えば、fragment shaderで色を出力する関数にはSV_Targetの指定が必要。
シェーダーセマンティクス - Unity マニュアル