Unityでシェーダーを触ってみよう

この記事はソフメアドベントカレンダー2021、18日目の記事です。

はじめに

みなさんこんにちは!みつばです!

1日目にもこんな記事を書きましたが、18日目には 『Unityでシェーダーを触ってみよう』 という題材で記事を書きます。1
浅く触れてみただけなのでもしかしたら適当なことを言っているかもしれないですが、その辺は許して欲しいです…

書いた人

HN : みつば
所属班 : プログラム班
回生 : 2回生
Twitter : @0508Maruyama

シェーダーって何?

シェーダーというのは簡単に言うと、3D空間上に存在する物体の表面に陰影をつけたり、質感を与えたりするプログラムのことを指します。
Unityではマテリアル1つに対してシェーダーが1つ設定されるような形になっていて、今回私が作ったのは地形(砂漠)がいい感じになるようなシェーダーです。

シェーダーを触ったきっかけ

第72回津田沼祭で発表したゲーム、Dear My Desertの世界観の表現のためにシェーダーを触ることになりました。
このゲームの世界観がToon調なイメージだったので、自分が『Toonシェーダーとか使ったらいいんじゃないんですかね』みたいなことを言ったら自分が実装担当になっていました。
Unity標準のToonシェーダーでも良さそうですが、どうせなので調べつつ自分で実装してみるのもいい経験かなと思い、Toonシェーダーを書いてみました。

シェーダーを書いてみる

0.今回の方針

Toon調シェーダーが欲しくて、影が現実っぽく無段階に存在するのではなく1影、2影のようなアニメーションっぽい雰囲気の描写がしたい。
インターネットを調べるとRampテクスチャというものを用意し、それをいい感じにマテリアルに適応するシェーダーを書くのが良さそう?
Unityでシェーダーを書くには色々方法がありますが、その中でも一番簡単にシェーダーを書けそうなSurface Shaderを使います。2

1.サンプルファイルを覗く

プロジェクトを作ってシェーダーのテンプレートを作るあたりの操作方法は他の記事を見てもらうとして、まずはSurface Shaderテンプレートのシェーダーの中身を見てみましょう。

SubShader内にいい感じのコードを書いていくことで独自のシェーダーを書くことが出来ます。

2.Toonシェーダーを実現するために用意したもの

インターネットを調べると、光が当たる角度に応じてどんな影色を付けるかを定めたテクスチャ(Rampテクスチャ)を作っておいて、それをマッピングするようなシェーダーを作ると良さそうなことがわかったので、実装していきます。
面の明るさは、光の向きとオブジェクトの法線の内積を計算すると良さそうです。

上で求めた面の明るさをRampテクスチャのx軸の座標として使えば、明るさに応じた色を出力することができそうです。

3.実装

最初に、Properties{...} 内に、UnityのGUIからRampテクスチャを指定できるように_RampTex ("Ramp", 2D) = "White"{}というのを追記しておきます。これで、シェーダープログラムからRampテクスチャを利用できるようになりました。

まずは16行目の #pragma surface ... の部分を #pragma surface surf Ramp へと書き換えます。 この部分はサーフェスシェーダーコンパイルディレクティブと呼ばれ、このシェーダーのエントリー関数が何で、ライティングはどんな方式で行うのかを指定します。
今回作るRampテクスチャを使ったライティングは、Unityが標準で持っているライティングではなく独自に実装したライティング処理を使うので、そのために関数LightingRampを定義します。

fixed4 LightingRamp (SurfaceOutput s, fixed3 lightDir, fixed atten){
  half d = dot(s.Normal, lightDir)*0.5 + 0.4;
  fixed3 ramp = tex2D(_RampTex, fixed2(d, 0.5)).rgb;
  fixed4 c;
  c.rgb = s.Albedo * _LightColor0.rgb * ramp;
  c.a = 0;
  return c;
}

half d = dot(s.Normal, lightDir)*0.5 + 0.4;の部分で、光の向きとオブジェクトの法線の内積を取り(これが明るさ)、それを0~1の間で表現できるようにします。
次に、fixed3 ramp = tex2D(_RampTex, fixed2(d, 0.5)).rgb;の部分で、先ほど求めた値と対応する_RampTexの座標のRGB値を取ります。
最後に、c.rgb = s.Albedo * _LightColor0.rgb * ramp;の部分で、出力される色を決定します。

4.成果物

最終的に、こんな感じのコードが出来ました。

標準シェーダーに色だけ指定して描画した時の画面と、今回書いたシェーダーを使って描画した時の画像を並べてみます。

思った通りの描画ができてよかったです(小学生並の感想)。

最後に

シェーダーに簡単に触れてみた感想は、やっぱりシェーダーの書き方はUnityで使われているC#とは少し感触が違うなと感じました。しかし、上手くシェーダーを使うことができれば豊かな表現ができそうだと感じました。
最新のUnityの機能として、シェーダーグラフなんていうのも追加されたらしく、これはGUI上のエディタでシェーダーが作れる機能っぽいので、わざわざコードを書かなくても良さそうなのがいいですね。
この記事でもしシェーダーに興味を持ってくれた方がいたら、ぜひ触れてみてほしいです。

参考

【Unityシェーダ入門】トゥーンシェーダを自作してみる https://nn-hokuson.hatenablog.com/entry/2017/03/27/204255

カスタムライティング関数でRampシェーダーを作るhttps://unityshader.hatenablog.com/entry/2017/04/02/155623


  1. シェーダーグラフの話は?←時間がなかったです… 
  2. 実はシェーダーをUnityで簡単に触ることが出来るシェーダーグラフというのがあるのですが、これの存在に(制作中は)全く気づきませんでした。 

コメント

タイトルとURLをコピーしました