シェーディング

 立体の描画の所で描画した球体に陰影を付けます。ライトの光が当たっている部分の光沢やその反対部分に行くにつれて徐々に暗くなっていく影の表現など、ちゃんとした球体に見えます。こうした陰影を付ける作業をシェーディングと言います。シェーディングを行うためには、球体の情報の他に視点と光源の情報、最低でもこの3つの情報は必要になります。基本的な原理はとてもシンプルです。光源から球体上の一点を結ぶ直線と、視点から同じ球体上の一点を結んだ直線との間に出来る角度が狭いほど明るくなり、角度が広くなると暗くなります。特に90°を超えると、光が当たらない真っ暗な状態になります。それらの計算はベクトルの内積で求めます。

今回はシェーディングの中でもフォンシェーディングという種類の実装を行いました。ここで使われた数式は、上記に書かれているものですが、Wikipediaの「Phongの反射モデル」のページにある式をそのまま使いました。パラメータのそれぞれの意味は、Wikipediaに書かれている通りです。パラメータの値は、適当に設定しました。計算式は、3つの項に分かれていますが、左からそれぞれ、環境反射光、拡散反射光、鏡面反射光を計算しています。それぞれの反射光のみを球体に適用した時の画像が以下になります。この3つの反射光を単純に足し合わせた結果が、ページ一番上にあるトップ画像になります。

環境反射光

拡散反射光

鏡面反射光

​コード解説

① 構造体の定義とレイアウト
 頂点情報として、座標値の他に法線情報を持たせます。それに伴い頂点レイアウトも変更されています。また、フォンシェーディングを計算するために、定数バッファのメンバが増えました。

1行目から4行目では、頂点情報を格納する構造体を定義しています。メンバは、「V」が頂点座標で、「N」が法線ベクトルになります。ここでの頂点情報を格納する構造体と頂点レイアウトの関係は「頂点カラー」で説明をした通りです。6行目からの構造体で定義されているメンバは、DirectX側に送られて使われるものです。各メンバの内容は以下の通りです。

 

  ・mW     : ワールド座標変換のための行列

  ・mWVP    : ワールド座標変換から透視変換までの行列

  ・vLightPos : 光源の位置

  ・vEyePos  : 視点の位置

  ・vColor   : 球体の色

​これらのメンバを受け取るための構造体を、シェーダファイルの方でも定義します。

② シェーダファイル内の構造体と頂点シェーダ
 頂点シェーダ及びそこで使われる構造体等の定義を以下のようにします。

1行目から7行目に書かれているのは、定数バッファで送られてきた値を受け取る変数です。それぞれ、宣言している順番も同じように対応しています。9行目からの構造体は、頂点シェーダからピクセルシェーダへ渡すためのものです。頂点情報の他に光源の位置、法線、視点の位置情報を保持します。

 16行目からは、頂点シェーダを定義しています。やっていることは、19行目で頂点情報を透視変換までの座標変換をしています。20行目の法線に対しては、ワールド座標変換のみを行っています。理由としては、基本的にシェーディングの計算は、ワールド座標内で行うためです。光源と視点の位置情報は、ワールド座標の値なのでそのまま代入しています。

サンプル​コード

< 動作確認環境 >
 ・Windows10 Pro
 ・Visual Studio Community 2017


基本的には空のプロジェクトを作ってもらい、以下のソースコードをコピペするだけでうまく実行できるかと思います。もしうまくいかない場合がありましたら、サンプルコードの実行に関する注意事項を一度参照してみてください。

​ソースコード

③ ピクセルシェーダ
 ピクセルシェーダ内では、フォンシェーディングの計算を行い、最終的な色を算出してします。

このピクセルシェーダ内で行われている各行の計算は、ページ上部にある式を利用するためのものです。3行目から8行目までで各パラメータの計算を行い、9行目から11行目の各行で、環境反射光、拡散反射光、鏡面反射光を計算しています。

 ポイントとなる部分、というより裏技に近いですがそれは3行目になります。この3行目で計算しているパラメータ「L」は、球体上から光源へと延びるベクトルを求めています。本来であれば、光源の位置から球体上の座標を引かなくてはいけません。ですが、ここでは法線が使われています。法線で大丈夫な理由は、今回用いた物体の形が球体だからです。半径が1で、なおかつその中心座標が原点の球体は、表面の座標値と法線の値が等しくなるからです。シェーディングの計算は、同じワールド座標内で行われなければならないため、今回はこのような手法を取りました。

シェーダのソースコード (shader.hlsl ※必ずこの名前で保存してください)