ホームページへ

「メビリンス」ポートフォリオ

河原電子ビジネス専門学校 ゲームクリエイター科2年

氏名:米地 真央




目次



1. 作品概要


目次へ


2. 担当ソースコード

●cppファイル、hファイル
●fxファイル
●全てのファイルのリファクタリングを担当

目次へ


3. 改造したエンジンコード

●cppファイル、hファイル

目次へ


4. ゲーム内容

 男性キャラクターがメビウスの輪のステージで、アイテムを裏側に投げて性質を反転させ、その性質を使ってゴールを目指すパズルゲーム。




目次へ


5. 操作説明

5.1. キーボード

5.2. Xbox 360 コントローラー




目次へ


6. 技術紹介

6.1. メビウスの輪の上の移動

 メビウスの輪の上を移動するには、重力の方向も移動する方向も常に一定ではないので、普通の移動では難しい。よって私はウェイポイントを使ってメビウスの輪の上の移動を実装した。ここでいうウェイポイントとは位置情報と回転情報を保持したデータのことを指す。そのウェイポイントをメビウスの輪の上に配置する。このゲームでは32個のウェイポイントが配置されている。



そして以下の手順で移動する。

  1. ウェイポイントのデータを順番に番号を割り振って読み込む。
  2. プレイヤーが何番のウェイポイントと何番のウェイポイントの間にあるか調べる。
  3. プレイヤーが右に進むときは右にあるウェイポイントにむかって進む。
  4. プレイヤーの回転は、左右のウェイポイントの回転情報を球面線形補完をして計算する。
  5. ウェイポイントを通り過ぎたら次のウェイポイントへ番号を変更する。

 しかし、この方法ではウェイポイントの間を直線的に移動することしかできない。さらに地面からも離れている。そうではなくメビウスの輪に沿って移動して欲しい。



 そのため、下向きのベクトルを補完済みの回転情報で回転させ、その方向にプレイヤーからレイを飛ばし、レイとメビウスの輪の交差点を求め(次項の4.2.メッシュとレイの交差点を求めるで説明)、その座標をプレイヤーの座標とすることでメビウスの輪に沿った移動にする。




つまり最終的な手順は

  1. ウェイポイントのデータを順番に番号を割り振って読み込む。
  2. プレイヤーが何番のウェイポイントと何番のウェイポイントの間にあるか調べる。
  3. プレイヤーが右に進むときは右にあるウェイポイントにむかって進む。
  4. プレイヤーの回転は、左右のウェイポイントの回転情報を球面線形補完をして計算する。
  5. プレイヤーから下向きにレイを飛ばしてメビウスの輪との交差点をプレイヤーの座標にする。
  6. ウェイポイントを通り過ぎたら次のウェイポイントへ番号を変更する。

となる。


目次へ

6.2. メッシュとレイの交差点を求める

 メッシュとレイの交差点を求めるには以下の処理手順で計算する。

  1. 三角形ポリゴンを含む無限平面とレイ(線分)の交差判定。
  2. 交差している座標の計算。
  3. 2で求めた交差点の座標が三角形の中にあるかどうか判定。
  4. メッシュの三角形ポリゴン全てに1~3の処理を行う。その中で、複数の交差点が存在したら、レイの始点から一番近い座標を交差点とする。
  5. 交差点が存在しなければ、交差していない。

