【Unity】Shader Graphの使い方を分かりやすく解説!シェーダーとは?
「Unityを学ぶ際によくつまずくのはシェーダである」という話があります。
そんな話を聞くと、
- シェーダって何?
- どんなところが難しいの?
といった疑問が出てくると思います。私もそうでした。
そこで本記事では、
- シェーダとはどういうものか
- シェーダを用いた影の書き方・使い方
- シェーダを使った実践的なサンプル
- 学ぶのにおすすめの本
の順でご紹介いたします。
9分くらいで読めますし、Unityにおけるシェーダを学ぶ第一歩となるかと思いますので、ぜひご一読をください!
Contents
Unityのシェーダとは
まず「シェーダ」 とはなんなのでしょうか?
「シェーダ」とは、3DCGにおいてシェーディング(陰影処理)を行うプログラムのことを指します。
対象のオブジェクトの色や質感の変更、頂点情報を操作してのアニメーションなど多岐にわたる使い方がされます。
Unityにおけるシェーダの種類
Unityにおけるシェーダは、書き方によって下記の3種類に分けられます。
サーフェスシェーダ
オブジェクトの基本的な色の変化、表面の質感やテクスチャの変更などを手軽にできる簡易的な書き方になります。
後述する頂点/フラグメントシェーダをマクロ化したもので、処理中で最終的に頂点/フラグメントシェーダに変換されます。
Unity上では、Standard Surface Shaderというテンプレートが用意されています。
シェーダを適用するにあたり、光源の影響や影のつき方など細かく考えなくてもよい一面はありますが、細かく制御するといったことは難しくなっています。
頂点/フラグメントシェーダ
Unityにおいて最も基本となるシェーダの書き方で、前述のサーフェスシェーダでは出来ない細かな制御が可能です。
頂点シェーダ、フラグメントシェーダという2つの部分で構成されています。
頂点シェーダは、名前の通り頂点を制御するシェーダになります。
3DCGモデルの頂点情報を読み取って、最終的に出力する位置を決定します。
頂点を動かして形状を変化させたり、アニメーションをさせたりすることもできます。
フラグメントシェーダは、DirectXにおいてはピクセルシェーダと呼ばれています。
CGの最終的な出力は画像になりますが、その画像の各画素をどのような色で出力するかを制御するのがフラグメントシェーダになります。
光源や影の影響を調整したり、色を変更したり、輪郭線を描画することなどができます。
サーフェスシェーダと比較して、光源や影の影響を全て考えて実装する必要があるので、実装にあたり高度な知識が必要となります。
Unity上では、Unlit Shader、Image Effect Shaderという2種類のテンプレートが用意されています。
Unlit Shader
UnlitとはUnLightedの略称で、光源からの影響などを無視したシェーダとなります。
このシェーダが適用されたオブジェクトは、光源の影響を受けず設定された色やテクスチャの色がそのまま表現されます。
Image Effect Shader
最終的に出力されるカメラやテクスチャに適用するシェーダでポストエフェクトとも呼ばれます。
画面全体の色相を変更したり、画面全体をぼかしたりすることができます。
また、シーン遷移をするときのフェードイン・アウトやモザイクなどの効果も実現することができます。
固定関数シェーダ
CPUを通さず、GPUに直接命令を出す書き方です。
CGを描画する過程はレンダリングパイプラインと呼ばれますが、このシェーダではよりGPUの特性を活かす方法を取ります。
Unity上では、Compute Shaderというテンプレートが用意されています。
詳細な説明は今回割愛します。
Unityにおけるシェーダの取り扱い方・使い方
記事を作成している時点(2019/2/23)で、Unityでシェーダを取り扱う方法は下記の2つのやり方があります。
ShaderLab言語を記述するやり方
ShaderLab言語は、シェーダコードを記述するために用意されているUnity独自のプログラム言語となります。
シェーダコードの拡張子は”.shader”もしくは”.cginc”となっており、プロジェクトウィンドウで作成するとアセットとして登録され、マテリアルコンポーネントのインスペクターウィンドウから選択できるようになります。
生成直後のコードは下記のようになっています。
コードの中身は大きく分けて3つのパートにわかれています。
・Parameters
インスペクターウィンドウに公開する変数を記述する箇所。
・Shader Settings
ライティングや透明度などのシェーダにおける設定項目を記述する箇所。
・Surface Shader
シェーダ本体のプログラムを記述する箇所。
シェーダファイルは、Unityがファイルの更新を検知するたびに、自動的にコンパイルが実行されマテリアルに反映がされていきます。
SharderGraph(シェーダグラフ)を使用するやり方
Unity 2018のバージョンから使えるようになった新しい機能になります。
ShaderGraphが実装される前は、アセットストアでShader Forgeという似たような機能が有料で提供されていました。
従来のShaderLab言語のような言語を記述するタイプのエディターではなく、グラフ上でボックスを配置し、それをつなげていくというノードベースで構築していくエディターになるのでコード書くようなプログラミングが苦手という人向けにおすすめです。
Unityのシェーダを用いた陰影(影)の書き方・使い方
それでは実際に使い方をみていきます。
Unityにおけるシェーダの使い方を知るために、
- ShaderLab言語で、物体の影への着色
- ShaderGraphで、背後から光があたったようなエフェクト(リムライティング)
を実装してみます。
今回サンプルを動作させるにあたり使用した環境は下記になります。
- モデル:MacBook(Ratina,12inch,2017)
- OS:macOS Mojave 10.14.3
- CPU:Intel Core i7
- GPU:Intel HD Graphics 615
- Unityのバージョン:Unity 2018.3.6
3Dモデルはアセットストアから下記を取得して使用しています。
参考URL)Unityのアセットストアの使い方
ShaderLab言語
プロジェクトの立ち上げ
テスト用のプロジェクトを立ち上げます。
今回は仮に「Shader_ShaderLab_Test」と名前をつけておきます。
Templateは「3D」で立ち上げます。
PlaneオブジェクトとBronze Bearの配置
影を映すための床としてPlaneオブジェクト、影を出す物体としてBronze Bearを配置します。
念のためオブジェクトの追加のやり方を記載しておきます。
Planeオブジェクトは、ヒエラルキーウィンドウ>Create>3D Object>Planeで追加できます。
Bronze Bearはアセットストアからインポート後、Asset>Statue Bear Bronze>Models>SatueBearをヒエラルキーウィンドウへドラッグ&ドロップすることで追加できます。
それぞれのオブジェクトは下記の画像の位置を参考に調整してください。
シェーダファイルとマテリアルの作成
まずはシェーダファイルを作成します。
プロジェクトウィンドウ>Create>Shader>Standard Surface Shaderと選択すると、シェーダファイルが出来上がります。
名前はなんでも構いませんが、とりあえず「Shadow」としておきます。
続けて、マテリアルを作成します。
プロジェクトウィンドウ>Create>Materialを選択すると、マテリアルファイルが出来上がります。
名前はシェーダファイルと同じでも構いませんが、とりあえず「Material_Shadow」としておきます。
影の色を変更するシェーダコードの記述
シェーダファイルをダブルクリックし、影の色を変更するシェーダコードを記述していきます。
影の色を変更するシェーダコードは下記になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
Shader "Custom/Shadow" { //インスペクターから色を指定できるようにする Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _ShadowColor("ShadowColor",Color)=(0,0,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 #pragma surface surf SimpleLambert sampler2D _MainTex; half4 _ShadowColor; struct Input { float2 uv_MainTex; }; // 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_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D(_MainTex, IN.uv_MainTex); } //影の色を変える処理 half4 LightingSimpleLambert(SurfaceOutput s, half3 lightDir, half atten) { half NdotL = max(0,dot(s.Normal, lightDir));//ライティングの計算(法線とライトのベクトルの内積) half4 c; //影の色の変更 c.rgb = s.Albedo*_LightColor0.rgb*NdotL*(atten+_ShadowColor.rgb); c.a = s.Alpha; return c; } ENDCG } FallBack "Diffuse" } |
Parameters部分では、インスペクターウィンドウから影の色を選択できるようにしています。
シェーダ本体の処理部分は、ランバート拡散照明モデルという照明モデルを利用して記述しています。
ランバート拡散照明モデルの詳しい解説は下記の参考URLを参照してください。
参考URL) おもちゃラボ【Unityシェーダ入門】ランバート拡散照明モデルを試す
オブジェクトへの適用
シェーダコードの記述が完了したら、シェーダファイルをマテリアルファイルへ適用します。
シェーダファイルをマテリアルファイルへドラッグ&ドロップを行えば適用が行われます。
オブジェクトへは、マテリアルファイルをオブジェクトへドラッグ&ドロップを行えば適用がされます。
シェーダを適用することで、影の色が黒から青へ、インスペクターウィンドウから選択することで様々な色に変更できるのがわかると思います。
ShaderGraph
プロジェクトの立ち上げ
こちらもテスト用のプロジェクトを立ち上げます。
名前は仮に「Shader_ShaderGraph_Test」としておきます。
ShaderGraph自体はまだ開発を進めているPerview版なので、下記のTempreteでしか動作しません。
- Hgh-DefinitionRP(Preview)
- LightweightRP(Preview)
今回は処理が軽いといわれているLightweightRPでプロジェクトを立ち上げます。
PlaneオブジェクトとBronze Bearの配置
プロジェクトを立ち上げた直後は、画像のようにサンプルのオブジェクトが配置されていますが、
今回はわかりやすくするために赤で囲っている初期配置オブジェクトは削除しておきます。
ShaderLab言語の時と同じように、PlaneオブジェクトとBronze Bearを配置していきます。
ShaderGraphファイルとマテリアルの作成
ShaderGraphファイルを作成します。
プロジェクトウィンドウ>Create>Shader>PBR Graphを選択することで作成ができます。
名前はなんでも構いませんが、とりあえず「Shadow」としておきます。
マテリアルの作成方法は、ShaderLab言語の時と同様になります。
リムライティングの処理をShaderGraphにて作成
ShaderGraphファイルを開くと、初期の状態では下記のように、PBR Masterノードのみが存在します。
これに様々なノードを作成し、接続していくことで目的とするエフェクトを作成することになります。
それでは、まずリムライティング自体をShaderLab言語で記述するとどのようになるか確認します。
リムライティングの詳しい解説は下記の記事を参照ください。
参考リンク)おもちゃラボ【Unityシェーダ入門】リムライティングのシェーダを作る
参考記事を確認するとリムライティングをShaderLab言語で記述すると下記のようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
Shader "Custom/sample" { SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Standard #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 viewDir; }; void surf (Input IN, inout SurfaceOutputStandard o) { fixed4 baseColor = fixed4(0.05, 0.1, 0, 1); fixed4 rimColor = fixed4(0.5,0.7,0.5,1); //以下をShaderGraphにて構築する o.Albedo = baseColor; float rim = 1 - saturate(dot(IN.viewDir, o.Normal));//処理1 o.Emission = rimColor * pow(rim, 2.5);//処理2 } ENDCG } FallBack "Diffuse" } |
それでは、上記のソースを参考にしてShaderGraphを実装していきます。
ShaderGraphウィンドウでの主な操作としては下記があります。
- 右クリックでノードの生成ウィンドウの立ち上げ
- 接続元のノードの●をドラッグし、接続先のノードの●へ持っていくと接続
- 使用するノードや作りたいシェーダ効果により数値の入力
完成したシェーダは下記の画像になります。
上記のShaderGraphでは、シェーダコード内の対象の計算部分を必要な要素を持ったノードで接続して実現しています。
リムライティングの処理としては、オブジェクトの輪郭部分のエミッションを高くすることで、背後から光が当たっているような処理を構成する必要があります。
シェーダコードとShaderGraphの処理の対応は下記のようになっています。
処理1:視線ベクトルと法線ベクトルの2つのベクトルの計算によりエミッションを計算
処理2:輪郭の色を設定し、マテリアルに適用
シェーダが完成したらウィンドウ内のSave Assetをクリックして保存後、シェーダファイルを適用したマテリアルファイルをBronze Bearにドラッグ&ドロップすることでシェーダが適用されます。
実践編!シェーダを使って波を表現してみよう
それぞれのツールの使い方がわかったところで、より実践的なエフェクトとしてシェーダで波を実装してみます。
ShaderLab言語
まずは、ShaderLab言語で実装をしてみます。
Planeオブジェクトの配置とシェーダファイル、マテリアル作成までは割愛します。
波のエフェクトのシェーダコードは下記になります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
Shader "Custom/SL_Wave" { Properties { _MainTex ("Water Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert #pragma vertex vert #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; //オブジェクトをなびかせるカスタム頂点シェーダ void vert(inout appdata_full v, out Input o ) { UNITY_INITIALIZE_OUTPUT(Input, o); float amp = 0.5*sin(_Time*10 + v.vertex.x * 100); v.vertex.xyz = float3(v.vertex.x, v.vertex.y+amp, v.vertex.z); } //UVスクロールを行う void surf (Input IN, inout SurfaceOutput o) { fixed2 uv = IN.uv_MainTex; //x軸方向へのUVスクロール(満ち引き) uv.x+=0.5*sin(_Time*10); //x軸方向へのUVスクロール(一方向) //uv.x+=0.5*_Time; fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = tex2D (_MainTex, uv); o.Alpha = c.a; } ENDCG } FallBack "Diffuse" } |
今回はサーフェスシェーダで設定したテクスチャを動かすUVスクロール、頂点シェーダでPlaneオブジェクトをなびかせる処理を入れています。
UVスクロールとは、ポリゴンにテクスチャを貼り付ける時に使用するuv座標を利用し、それにフレームごとにオフセットを足すことでスクロールさせる手法になります。
しかし、サーフェスシェーダでは前フレームを記憶するということができませんので、代わりに
uv座標にスクロール速度×_Time変数を足すことにより移動距離を算出しスクロールをさせます。
今回のコードでは、x座標の方向へ一方向にスクロールするコードとsin関数を使って満ち引きを表現したコードを記述しています。
1 2 3 4 5 6 7 8 9 10 11 12 |
void surf (Input IN, inout SurfaceOutput o) { fixed2 uv = IN.uv_MainTex; //x軸方向へのUVスクロール(満ち引き) uv.x+=0.5*sin(_Time*10); //x軸方向へのUVスクロール(一方向) //uv.x+=0.5*_Time; fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = tex2D (_MainTex, uv); o.Alpha = c.a; } |
波自体は対象となるPlaneオブジェクトの頂点を移動させるシェーダコードを記述することで実現が可能です。
今回のコードでは、sin関数と_TIme変数を使用し、時間とともに頂点のy座標を変化するように記述がしてあります。
1 2 3 4 5 6 |
void vert(inout appdata_full v, out Input o ) { UNITY_INITIALIZE_OUTPUT(Input, o); float amp = 0.5*sin(_Time*10 + v.vertex.x * 100); v.vertex.xyz = float3(v.vertex.x, v.vertex.y+amp, v.vertex.z); } |
動作をさせると下記の画像のようになります。
ShaderGraph
次にShaderGraphで波を実装していきます。
完成したシェーダは下記の画像になります。
それでは、それぞれの処理をみていきます。
まずはUVスクロールです。
基本的には、ShaderLab言語で記述した内容を必要なノードで表現する形になります。
①では、TimeノードとVector1ノード(WaveSpeed)をMultiplyノードで掛け合わせ移動距離の計算を行なっています。
②では、SampleTexture2Dノードで貼り付けるテクスチャの設定、Tilling And Offsetノードでテクスチャのタイリング数の設定を行なっています。
今回は、2種のタイリング数を使うことで表現に深みを出しています。
③では、先ほどの2種のタイリングを持ったテクスチャを線形補間し滑らかに設定ができるようにしています。
次に、頂点シェーダ部分になります。
こちらはシンプルに、ShaderLab言語で記載した数式をノードで実現していきます。
接続は画像を参考にしてみてください。
今回使用しているVector1のような変数ノードは、ノードを右クリック>Convert to propertyと選択することでプロパティ化し、マテリアルのインスペクターウィンドウから設定可能になります。
実際に動作をさせると下記のようになります。
設定した変数をインスペクターウィンドウでいじることでスピードも変わることがわかるかと思います。
今回は波を実装しましたが、組み合わせや処理を考えることで炎や煙などのエフェクトも作成は可能です。
Unityでシェーダを学ぶのに良い本2選
・Unityシェーダープログラミングの教科書1【ShaderLab言語解説編】/2【反射モデル&テクスチャマップ】
現在BOOTHでPDFにて販売しているUnityのシェーダーに焦点を当てた、タイトル通り教科書的な書籍になります。
シェーダに関してより知りたい、わからないことがあるという人は一読の価値があります。
サンプルプログラムはありますが、実行した結果の画像がないので読みながら実際に手を動かしてどのようになるか確認するといいです。
リンク)Unityシェーダープログラミングの教科書1
リンク)Unityシェーダープログラミングの教科書2
・Unityゲーム プログラミング・バイブル
複数の著者が様々なテーマについて記述している、サンプルゲームを作成しながらUnityの使い方を学ベル書籍になります。
上記のシェーダープログラミングの教科書の著者もシェーダーの項目で参加しています。
シェーダーだけでなく、キャラクターを動かすための方法も記載されているので手元にあると何かと役立ちます。
【番外編】
絶版本、価格が高いもの、シェーダを取り扱うにあたり必要な数学に関連する書籍も番外編として紹介しておきます。
・DirectX9 シェーダプログラミングブック【絶版】
使用されている言語自体はDirectXであるのものの、シェーダーで扱う数学、シェーダープログラミングの基礎事項、画像処理など幅広く網羅している良書です。
・GPU Gems3日本語版
内容としては、GPUを使用したグラフィックプログラミングに関する事項をまとめたものとなっています。
シェーダー とGPUは深い関係があるので是非読んでおきたい一冊です。
英語版であれば無料でネット公開されていますが、かなりのボリュームの本なのでコストとの兼ね合いで日本語版を購入するか検討する方が良いかもしれません。
リンク)GPU Gems 3 日本語版
・Unityでわかる!ゲーム数学
ゲームを作成する際に必要な数学をUnityを使って「動かしながら学ぶ」をコンセプトとして書かれた本になります。
シェーダー以外にも、ものを動かすために必要な数学、物理知識に関しても書かれていますので何か作りたい時にもあると便利です。
リンク)Unityでわかる!ゲーム数学
まとめ
いかがでしたでしょうか。
本記事では、Unityにおけるシェーダに関して紹介していきました。
シェーダという一つのハードルを超えることにより、より自身の表現したいことに近づけていくことができます。
この記事が、Unityを学んでいる人の参考になれば幸いです!
合わせて読みたい関連テック記事)
この記事はいかがでしたか?
もし「参考になった」「面白かった」という場合は、応援シェアお願いします!