勉強して7日たったのでシェーダーについてまとめる【Unity】【VRCHAT】

 はじめまして、あんくると申します。記事を見ていただきありがとうございます。この記事ではシェーダーの細かな改造や組み合わせ等を行う為に少しばかりシェーダーについて調べてみたのでその事について書いていきたいと思います。

※Unity2018を使用

使える言語について

UnityではOpenGL ESの言語GLSL、グラボで有名なNVIDIAが開発した最初のシェーディング言語Cg(開発終了)、MicrosoftのHLSL等が使えます。

ただし、VRCではGLSLは使えません。以下にHLSLに書き換える時に役立つサイトを載せておきます。

https://qiita.com/kitasenjudesign/items/89297f239059662cd38e

シェーダーの種類

シェーダーには大きく分けてサーフェスシェーダーバーテックス(頂点)シェーダー/フラグメントシェーダー固定関数シェーダーの3つがあります。サーフェスシェーダーは簡単にstandardShaderのようなライティングを実装できるものでCreateのStandardSurfaceShaderからテンプレートが作れます。バーテックスシェーダー/フラグメントシェーダーの方はライティングの影響を受けさせるのに手間です。UnlitShader・ImageEffectShaderでテンプレートが作れます。固定関数シェーダーはプログラマブルシェーダをサポートしてない古いハード用なようです。あまり見ませんが以下のDiffuseなどがそうです。

FallBack "Diffuse"

使い分けとしてはstandardShaderで見るようなライティングをしたい・簡単な物であればサーフェスシェーダーを、ライティングの影響を受けないもしくは自身で細かく設定したい時や、形を変形・複製したりしたい時はバーテックスシェーダー/フラグメントシェーダーと使い分ける必要があります。

無題1

見分け方・諸注意

SubShader内にある#pragma に書かれているライティングモデルオプション(Lambert・Standard・StandardSpecular)と下に続くサーフェスシェーダーがあるかどうかで確認できる。

#pragma surface surf Standard fullforwardshadows
Lambertなら void surf(Input IN,inout SurfaceOutput o)                        
Standardなら void surf(Input IN,inout SurfaceOutputStandard o)  
StandardSpecularならvoid surf(Input IN,inout SurfaceStandardSpecular o)

https://docs.unity3d.com/ja/2018.4/Manual/SL-SurfaceShaders.html

サーフェスシェーダーとバーテックスシェーダー/フラグメントシェーダーでは透過のさせ方が違ったりします。

シェーダーの構造について

こちらCreateから作れるサーフェスシェーダーをそのまま抜き出してきました。このコードに解説を入れてみます。

//ここでシェーダーの名前と場所を決める。/で階層が1つ下がる。
Shader "Custom/NewSurfaceShader"
{//Propertiesの中にあるものがunity上からも操作できる。
   Properties
   {//以下の形式で記載される。
   //内部で使う時の変数名(”unity上の表記”,タイプ)=(初期値)
       _Color ("Color", Color) = (1,1,1,1)
       //テクスチャがない場合白色になる
       _MainTex ("Albedo (RGB)", 2D) = "white" {}
       //Range(0,1)はスライダーの事。この時は0~1の値をとる。
       _Glossiness ("Smoothness", Range(0,1)) = 0.5
       _Metallic ("Metallic", Range(0,1)) = 0.0
   }
   //SubShaderはシェーダーをまとめておくもの。GPUが対応してないなら飛ばされる。
   SubShader
   {//Tags 表示順やタイプを指定できる。
   //ここを間違えば透過するもの設置したのにそこに背景がないって事になるのでしっかり設定しよう。
       Tags { "RenderType"="Opaque" }
       LOD 200//描画の計算量 その時の負荷によってハードウェアが実行するかしないか決める指標。特に変更する必要はない。
     
       CGPROGRAM
       // ライティングモデルオプション 標準的な照明モデル すべてのライトで影がつく
       #pragma surface surf Standard fullforwardshadows
       // target の数が上がれば上がるほど使える機能が上がるが対応していない端末もある。
       #pragma target 3.0
       //今回は使うが使わなくても宣言する必要がある?使ってなくても見るので...
       sampler2D _MainTex;
       struct Input
       {
           float2 uv_MainTex;
       };
       //内部での変数の宣言
       half _Glossiness;
       half _Metallic;
       fixed4 _Color;
       //GPU インスタンシング 同じメッシュのオブジェクトを最初に計算しておいて複製して使う。なぜか//をつけて消してある。仕様変更?
       // #pragma instancing_options assumeuniformscaling
       //GPU インスタンシングを使う時以下2つを記載する。
       //インスタンス毎に変えるプロパティをここに定義する。
       //インスタンスとは複製したほかのメッシュの事。VRCのワールドにもあるよね。
       UNITY_INSTANCING_BUFFER_START(Props)
       //何かあればここに記入
       UNITY_INSTANCING_BUFFER_END(Props)
       //Standardの場合のサーフェス
       //入力はIN,出力はo
       void surf (Input IN, inout SurfaceOutputStandard o)
       {
           // カラーをテクスチャに乗算
           fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
           o.Albedo = c.rgb;
           // メタリックさと滑らかさはスライダーの値を読む
           o.Metallic = _Metallic;
           o.Smoothness = _Glossiness;
           o.Alpha = c.a;
       }
       ENDCG
   }
   //もしSubShaderが飛ばされた時はDiffuseを使用する。
   FallBack "Diffuse"
}

