しゅみぷろ読者になるしゅみぷろプログラミングとかこの広告は、90日以上更新していないブログに表示しています。Algebraはじめに以前記事にしたesprog.hatenablog.comesprog.hatenablog.comこちらの知識を使って、なるべくUnityのライトやShaderLibrary(.cginc)を使わずシェーディングを行ってみました。Unityのグラフィックスを勉強するには結構いい題材だったかなと思います。プロジェクトは以下で公開しています。github.com変換行列をシェーダーに渡す普段から使っているUNITY_MATRIXですが、これは描画するオブジェクトごとにUNITY_MATRIX_Mが変化します(描画するオブジェクトごとにモデル変換行列が設定されます)。描画を行うカメラに応じてUNITY_MATRIX_Vも変化します。今回はこれらの変換行列をC#で計算して渡すようにしています。//matrixがオブジェクトごとに作用してくれない。}}上記の例ではシェーダーのuniform変数「mvp_matrix」「mv_matrix」「v_matrix」にそれぞれの変換行列をセットしています。シェーダーは簡易的に以下の様なものを作りました。Batchingが行われる場合、Batchingされた複数のオブジェクトは同一の変換行列を渡されることとなり、結果的にちゃんと表示されません。Batchingは描画パフォーマンスを大きく向上させてくれるので、これを無効にする(たとえばシェーダーを意味もなくマルチパスにしてしまう等する)のはナンセンスな気がします。うまい回避方法も思いつかなかったため、後述するシェーディングでは変換行列にUNITY_MATRIXを使っています。ランバートシェーダーランバート反射モデルを表す式は$$R_{\mathrm{diffuse}}=k_{\mathrm{diffuse}}(p)<n,l>L(p)$$でした。今回は頂点シェーダーでランバート反射モデルを実装します。環境光$R_{\mathrm{ambient}}$についても考慮します(といっても出力色に定数値を加算するだけです)。C#からシェーダーに情報を渡す部分です。環境光に関してはカラーを考慮せず適当に$$R_{\mathrm{ambient}}=k_{\mathrm{ambient}}$$としています。シェーダーは以下のようになります。}}ほぼ頂点シェーダーで完結しています。フラグメントシェーダーではラスタライザーで線形補間された頂点色をそのまま返しているだけです。v2fのvertexは使用していませんが、頂点シェーダーがPOSITION(SV_POSITION)セマンティクスの値を返さなければ描画が無視されてしまいます。処理を詳しく見ていきます。vs_pos);という処理をしています。頂点の座標をModelSpaceからViewSpaceに変換ViewSpaceにおける頂点位置から光源方向のベクトルを計算を意味します。3、4に関しては見慣れているので1、2について解説します。頂点の法線をViewSpaceに変換する場合、UNITY_MATRIX_MVを使うわけには行きません。変換にはT変換(平行移動)が含まれる(法線は方向を表すため不適切)変換にはS変換(拡大縮小)が含まれる(法線に対してはこの変換の逆数をとる必要がある)T変換成分は行列内で孤立しているのでこれを無視するのは簡単で、左上3x3の行列を取り出して使えばいいだけです。R_{M}$$となります。ここで、$R_{V}$はView変換のR行列、$S_{M}$はModel変換のS行列、$R_{M}$はModel変換のR行列です。R_{M}$$となります。^{-1})^T$$です。^{-1})^T$$となります(演算順は変わります)。^T$$となります(演算順が変わり、元の順に戻ります)。法線をModelSpaceからViewSpaceに変換するには、MV変換行列の左上3x3成分に関して逆行列を求め、転置した行列で変換を行えば良いことになります。ここで気付かれた方もいるかもしれませんが、今回のシェーダーでは左上3x3の行列を取りしていません。これは、行列の平行移動成分が逆行列を求める際に、拡大縮小/回転成分に直接影響を及ぼさないことと、行列の転置によって変換後は平行移動成分の逆数が結果の$w$成分にしか現れないことを利用しています。この$w$成分は利用しないため、どんな値が代入されていようと問題になりません。光源の座標をWorldSpaceからCameraSpaceに変換こちらは簡単です。光源の座標を変換するのですが、光源は頂点(Model)の変換に合わせる必要が無いので、Model変換はせず光源のWorld座標をそのままV変換しています。C#から与えられる光源の座標はWorldSpaceであることに注目して下さい。フォンシェーダーブリンの近似によって得られるフォンの反射モデルです。先ほどのランバート反射をするシェーダーに$$R_{\mathrm{specular}}(p,v)=k_{\mathrm{specular}}(p)<n,h>^{S}L(p)$$の計算を追加しただけです。C#では以下のようにシェーダーに値を渡しています。}}スペキュラー反射用の反射係数と鏡面反射指数が加わっただけです。シェーダーは以下のようになりました。}}視点方向のViewベクトルと光源方向のLightベクトルからハーフベクトルを求め、先ほどの式に代入しました。スペキュラー反射は視点方向のベクトルに依存するため、ディフューズ反射と違って視点移動で変化が生まれます。フォンシェーディング「フォンシェーディング≠スペキュラー反射(フォンの反射モデル)」なので注意です。先ほどのフォンシェーダーでは、頂点に設定された法線を用いて「頂点シェーダー」でスペキュラーを計算していました。頂点数の少ないオブジェクト等では、ラスタライザーによる線形補間が問題になり、不自然なボケ方をしてしまいます。そこで用いるのがフォンシェーディングです。フォンシェーディングは、ラスタライザーに法線を入力し、線形補間してもらう手法のことです。ラスタライザーはTEXCOORDやCOLORセマンティクスが付加された値が入力されると、その値を線形補間します(COLORを線形補間し、フラグメントを埋める処理はグローシェーディングまたはスムーズシェーディングと呼びます)。このフォンシェーディングを用いて、先ほどのフォンシェーダーをピクセルごとの法線で計算します。このシェーダーを「パーピクセルフォンシェーディング」と呼ぶことにします。C#スクリプトはフォンシェーダーのものをそのまま使えるので省略します。シェーダーは以下のようにしました。}}頂点カラーでシェーディングしなくなるため、COLORセマンティクスは不要になりました。normal:TEXCOORD1;という記述が増えています。説明は不要だと思いますが、positionはピクセル単位でViewSpaceの位置を得たかったため、ラスタライザーに線形補間させた値を使えるようにしています。normalもピクセルごとに法線を得たかったため、ラスタライザーに線形補間させた値を使えるようにしています(フォンシェーディング)。UnityでMatcapシェーダー書いたのでメモ。)はてなブログをはじめよう!es_programさんは、はてなブログを使っています。ブログを報告する引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。