それぞれ手順を詳しくみていく。

  1. 「三角形ポリゴンを含む無限平面と線分(レイ)の交差判定。」

    1. メッシュから三角形ポリゴンを一つ取り出して、三つの頂点の内どれか一つの頂点の座標、その頂点の法線(正規化済み)、レイの始点、レイの終点の四つのデータを使う。



    2. 頂点座標からレイの始点へのベクトルvertToStartVec、頂点座標からレイの終点へのベクトルvertToEndVecの二つのベクトルを求め、それぞれ正規化しておく。



    3. 頂点の法線と正規化されたvertToStartVecの内積normAndStartDotと、頂点の法線と正規化されたvertToEndVecの内積normAndEndDotを求める。



    4. normAndStartDotとnormAndEndDotの積が負の値だったら無限平面と交差しているので次の工程へ。0以上の値だったら交差していないので次の三角形ポリゴンを調べる。

      • 二つの内積の値の組み合わせが正と負のときに、無限平面とレイが交差してる。それ以外の正と正、負と負のときは交差していない。




  2. 「交差している座標の計算。」

    1. 無限平面とレイの交点とレイを使って図のような三角形を二つ作る。この二つの三角形は相似のため対応する辺の比が等しい。レイの始点と終点の座標は分かっているので、辺の比が分かれば無限平面とレイの交点の座標が求めることができる。



      辺の比を求めるために下の図の場所の辺の長さを調べる。



    2. 辺の長さを調べるために、まず三角形のどれか一つの頂点座標からレイの始点へのベクトルとvertToStartVec、頂点座標からレイの終点へのベクトルvertToEndVecの二つのベクトルを求める。今回は正規化しない。そして反転した頂点の法線も求めておく。



    3. 頂点の法線とvertToStartVecの内積normAndStartDotと、反転した頂点の法線とvertToEndVecの内積normAndEndDotを求める。これで頂点の法線に射影したvertToStartVecの長さと、反転した頂点の法線に射影したvertToEndVecの長さを求めることができる。



    4. この二つの長さは、図の位置の辺の長さと等しい。つまり、この二つの三角形の相似比はnormAndStartDot : normAndEndDotになる。



    5. 相似比が分かったので、レイの始点から終点へのベクトルstartToEndVecを求めて normAndStartDot / (normAndStartDot + normAndEndDot) を掛けるとレイの始点から交点までのベクトルstartToIntersectVecが求まる。



    6. レイの始点とstartToIntersectVecの和が無限平面とレイの交差点の座標になる。



  3. 「2で求めた交差点の座標が三角形の中にあるかどうか判定。」

    1. 今までは三角形ポリゴンを真横から見て考えていたが、次は真上から見て考える。2で求めた交差点の座標と、三角形ポリゴンの頂点座標三つのデータを使う。



    2. 三角形の頂点それぞれから無限平面とレイの交差点へのベクトルvert1~3ToIntersectVecを求める。そして三角形を時計回りに回るように、頂点1から頂点2へのベクトルvert1ToVert2Vec、頂点2から頂点3へのベクトルvert2ToVert3Vec、頂点3から頂点0へのベクトルvert3ToVert1Vecを求める。



    3. 三角形の頂点それぞれから伸びている二つベクトルを、終点が交点の方のベクトル(vert1~3ToIntersectVec)、もう一方のベクトル、の順番で外積を求めるcross1~3。そして、それぞれ正規化しておく。



    4. cross1とcorss2の内積dot1、cross1とcross2の内積dot2を求める。dot1とdot2が両方正の値だったら交点が三角形の中にあるため、レイと三角形ポリゴンが交差している。

      • 交点が三角形の中にあるときは三つの外積の値cross1~3がどれも同じ方向になる。



  4. 「メッシュの三角形ポリゴン全てに1~3の処理を行う。その中で、複数の交差点が存在したら、レイの始点から一番近い座標を交差点とする。」

  5. 「交差点が存在しなければ、交差していない。」


目次へ

6.3. OBB同士の当たり判定

  1. 分離軸を探す。
  2. 分離軸に二つのOBBを射影して射影線分を作る。
  3. 二つの射影線分が重なっていないか調べる。
  4. 1~3の処理を全ての分離軸で調べる。重なっていない射影線分が一つでもあれば衝突していないので処理を抜ける。
  5. 全ての分離軸で重なっていたら衝突している。

それぞれ手順を詳しくみていく。

  1. 「分離軸を探す。」

  2. 「分離軸に二つのOBBを射影して射影線分を作る。」

  3. 「二つの射影線分が重なっていないか調べる。」

  4. 「1~3の処理を全ての分離軸で調べる。重なっていない射影線分が一つでもあれば衝突していないので処理を抜ける。」

  5. 「全ての分離軸で重なっていたら衝突している。」


目次へ

6.4. レベルデータの読み込み