次にバーテックスシェーダー/フラグメントシェーダーです。サーフェスシェーダーで記載した部分は省きます。

Shader "Unlit/NewUnlitShader"
{
   Properties
   {
       _MainTex ("Texture", 2D) = "white" {}
   }
   SubShader
   {
       Tags { "RenderType"="Opaque" }
       //計算の負荷はサーフェスシェーダーより低め
       LOD 100
       Pass//パスはフォルダーのようなもの
       {
           CGPROGRAM
           //ここでシェーダー類の宣言をしておきます。
           //複製させるならhull,geometryシェーダーの宣言も必要になります。
           #pragma vertex vert
           #pragma fragment frag
           // Unityの設定でフォグを選択した時その影響を受けるようになる。
           //逆にここでフォグについて設定しなければフォグがのらない。
           #pragma multi_compile_fog
           //UnityCG.cgincは便利な関数の詰め合わせ
           #include "UnityCG.cginc"
           //structは構造体、関数の中にどういう情報を含めておくか。
           //バーテックスシェーダーの入力ではここに記載したのもだけ取得できる。
           struct appdata
           {
               float4 vertex : POSITION;
               float2 uv : TEXCOORD0;
           };
           //バーテックスシェーダーの出力/フラグメントの入力
           struct v2f
           {
               float2 uv : TEXCOORD0;
               UNITY_FOG_COORDS(1)
               float4 vertex : SV_POSITION;
           };
           sampler2D _MainTex;
           float4 _MainTex_ST;
           v2f vert (appdata v)
           {
               v2f o;
               //座標をオブジェクト空間からカメラのクリップ空間に変換
               o.vertex = UnityObjectToClipPos(v.vertex);
               //テクスチャスケールとオフセットを合わせる
               o.uv = TRANSFORM_TEX(v.uv, _MainTex);
               //Fog の効果を追加
               UNITY_TRANSFER_FOG(o,o.vertex);
               return o;
           }
           //SV_Targetは自身にレンダリングするときに使う。基本そのまま。
           fixed4 frag (v2f i) : SV_Target
           {
               // テクスチャを頂点に合わせている
               fixed4 col = tex2D(_MainTex, i.uv);
               // Fog の効果を追加
               UNITY_APPLY_FOG(i.fogCoord, col);
               return col;
           }
           ENDCG
       }
   }
}

UnityCG.cgincについて https://docs.unity3d.com/ja/2019.4/Manual/SL-BuiltinIncludes.html

ShaderLabについて

ShaderLabとは、Unity上でシェーダー類を使えるようにする入れ物です。シェーダー以外のProperties,SubShader等もUnity独自の仕様で、これらがあるのでUnity上からプロパティをいじれたり、アセットと同様にプロジェクトに入れるだけでシェーダーが使えるようになってます。形式だけ守っていれば特に気にしないで大丈夫です。

Shader "Custom/NewSurfaceShader"
{
   Properties
   {
   }
   
   SubShader
   {
       Pass
       {

       }
   }
}

最後に

 サーフェスシェーダーとバーテックスシェーダー/フラグメントシェーダーの違いを認識して検索すれば必要な情報は出てくると思います。わからない場合は近くのつよつよシェーダーマンにお伺いしてみてください。道筋を示してくださるでしょう。

参考になるサイト

https://amagamina.jp/unity-shader/

この記事が気に入ったらサポートをしてみませんか?