Unityで始めるシェーダー開発入門

3Dゲームエンジンとして広く使われるUnityでは、ビジュアル表現の根幹を支えるのがシェーダーです。見た目の質感や光の反射、透明効果など、画面上に映るすべてのピクセルはシェーダープログラムによって制御されています。しかし、多くの開発者はビルトインシェーダーの選択やマテリアルの調整に留まり、その内部構造やカスタム実装には手を出さない傾向があります。本稿では、ゼロからシェーダーの基本構造と表面シェーダー(Surface Shader)の書き方を解説し、自作シェーダー開発への第一歩を踏み出すための足場を提供します。

シェーダーとマテリアルの関係

シェーダーとは、頂点座標やテクスチャ、色などの入力を元に、最終的なピクセル色を計算する小さなプログラムです。一方、マテリアルは「どのシェーダーを使うか」「そのシェーダーにどのようなパラメータ(テクスチャや色)を渡すか」を定義した設定ファイルのようなものです。つまり、マテリアル = シェーダー + パラメータセット と考えるとわかりやすいでしょう。レンダラーにマテリアルを割り当てることで、オブジェクトがどのように描画されるかが決定されます。

シェーダーの種類:表面 vs フラグメント

Unityにおけるシェーダーは主に二種類あります:

  • 表面シェーダー(Surface Shader):光照モデルやパス生成を自動で処理してくれる高レベルAPI。初心者向けで、短いコードで自然なライティングを実現可能。
  • フラグメントシェーダー(Fragment Shader):ピクセル単位の完全制御が可能だが、低レベルな記述が必要。パフォーマンスチューニングや特殊エフェクト向け。

本稿では、学習コストが低い「表面シェーダー」を中心に解説します。

最小限のシェーダー構造

以下は、テクスチャをそのまま表示するシンプルな表面シェーダーの例です:

Shader "Custom/BasicTexture" {
    Properties {
        _BaseMap ("Main Texture", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM
        #pragma surface Execute LightingLambert

        sampler2D _BaseMap;

        struct VertexInput {
            float2 uv_BaseMap;
        };

        void Execute (VertexInput input, inout SurfaceOutput output) {
            fixed4 sampledColor = tex2D(_BaseMap, input.uv_BaseMap);
            output.Albedo = sampledColor.rgb;
            output.Alpha = sampledColor.a;
        }
        ENDCG
    }
    Fallback "Diffuse"
}

Properties:ユーザー入力の定義

Propertiesブロックでは、マテリアルインスペクターに表示されるパラメータを宣言します。書式は以下の通り:

_PropertyName("Display Name", Type) = DefaultValue

例えば_BaseMap ("Main Texture", 2D) = "white" {}は、「_BaseMap」という名前の2Dテクスチャ変数を宣言し、デフォルト値として白色テクスチャを指定しています。この変数はCGコード内でも同じ名前で参照可能です。

Tags:描画条件の指定

Tags { "RenderType"="Opaque" }は、このシェーダーが不透明オブジェクト用であることを示します。他にも"Queue"="Transparent+50"のように描画順序を細かく制御することも可能です。キュー値が小さいほど先に描画され、背景(1000) → ジオメトリ(2000) → 半透明(3000) → オーバーレイ(4000) の順が標準です。

LOD:品質レベルの閾値

LOD 200は、このシェーダーが中程度の品質設定(例:Diffuse相当)で有効になることを意味します。Quality SettingsでLOD上限を下げると、より軽量なシェーダーに自動切り替えられます。

CGPROGRAM~ENDCG:シェーダー本体

ここがシェーダーの核となる計算部分です。#pragma surface Execute LightingLambertは、「Execute」という関数を表面シェーダーとしてコンパイルし、ランバート(拡散光)モデルを使用することを指示します。

sampler2D _BaseMap;は、Propertiesで宣言したテクスチャ変数をCG側で再宣言してリンクする必要があります。

構造体VertexInputは、頂点シェーダーからフラグメントシェーダーへ受け渡すデータを定義します。ここではUV座標のみを保持しています。

関数Executeがメイン処理部です。第一引数は入力データ、第二引数inout SurfaceOutputは出力構造体で、以下のようなメンバーを持ちます:

struct SurfaceOutput {
    half3 Albedo;   // 拡散色
    half3 Normal;   // 法線
    half3 Emission; // 自己発光
    half Specular;  // 鏡面反射強度
    half Gloss;     // 光沢度
    half Alpha;     // 透明度
};

処理内容は単純で、tex2D関数でUV座標に対応するテクセル色を取得し、それをAlbedo(基本色)とAlpha(透明度)に代入しています。これにより、テクスチャ通りの色でオブジェクトが描画されます。

次の一歩

この基本形を理解したら、Unity公式ドキュメントのSurface Shader Examplesを読むことをお勧めします。diffuse、bump mapping、rim lightingなど、代表的なエフェクトの実装例が多数掲載されており、実践的な学習に最適です。少しずつ改造を加えながら、自分の目指すビジュアルをコードで表現できるようになりましょう。

タグ: Unity Shader CG HLSL SurfaceShader

5月29日 03:04 投稿