CustomRenderTargetその5

今回はサンプルのmaptest.rbの話。名前がひどい気味なのはいつもの話なので気にしない。でもさすがにそのうち変えるかも。それはそれとして、動かすとこのような画面が出る。

ちなみに壁との衝突判定は面倒なのでやっていない。外側に出てどこまでも行ける。

迷路情報

classic_sampleのmeiro.rbと同じマップを使っている。これを元に3Dのワールド座標系の座標を作って頂点バッファに詰め込む感じである。迷路の頂点は通路上にいる場合に見える向きのポリゴンだけ設定していて、これを1つの頂点バッファに入れているので、描画は1回となる。この方式は簡単だが、視界に入らない頂点も頂点シェーダに投入されるので、頂点数が多い(世界が広いとか壁などの頂点数が多いとか)の場合に頂点シェーダの負荷が増える。マップをある程度のサイズで区切って視界に入りうる範囲だけを描画するとかすると軽くなる。
ワールド座標系は画面のマップ描画に合わせて1マス16というサイズ感となっている。これは別にいくつにしても構わないのだが、マップ上に描画範囲の台形などを描画するのに座標が共有できるとラクなので合わせてみた次第。2Dのマップはxyになるが、3Dの座標はyが縦になるのでxz平面がマップと一致する方向となる。これも別にxy平面を合わせてもいいっちゃいいのだが、まあサンプルなので普通の3D座標系を使うようにした。ちなみに3D上の迷路は2Dマップの上のほうを向こう側にバタっと倒したようなイメージになるが、それだと画面ではyが下に+なのに対し、3Dはzが奥に+になるので、そこだけ逆転させている。

3D描画

今回のサンプルでは頂点バッファには壁の頂点位置しか入れていない。色は白固定で、ライティングをしないので法線も無い。これでどうやってこういう描画をするか、という話なのだが、頂点バッファの出力にワールド座標系の座標をセットしていて、これがTEXCORDでピクセルシェーダに渡されるときに頂点間が補間される。ようはピクセルシェーダにはワールド座標系でのピクセルの位置が渡されてきて、あとはRuby側から渡した自分の位置(vJibun)との距離を算出して遠いほど暗くする、という寸法である。フォグと呼ばれる手法となる。
壁を正面から見たとき、真ん中が一番明るくて、端に行くほど暗くなる。これは単純に直線距離を算出しているからであり、斜めのほうが遠いという話。これにより微妙な明暗が生まれてなんとなく迷路の形状が見えるようになっている。テクスチャ貼ってしまえば綺麗でリアルになるんだろうけども。

マップ上に見えているもの

マップには3Dの視界に入る範囲とカメラの位置を描画してみた。この台形が視錐台であり、カメラから短いほうの辺までがZn、長いほうの辺までがZf、ということになる。と思う。自分の位置は短いほうの辺の真ん中であり、そこにカメラを置くと視野角180度とかになってしまうのでカメラは1マスぶん下げている。こういうのは試行錯誤して見た目を調整するのか、それとも鉄板のパラメータがあるのかはよく知らないのだが、とりあえず適当にそうしてみた。
また、台形の短いほうの線は視錐台の近いほうなので、これが3Dの描画面となる。このサイズと位置を決めているのがプロジェクション変換行列で、

# プロジェクション変換行列
# ワールド内で16*12サイズの描画面を作り、カメラ位置から前に16の距離に置くイメージ。奥はカメラから64の距離までを描画。
rt3d.proj = Matrix.projection(16, 12, 16.0, 64.0)

というやつである。横16、縦12でカメラから16の距離、奥は64の距離まで。すべてワールド座標系での表現となり、ワールド内のこのサイズがCustomRenderTargetのサイズに拡大されることになる。

奥行きと距離の話

3Dの描画では物体のサイズは遠いほうが小さくなる。色は距離で見ているので端のほうが暗い。ということは、端のほうが小さく描画されないといけないのではないか。描画範囲の台形を眺めながらそんなことを思う人は一定割合で存在していると思うが、このへんの話は地味にややこしい。そうでもないか。
あなたがもし部屋の中でコレを読んでいるなら、部屋の壁を正面から眺めてみるとよい。壁の一番上、天井と繋がっている線は、直線に見えているだろうか。見えている人は正しい。曲がって見えている人も正しい。壁を正面から見たとき、一番近いところが一番大きく見えて、遠いほうは小さく見える、つまり連続的な曲線に見えるのが実際のところだが、脳がそれを補正してまっすぐに感じさせているのだ。
このへんを「正しく」描画するサンプルはDXRubyのnew_sampleに入っているraycast.rbなのだが、これで壁に近づいて正面から見ると歪んで見える。まったくおかしい描画に見えるのは、画面のソレを脳が補正しないから、というわけだ。なので3Dの描画は壁がまっすぐ描画されるように、カメラからの距離じゃなく奥行きを拡大率に使う。まあ、カメラからの距離を拡大率に使ったら大きなポリゴンが使えなくなるしな。

移動とか回転とかを既定回数繰り返すためにprocを使ってみた。何かサンプルを作るたびにやりかたが変わっているが、こういうののやりかたは無数にあると思われるので、作るそのときに良いと思った方法を採用すればいいんじゃないかな。日々勉強、日々実践。人間は学習して成長する生き物なのでいつもやりかたが同じほうがおかしいのである。知らんけど。
今後もCustomRenderTargetでできること、やってみたいことを試して行きたい所存である。