Unity の HLSL シェーダー：シンプルなカラーシェーダーを実装する

# Unity の HLSL シェーダー：シンプルなカラーシェーダーを実装する

こんにちは、@studio_meowtoon です。今回は Unity でシンプルなカラーシェーダーを実装する方法を紹介します。
![hlsl_in_unity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/51eb64b3-01db-a6bd-7261-dae4e12b8bf9.png)

## 実現すること

Unity で HLSL 言語を使用したシンプルなカラーシェーダーを作成します。

### HLSL とは？

:::note info
Unity では、シェーダープログラムを記述するために HLSL というプログラミング言語を使用します。HLSL (High Level Shading Language) は元々はマイクロソフトによって開発された、Direct3D (DirectX) で使われるプログラマブルシェーダーのためのプロプライエタリなシェーディング言語です。
:::

## Unity のシェーダーを実装する

Unity における HLSL シェーダーの構造は、以下のようになっています。

```hlsl
// シェーダー名
Shader "Germio/Color"
{
    // プロパティセクション
    //   マテリアルの外部から調整可能なパラメータを定義します。
    Properties
    {
    }

    // サブシェーダーセクション
    //   ここにシェーダーの実際の描画ロジックを記述します。
    SubShader
    {
    }

    // フォールバック
    //   このシェーダーが使用できない場合、
    //   デフォルトの "Standard" シェーダーを使用します。
    Fallback "Standard"
}
```

次のシェーダーは、Unity のシェーダープログラムで書かれたシンプルなカラーシェーダーです。

```hlsl:Assets\Shaders\Germio\Color.shader
// シェーダー名
Shader "Germio/Color"
{
    // プロパティセクション
    Properties
    {
        // カスタムプロパティ "_Color" の宣言
        _Color("Color", Color) = (1, 1, 1, 1)
    }

    // サブシェーダーセクション
    SubShader
    {
        // タグの設定: レンダリングキューを透明に設定
        Tags { "Queue" = "Transparent" }
        
        // ブレンドモードの設定: アルファブレンド
        Blend SrcAlpha OneMinusSrcAlpha
        
        // シェーダーのパスを定義
        Pass
        {
            // パスの名前
            Name "COLOR"
            
            // プログラマブルなシェーダーの開始宣言
            CGPROGRAM
            
            // 頂点シェーダーの指定
            #pragma vertex vert
            
            // フラグメントシェーダーの指定
            #pragma fragment frag

            // 頂点情報を格納する構造体の宣言
            struct appdata
            {
                // 頂点の座標情報
                float4 vertex : POSITION;
            };

            // 頂点シェーダーからフラグメントシェーダーにデータを渡す構造体の宣言
            struct v2f
            {
                // 頂点のスクリーン座標
                float4 pos : SV_POSITION;
            };

            // 頂点シェーダー関数の宣言
            v2f vert(appdata v)
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex); // 頂点位置をクリップ座標に変換
                return o;
            }

            // プロパティで定義されたカラー情報を格納する変数
            float4 _Color;

            // フラグメントシェーダー関数の宣言
            fixed4 frag(v2f i) : SV_Target
            {
                float4 final_color = _Color; // シェーダーの最終出力カラーを定義
                final_color.a = _Color.a; // アルファ値をカスタムプロパティのアルファ値に設定
                return final_color; // 最終出力カラーを返す
            }
            
            // プログラマブルなシェーダーの終了宣言
            ENDCG
        }
    }

    // フォールバック
    Fallback "Standard"
}
```

### シェーダーで描画する

Unity 標準の Standard シェーダーで描画した立方体の3Dモデルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/1d931991-e7af-eda4-4b53-6b8dca53099c.png)

こちらは今回実装したシンプルなカラーシェーダーで描画した立方体の3Dモデルです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/58e435a5-8006-5ffe-fcda-0f92c3f0584e.png)

:::note alert
シェーダーの実装通りに全体が白で描画されています。この段階で重要なことは、シェーダーの基本的な記述方法を学ぶことです。
:::

### 各セクションの説明

以下、各部分について詳しく説明します。

```hlsl
Shader "Germio/Color"
{
    // プロパティセクション
    Properties
    {
        // カスタムプロパティ "_Color" の宣言
        _Color("Color", Color) = (1, 1, 1, 1)
    }
```