このtklファイルを以下の処理手順で読み込んでいく。

  1. tklファイルを読み込む。
  2. 読み込んだオブジェクト達のデータの、オブジェクト一つずつ取り出してデータを見ていく。
  3. オブジェクトの座標、回転、拡大率、オブジェクトネーム、のデータを取得する。
  4. このときフック関数が指定されている時はフック関数を呼び出す。このときフック関数の引数に、取り出したオブジェクトのデータを渡す。
  5. フック関数が指定されていない時、またはフック関数の戻り値がfalseだった場合、デフォルトの処理を行う。
  6. デフォルトの処理
    1. モデルデータのアセットを入れておくディレクトリから、オブジェクトネームと同じ名前のモデルを生成する。モデルがなかったらエラーを出す。
    2. モデルが生成できたら、静的物理オブジェクトをオブジェクトのメッシュの形から生成する。
  7. 全てのオブジェクトデータ分、2~6を繰り返す。

目次へ

6.5. ワイプ

 レベル遷移時に急に場面が切り替わるのではなく、ワイプで画像を表示してから、見えないように画像の後ろでレベル遷移を行うようにしている。ワイプの種類は以下の5種類あり、ランダムでワイプするようになっている。

  1. リニアワイプ



    1. リニアワイプでは、CUP側(C++)とGPU側(HLSL)の両方の処理で行う。

    2. CPU側では、現在どのくらいワイプしているかの変数wipeSize、ワイプする方向wipeDirを用意し、GPUに渡すようにする。

    3. wipeSizeを毎フレーム加算することでワイプを進める。

    4. GPU側では、wipeDirと描画するピクセル座標の内積をとり、wipeDirに射影したピクセル座標の長さwipeProjLenを求める。



    5. wipeProjLenがwipeSizeより大きかったら通常の描画をして、小さかったらワイプ用のスプライトを描画する。



  2. 円形ワイプ



    1. 円形ワイプでは、CUP側で、現在どのくらいワイプしているかの変数wipeSizeを用意し、GPUに渡すようにする。

    2. wipeSizeを毎フレーム加算することでワイプを進める。

    3. GPU側では、画面中央からピクセル座標への距離posFromCenterを求める。

    4. posFromCenterがwipeSizeより大きかったら通常の描画をして、小さかったらワイプ用のスプライトを描画する。



  3. 縦縞ワイプ



    1. 縦縞ワイプでは、CUP側で、現在どのくらいワイプしているかの変数wipeSizeを用意し、GPUに渡すようにする。

    2. wipeSizeを毎フレーム加算することでワイプを進める.

    3. GPU側では、画面の横幅 / 分割したい数 で分割された一つ分の横幅divideLengthを求める。

    4. ピクセル座標のX座標をdivideLengthで割った余りdividedPosを求める。これによって画面を縦に分割することができる。



    5. dividedPosがwipeSizeより大きかったら通常の描画をして、小さかったらワイプ用のスプライトを描画する。



  4. 横縞ワイプ



    1. 先ほどの縦縞ワイプとほぼ同じ。横縞ワイプではCUP側で、現在どのくらいワイプしているかの変数wipeSizeを用意し、GPUに渡すようにする。

    2. wipeSizeを毎フレーム加算することでワイプを進める.

    3. GPU側では、画面の縦幅 / 分割したい数 で分割された一つ分の縦幅divideLengthを求める。

    4. ピクセル座標のY座標をdivideLengthで割った余りdividedPosを求める。これによって画面を横に分割できる。



    5. dividedPosがwipeSizeより大きかったら通常の描画をして、小さかったらワイプ用のスプライトを描画する。



  5. チェッカーボードワイプ



    1. 縦縞と横縞のワイプを合わせたワイプ。チェッカーボードワイプではCUP側で、現在どのくらいワイプしているかの変数wipeSizeを用意し、GPUに渡すようにする。

    2. wipeSizeを毎フレーム加算することでワイプを進める.

    3. GPU側では、画面の横幅 / 分割したい数 で分割された一つ分の横幅divideLengthXと、画面の縦幅 / 分割したい数 で分割された一つ分の縦幅divideLengthYを求める。

    4. まずは、縦分割から考えていく。ピクセル座標のY座標をdivideLengthYで割って、ピクセル座標が偶数段目にいるのか奇数段目にいるのか調べる。



    5. 次に、横分割を考える。偶数段目の時は普通の縦縞ワイプと同じようにする。奇数段目の時は、ピクセル座標のX座標をdivideLengthXの半分ずらして縦縞ワイプをさせる。




目次へ

