Unity
シェーダ

Unityのシェーダの自由研究。SurfaceShader

趣旨

うちの会社では、シェーダ等の案件が多いので、メンバーの教育のため
UNITYを使ってシェーダを勉強しよう!
という目的だったのだが
UNITYのシェーダは、組み込み関数など、独自に覚えることも多かったので
その部分を 勉強しながらまとめていく

とりあえず 今回は去年のUNITY
UNITY2017.1.0f3
なので 今は少しコードが変わっているかもしれない

シェーダーの種類

UNITYでは

  1. お手軽な SurfaceShader
  2. 本格的な UnlitShader
  3. ポストプロセス用 ImageEffectShader
  4. GPGPU ComputeShader
  5. シェーダーのプリロードを行う ShaderVariantCollection

があるが、とりあえずは通常使う UnlitShaderから覚える
ただし、SurfaceShaderも簡単にふれておく

SurfaceShaderを試す

概要

本来は シェーダーでは 最低2つのシェーダ、頂点シェーダと フラグメントシェーダが必要である
が、SurfaceShaderでは、頂点シェーダーだけ記述し、フラグメントシェーダはビルトインを指定するだけで書かなくていい
もちろん フラグメントシェーダも、カスタムライティングを使えば 独自に作る事ができるが
個人的には それなら あとで話す UnlitShaderで 頂点シェーダとフラグメントシェーダを作ればいいと思う

サンプルのモデル読み込み

今回は中野シスターズを使う(UnityChanをあえて使わない)
http://nakasis.com/

ファイルをダウンロードしてUNITYにインポートし、Sceneに貼り付ける

1.png

シェーダの確認

中野シスターズには Standardというビルトインシェーダーが使われている
ビルトインシェーダーは下記からダウンロードできる
https://unity3d.com/jp/get-unity/download/archive

Standardシェーダの中身は

Shader "Custom/NewSurfaceShader" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

解析

シェーダー名

Shader "Custom/NewSurfaceShader" {  

シェーダー名。UNITYからはこの名前で呼べる

Properties

 Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
    }

インスペクターで設定する値(パラメータ)

シェーダ内で識別する名前 ("インスペクターに表示する名前", 型 ) = デフォルト値

という形式だ

条件

 SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

SubShaderは複数作れる。
TagsやLODに条件を指定しており
SubShaderを上から順番に 最初に一致したSubShaderを実行する
全てに一致しない場合は 下記のFallbackが実行される

 FallBack "Diffuse"

コンパイルディレクティブ

     CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
       #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
       #pragma target 3.0

UNITYマニュアルによると

# pragma surface surfaceFunction lightModel [optionalparams]

上記の場合は
サーフェースシェーダは surf という関数
ライトモデルは Standard
Optionは fullforwardshadows

ということになる

ライトモデルは 下記の4つがビルトインで用意されている

  • Standard: 物理ベースレンダリング
  • StandardSpecular: 物理ベース with スペキュラー
  • Lambert: 非物理ベース
  • BlinnPhong: 非物理ベース with スペキュラー

ちなみに fullforwardshadows のオプションは
フォワードレンダリング時の 影(ドロップシャドウ)を 通常はディレクショナルライト以外は表示しないが
このオプションがあれば スポットライトやポイントライトでも影がつくらしい

変数部

    sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;


        UNITY_INSTANCING_CBUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_CBUFFER_END

変数(uniform)部分
_MainTex 等は 上記のインスペクターの値をここで定義してはじめて使えるようになる

UNITY_INSTANCING_CBUFFER_START
は c_bufferを使う時に使うマクロ

struct Input
は、SurfaceShaderにわたす入力構造体(attribute)だ
セマンティクスがないのも特徴だ

Surfaceシェーダ本体

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }

中身はパット見でわかるとおもうが、構造体に各パラメータを渡している

struct SurfaceOutputStandard
{
    fixed3 Albedo;      // ベース (ディフューズかスペキュラー) カラー
    fixed3 Normal;      // 書き込まれる場合は、接線空間法線
    half3 Emission;
    half Metallic;      // 0=非メタル, 1=メタル
    half Smoothness;    // 0=粗い, 1=滑らか
    half Occlusion;     // オクルージョン (デフォルト 1)
    fixed Alpha;        // 透明度のアルファ
};

今回は ライトモデルに Standardを使っているので SurfaceOutputStandardを returnする

また
StandardSpecula、Lambert、BlinnPhong の場合は 

struct SurfaceOutputStandardSpecular
{
    fixed3 Albedo;      // ディフューズ色
    fixed3 Specular;    // スペキュラー色
    fixed3 Normal;      // 書き込まれる場合は、接線空間法線
    half3 Emission;
    half Smoothness;    // 0=粗い, 1=滑らか
    half Occlusion;     // オクルージョン (デフォルト 1)
    fixed Alpha;        // 透明度のアルファ
};
struct SurfaceOutput
{
    fixed3 Albedo;  // ディフューズ色
    fixed3 Normal;  // 書き込まれる場合は、接線空間法線
    fixed3 Emission;
    half Specular;  //  0..1 の範囲のスペキュラーパワー
    fixed Gloss;    // スペキュラー強度
    fixed Alpha;    // 透明度のアルファ
};

の構造体になる