この部分では、シェーダーのプロパティを定義しています。このシェーダーには "_Color" という名前のプロパティがあり、デフォルトで白色 (1, 1, 1, 1) が設定されています。このプロパティは後でフラグメントシェーダー内で使用されます。

```hlsl
// サブシェーダーセクション
SubShader
{
    // タグの設定: レンダリングキューを透明に設定
    Tags { "Queue" = "Transparent" }
    
    // ブレンドモードの設定: アルファブレンド
    Blend SrcAlpha OneMinusSrcAlpha
```

SubShader セクションでは、このシェーダーのサブシェーダーを定義しています。"Queue" タグを設定し、レンダリングの優先順位を "Transparent" に設定しています。また、ブレンドモードも設定しており、SrcAlpha と OneMinusSrcAlpha を使用して透明なオブジェクトの描画に適しています。

```hlsl
// シェーダーのパスを定義
Pass
{
    // パスの名前
    Name "COLOR"
    
    // プログラマブルなシェーダーの開始宣言
    CGPROGRAM
    
    // 頂点シェーダーの指定
    #pragma vertex vert
    
    // フラグメントシェーダーの指定
    #pragma fragment frag
```

Pass セクションでは、シェーダーのパスを定義しています。このパスの名前は "COLOR" で、CGPROGRAM ブロック内で定義された頂点シェーダーとフラグメントシェーダーが使用されます。

```hlsl
// 頂点情報を格納する構造体の宣言
struct appdata
{
    // 頂点の座標情報
    float4 vertex : POSITION;
};
```

appdata 構造体は頂点情報を格納するために使用されます。ここでは、頂点の位置情報を float4 型の変数として定義しています。

```hlsl
// 頂点シェーダーからフラグメントシェーダーにデータを渡す構造体の宣言
struct v2f
{
    // 頂点のスクリーン座標
    float4 pos : SV_POSITION;
};

```

v2f 構造体は頂点シェーダーからフラグメントシェーダーにデータを渡すために使用されます。ここでは、頂点の座標情報を float4 型の変数として定義しています。

```hlsl
// 頂点シェーダー関数の宣言
v2f vert(appdata v)
{
    v2f o;
    o.pos = UnityObjectToClipPos(v.vertex); // 頂点位置をクリップ座標に変換
    return o;
}
```

vert 関数は頂点シェーダーで、appdata 構造体から v2f 構造体にデータを変換します。UnityObjectToClipPos 関数を使用して頂点位置をクリップ空間に変換し、v2f 構造体に格納しています。

```hlsl
// プロパティで定義されたカラー情報を格納する変数
float4 _Color;
```

```hlsl
// フラグメントシェーダー関数の宣言
fixed4 frag(v2f i) : SV_Target
{
    float4 final_color = _Color; // シェーダーの最終出力カラーを定義
    final_color.a = _Color.a; // アルファ値をカスタムプロパティのアルファ値に設定
    return final_color; // 最終出力カラーを返す
}
```

frag 関数はフラグメントシェーダーで、各フラグメントの最終的な色を計算します。ここでは、プロパティで指定されたカラー情報を使い、アルファ値を変更せずに final_color 変数に格納しています。

```hlsl
    // フォールバック
    Fallback "Standard"
}
```

このシェーダーが使用できない場合、デフォルトの "Standard" シェーダーを使用します。

### Unity エディタからプロパティを使用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3206449/16985fda-8743-d1db-ff6b-5038984ef721.png)

:::note info
作成したシェーダー(マテリアル)のプロパティは、Unity エディタから設定を変更することが可能です。
:::

## まとめ

Unity で HLSL 言語を使用したシンプルなカラーシェーダーを作成することができました。

:::note alert
この記事の実装例は一つのアプローチに過ぎず、必ずしも正しい方法とは限りません。他にも多様な方法がありますので、さまざまな情報を照らし合わせて検討してみてください。
:::

どうでしたか？ Window 11 の Unity で3Dゲームを開発する環境を手軽に構築することができます、ぜひお試しください。今後も Unity の開発トピックなどを紹介していきますので、ぜひお楽しみにしてください。

## 推奨コンテンツ

https://qiita.com/studio_meowtoon/items/fbdb4a09dd9a205a1abd

## 参考資料

https://docs.unity3d.com/ja/2023.2/Manual/SL-VertexFragmentShaderExamples.html