6.6. キューブマップスカイボックス

 このゲームでは空の表現にキューブマップスカイボックスを使用している。キューブマップスカイボックスとは、空用の球体のモデルのピクセルカラーを、球体の法線の先にあるキューブマップのカラーにする、というものである。詳しい手順は以下の通りである。

  1. キューブマップを用意する。キューブマップとは下の画像のように立方体の展開図のような形をした画像である。



  2. 空用の球体のモデルのシェーダーリソースビューにキューブマップを登録する。この時D3D12_SHADER_RESOURCE_VIEW_DESCのViewDimensionをD3D12_SRV_DIMENSION_TEXTURECUBEにしなくてはいけない。

  3. GPU側では、TextureCubeの型でキューブマップを受取って、球の法線を使ってサンプリングする。球の法線の先にあるキューブマップのテクスチャのカラーがそのピクセルのカラーになる。




目次へ

6.7. 川瀬式ブルーム

 ポストエフェクトの一つであるブルームを、川瀬正樹氏が発表した川瀬式ブルームを使用して実装した。
 普通のブルームはレンダリング後の画面から輝度抽出し、明るい部分にガウシアンブラーをかけて、もとの画面に加算合成するものだ。しかし、このやり方だとある程度明るい光は全て同じ半径のブルームになってしまう。
 一方、川瀬式ブルームはダウンサンプリングしながら複数回ガウシアンブラーをかけ、それらを平均化したものを元の画面に加算合成する。これにより大きい光はより大きい半径のブルームがかかるようになる。
 詳しい手順は以下の通りである。

  1. モデルなどを全てレンダリングする。



  2. レンダリング後の画面から輝度抽出をする。



  3. 輝度抽出したテクスチャをダウンサンプリングして、ガウシアンブラーをかけボケ画像を作る。ダウンサンプリングすると解像度が低くなるため、ガウシアンブラーの処理が軽くなる。(1280×720 -> 640×360)



  4. 3.で作ったボケ画像を、さらにダウンサンプリングしてガウシアンブラーをかける。(640x360 -> 320x180)



  5. 4.で作ったボケ画像を、さらにダウンサンプリングしてガウシアンブラーをかける。(320x180 -> 160x90)



  6. 5.で作ったボケ画像を、さらにダウンサンプリングしてガウシアンブラーをかける。(160x90 -> 80x45)



  7. 出来上がった四つのボケ画像を同じ解像度になるように拡大し、加算合成したあと平均を取り、それを描画する。




目次へ

6.8. VSM(分散シャドウマップ)

 VSM、分散シャドウマップとは、深度値のグループごとの局所的な分散を利用するソフトシャドウの一種である。深度値の局所的な分散が大きい場合、そのグループ内の深度値の幅が大きいということであり、つまり、影の境界線であることがわかる。影の境界線はジャギーが発生しやすいため、影を薄くするという手法。

▼グループごとに分けたシャドウマップ

▼分散が大きいグループが影の境界線になる

詳しい手順は以下の通りである。

  1. まず、シャドウマップを描画する。シャドウマップには深度値(ライトからピクセルまでの距離)と深度値の二乗を描き込む。深度値の二乗は分散を計算するときに使用する。



  2. シャドウマップをダウンサンプリングしながらガウシアンブラーを掛けて、ブロックごとのおおよその平均値を求める。これで局所的な深度値の平均値と、深度値の二乗の平均値を求めることができる。

  3. ガウシアンブラーを掛けたテクスチャをシャドウマップとして、シャドウレシーバーのシェーダーリソースビューに登録する。

  4. シャドウレシーバーで影の計算をする。

  5. ピクセル座標をライトビュースクリーン空間からUV空間に変換して、UV空間の座標がXとYそれぞれ0.0f~1.0fの値におさまっているか調べる。範囲外の影は落とせない。(VSMの機能ではないため詳しい説明は割愛)

  6. ライトビュースクリーン空間でのZ値がシャドウマップに描かれている深度値より大きかったら、ピクセルとライトの間に遮蔽物があるということになり、影を描画する。(デプスシャドウの機能。VSMの機能ではないため詳しい説明は割愛)

  7. チェビシェフの不等式を使ってピクセルに光が届く確率を求める。まずは深度値の分散を求める。分散は「二乗の平均 - 平均の二乗」で計算できる。二乗の平均は、シャドウマップにすでに「深度値の二乗の平均値」が描かれている。平均の二乗は、シャドウマップの「深度値の平均値」を二乗することで求まる。二つの値の差で求めた分散の値をvarianceとする。



  8. ライトを遮っている座標から影までの距離を出すために、ライトビュースクリーン空間でのZ値からシャドウマップに描かれている「深度値の平均値」引いた値を求めてmdとする。



  9. varianceとmdの値を利用して光が届く確率lit_factorを計算する。計算式は以下の通りである。

float lit_factor = variance / (variance + md * md);

varianceとmdとlit_factorの関係性は以下の通りである。

variancefit_factorの関係の表。(mdは0.5で固定)

variance
分散
variance / (variance + md * md)
計算式
lit_factor
光が届く確率
0.1 0.1 / (0.1 + 0.5 * 0.5) 0.285714
0.5 0.5 / (0.5 + 0.5 * 0.5) 0.666667
1.0 1.0 / (1.0 + 0.5 * 0.5) 0.8
3.0 3.0 / (3.0 + 0.5 * 0.5) 0.923077


variancefit_factorの関係のグラフ。(mdは0.5で固定)

mdfit_factorの関係の表。(varianceは0.5で固定)

md variance / (variance + md * md)
計算式
lit_factor
光が届く確率
0.1 0.5 / (0.5 + 0.1 * 0.1) 0.980392
0.5 0.5 / (0.5 + 0.5 * 0.5) 0.666667
1.0 0.5 / (0.5 + 1.0 * 1.0) 0.333333
3.0 0.5 / (0.5 + 3.0 * 3.0) 0.0526316


mdfit_factorの関係のグラフ。(varianceは0.5で固定)

variance(分散)が大きくなるとlit_factor(光が届く確率)が増大し、mdが大きくなるとlit_factorが減少する。
つまり、varianceが大きいほど影が薄くなり、小さいほど影が濃くなる。
そして、mdが大きいほど影が濃くなり、小さいほど影が薄くなる。


目次へ

6.9. アウトライン描画

 このゲームはポップな世界観のため、それを表現するためにシェーダーでモデルにアウトラインを描画するようにした。

▼アウトライン描画あり



▼アウトライン描画なし



描画するテクセルと近傍8テクセルの深度値や法線のデータを比べて、差が一定以上ある時はアウトラインを描画する。詳しい手順は以下の通りである。

  1. モデルのプロジェクション空間での深度値と、法線をテクスチャに描き込む。

  2. モデルを描画するときに1.で作ったテクスチャをデータを持ってくる。

  3. 今から描画するテクセルの深度値と、近傍8テクセルの深度値の平均、この二つの差の絶対値が一定以上ならアウトラインを描画する。



  4. また、深度値だけだとモデルの凹凸のアウトラインが、深度値の差が小さすぎるため、描画されない。



    そのため今から描画するテクセルの法線と、近傍8テクセルのそれぞれの法線とのどれかの内積が一定以下だったらアウトラインを描画する




目次へ

6.10. リムライトを使ったこのゲーム専用のシェーディング

 このゲームの世界観に合うように、シェーディングはリムライトの仕組みを利用して行っている。通常のリムライトとは違いライトの位置や方向は考慮せず、常にカメラから見た時のモデルの輪郭部分の光が強いようにしている。その光はキューブマップを利用して、空のカラーの光が少し映り込むようにしている。
詳しい手順は以下の通りである。

  1. アルベドカラーをサンプリングしてカラーを持ってくる。
  2. 空のキューブマップを、モデルの法線を使ってサンプリングしてカラーを持ってくる。
  3. 以下の式を使ってリムライトの強さを求める。
float limPower = pow( 1.0f - abs(viewNormal.z), 5.0f );

1.0f - abs(viewNormal.z)で、カメラ空間での法線のZ成分の絶対値が大きいほど0.0fに、小さいほど1.0fに近づく値になる。その値をpowを使って光の強さを調節している。

  1. キューブマップから持ってきたカラーに、今計算したlimPowerを掛ける。これで輪郭部分に近づくほど空のカラーが強く映り込むようになる。
  2. アルベドカラーに4.の値を足して最終的なカラーにする。

目次へ


7. ゲーム的にこだわったところ

7.1 透明アイテムの表現

 スイッチを押すまで触ることのできない透明アイテム。その透明アイテムの表現をただモデルを半透明にして描画するのではなく、モデルを透明にし、アウトラインのみ描画した後、そのアウトラインを更にディザリングすることでユニークな表現にした。



7.2 メビウスの輪の上での移動以外の動き

7.3 多彩なギミック


目次へ


8. 日本ゲーム大賞2021「アマチュア部門」応募時と、U-22プログラミングコンテスト2021応募時の内容比較

 以下からは、日本ゲーム大賞2021「アマチュア部門」に応募した「メビリンス」とU-22プログラミング・コンテスト2021に応募した「メビリンス」の比較内容を記述する。

8.1. 比較内容1(追加機能)

8.1.1. ステージごとにスカイキューブマップの変更

 改変前はどのステージでも同じスカイキューブマップを使用していたが、ステージごとにスカイキューブマップを変更するようにした。最初の方のステージでは穏やかな空にし、ステージが進むに連れてより壮大な空に変更し、雰囲気が盛り上がるようにした。

▼改変前のスカイキューブマップ

▼改変後のスカイキューブマップ

▼ステージ1,2

▼ステージ3,4

▼ステージ5,6

▼ステージ7,8

▼ステージ9


目次へ

8.1.2. IBLの追加

 スカイキューブマップの変更をより効果的にするためにIBL(イメージベースドライティング [Image-Based Lighting])の機能を追加した。
 改変前も、ライティングにスカイキューブマップのカラーの影響を受けていたが、よりきれいにスカイキューブマップのカラーが映り込むような計算にして、更にスカイキューブマップに依って明るさが変化するため明るさもステージごとに調整できるようにした。
 IBLの手順は以下の通りである。

  1. まずはCPU側(C++)の処理で、コンスタントバッファを利用して、カメラの視点eyePos、明るさluminance、どのくらい空のカラーの影響を受けるかのIBLRateの値をCPU側からGPU側に渡す。

  2. 次にシェーダーリソースビューとしてスカイキューブマップをCUP側からGUP側に渡す。

  3. GPU側(hlsl)の処理で、描画するピクセルのワールド座標posInWorldから視点eyePosへのベクトルfromEyeVを計算する。(fromEyeV = psInWorld - eyePos)



  4. fromEyeVとピクセルの法線normalとhlslのreflect関数を使って、反射ベクトルrefVを求める。(refV = reflect(fromEyeV, normal))



  5. スカイキューブマップのテクスチャをrefVを使ってサンプリングしてカラーを取ってくる。



  1. 最後にluminanceIBLRateの値を調節する。使用するスカイキューブマップのカラーによって、明るいカラーや暗いカラー、映り込みが目立つカラーや目立たないカラーがあり、それらを調節するためである。luminanceで明るさ、IBLRateで空のカラーの影響をどのくらい受けるかを調節する。

目次へ

8.1.3. ディファードレンダリングとフォワードレンダリングのハイブリッドレンダリングエンジンの追加

 改変前はフォワードレンダリングのみでライティングをしていたが、改変後はディファードレンダリングとフォワードレンダリングを組み合わせたライティングになっている。
 フォワードレンダリングは、モデルを描画するときに一緒にライティングも行うレンダリング方法だが、ディファードレンダリングはモデルを描画するときにライティングに必要なデータを書き出し、全部書き出し終わった後でライティングを行う方法だ。これにより、モデルの奥に隠れた見えないモデルの不必要なライティングをしなくて済む。
 しかし、ディファードレンダリングでは半透明描画ができない。半透明描画をするにはアルファブレンディングを行わなければならない。だが、ライティングに必要なデータを描き込むときに、アルベドカラーなどはアルファブレンディングできるが、法線などアルファブレンディングできないデータがあり、後にライティングするときにおかしな計算になってしまうためである。
 これを解決するために、ディファードレンダリングとフォワードレンダリングを組み合わせたライティングを行う。ディファードレンダリングを行った後、半透明描画をするモデルをフォワードレンダリングで描画する。
 詳しい手順は以下の通りである。

  1. まずは、MRT(MultiRenderingTarget)という機能を使い、G-Bufferと呼ばれる複数枚のテクスチャにライティングに必要なデータを書き出していく。このゲームでは、7つのG-Bufferを作成しており、それぞれ、アルベドカラー、法線、ビュー座標系の法線、ワールド座標系の座標、ライトビュープロジェクション座標系の座標、プロジェクション座標計の座標、自己発光色の内容になっている。



  2. 作成したG-Bufferを、ディファ―ドライティングを行うスプライトのシェーダーリソースビューに登録する。

  3. G-BufferをUVでサンプリングして、そのピクセルのライティングに必要なデータを取り出して、ライティングを行う。ここでディファードレンダリングの処理は終了



  4. ここからフォワードレンダリングを行う。フォワードレンダリングを行う際、深度ステンシルビューを、G-Bufferを作成した時に作られた深度ステンシルビューに設定する。これにより、後から描画してもモデルの前後関係が正しく描画される。




目次へ

8.2. 比較内容2(リファクタリング)

8.2.1. マジックナンバーの取り除き

 マジックナンバーとは、ソースコード内に直に書き込まれた数値である。マジックナンバーは書いた本人は、どんな目的の数値なのか分かっても、他の人から見たら何の数値なのかわからない。更に、書いた本人すらも時間が立てば忘れてしまい、何に数値かわらかなくなるかもしれない。
 また、同じ目的で同じ数値のマジックナンバーが複数個所ある場合、その数値を変更する時、一つ一つ変更していかなければならない。一部変更し忘れるミスをしてしまう危険性もある。
 以上のことからマジックナンバーを使用すると、可読性と保守性を下げてしまう。そのため、マジックナンバーを定数や列挙型に置き換えて、一か所にまとめておく改変を行った。

▼Player.cppの一部のコード


目次へ

8.2.2. namespaceの追加

 改変前はグローバルな名前空間に直書きだったコードを、改変後ではnamespaceの中に、コードの種類ごとに整理して入れた。名前空間中に入れることで、変数などの識別名の競合を限定的なものにすることができる。自分だけなら、識別名の競合ぐらい気をつけておけば大丈夫だと思うかもしれないが、他の人と作業するする時や、ライブラリを利用するときなどに、競合してしまう可能性が出てくる。

▼GameCamera.hの一部のコード


目次へ

8.2.3. 変数の初期化

 変数を初期化しなければ、想定外のエラーが出てくる可能性がある。初期化せずに宣言だけをした変数にもアドレスは割り振られ、そのアドレスには、宣言前に使用していたデータが残っている可能性がある。初期化していない変数を使用してしまったときに、何の値か分からないデータを使用することとなり、想定外のエラーが出てくる可能性がある。  改変前も気を付けていたが、初期化を忘れているところがあったため、修正した。

▼Player.hの一部のコード


目次へ

8.2.4. 暗黙の型変換をなくす

 暗黙の型変換は、どのように変換されるか理解しておけば問題ないように思えるが、ビルドするときに出力ウィンドウに警告が発生する。暗黙の型変換は注意しておかないと大量に発生してしまい、出力ウィンドウを圧迫してしまう。その結果、重要な警告を見逃してしまいやすくなってしまう。
 そのため、明示的にキャストを書いたり、関数のオーバーロードを増やしたりして、暗黙の型変換をなくすようにした。

▼暗黙の型変換による警告と警告がでた関数

▼関数のオーバーロードを増やし、暗黙の型変換を防ぐ


目次へ

8.2.5. 処理の長い関数の分割

 一つの関数の処理が長いと、見づらい上に、いろんな機能が一つの関数で行われているため何をやっている関数なのか分かりずらい。処理の長い関数は、処理ごとに細かく分けて関数を分割することで、バグが発生した時も確認する部分が限定でき、可読性と保守性に優れる。
 改変前は、気にせず同じ関数内にいろんな処理を書いてしまい100行を超える関数が沢山あったが、改変後は、処理の長い関数は、処理ごとに細かく関数を分割した。

▼Player.cppのStart関数


目次へ

8.2.6. dynamic_castによるダウンキャストの取り除き

 dynamic_castを使えば比較的安全にダウンキャストを行うことができるが、dynamic_cast事態の処理速度が遅いためあまり使用すべきではない。
 そのため、改変前では、dynamic_castでダウンキャストが成功した時にだけその派生クラスにしかないメンバ関数を呼んでいたが、改変後では、基底クラスに何も処理をしない仮想関数を作り、目的の派生クラスだけでオーバーライドを行うことで解決した。

▼Player.cppのStart関数


目次へ

リンク