今回は、一息ついて魚眼レンズです。 単純な頂点シェーダープログラムなので、すぐにおわかりになられるでしょう。
まぁ、いつものように適当にファイルが入っています。
| vs.vsh | 頂点シェーダー。 |
| ps.psh | ピクセルシェーダー。 |
| draw.cpp | メインの描画部分。 |
| draw.h | 描画の各関数の定義。 |
| bg.cpp | 背景の描画。 |
| main.h | 基本的な定数など。 |
| main.cpp | 描画に関係しないシステム的な部分。 |
| load.cpp | ロード。 |
| load.h | ロードのインターフェイス。 |
あと、モデルのファイル、実行ファイル及び、プロジェクトファイルが入っています。
魚の目は、横に付いています。
従って、見える角度が非常に広くなっています。
魚のに見える景色を一枚の写真に焼き付けようとしたレンズが魚眼レンズです。
一番上に出ている画像を普通に(視野角60度で)レンダリングした場合には、次のようになります。
Pr は、次の式を連立させて求めることが出来ます。
Pr = Pz tanθ
1
Pz = - (1-Pr2)
2
(X,Y,Z) が、頂点の位置です。
Pz を消すように連立すれば、Pr は、
1
Pr = - (1-Pr2) tanθ
2
↓Prの冪乗でまとめて
tanθ Pr2 + 2Pr - tanθ =0
↓2次方程式を解く
-1+sqrt(1+tan2θ)
Pr = ――――――
tanθ
-cosθ + 1
= ――――――
tanθ cosθ
1 - cosθ
= ――――――
sinθ
に、なります。
この式を代入すると、
1 - cosθ
X射影空間 = ――――― cosφ
sinθ
1 - cosθ
Y射影空間 = ――――― sinφ
sinθ
に成ります。さて、それぞれの角度は、頂点座標で、
x y
cosφ = ―, sinφ = ―
Pxy Pxy
z Pxy
cosθ = ―, sinθ = ―
d d
Pxy2 = x2 + y2,
d2 = x2 + y2 + z2,
と書けます。射影空間の位置に関して、これらの値を代入すると、
1 - (z / d) x
X射影空間 = ―――――― ―
(Pxy / d) Pxy
(d - z) x
= ―――― ―
Pxy Pxy
x
= ―― (d - z),
Pxy2
y
Y射影空間 = ―― (d - z),
Pxy2
になります。これが欲しかった量です。
一つ忘れてはいけないことは、これはカメラから見た位置です。
座標はビュー座標系で処理する必要があります。
今回、特別なプログラムは、頂点シェーダの座標変換しかありません。
vs.vsh
0001: ; c0-3 -- world + ビュー 行列
0002: ; c12 -- {0.0, 0.5, 1.0, 2.0}
0003: ; c13 -- ライトのベクトル (w成分は環境光の強さ)
0004: ; c14 -- モデルの色
0005: ; c15 -- 深度調整
0006: ;
0007: ; v0 頂点の座標値
0008: ; v7 テクスチャ座標
0009:
0010: vs.1.0
0011:
0012: ;座標変換
0013: m4x4 r0, v0, c0
0014:
0015: dp3 r0.w, r0, r0
0016: rsq r1.w, r0.w
0017: mul r0.w, r0.w, r1.w
0018: mov oPos.w, r0.w ; oPos.w = r0.w = d
0019:
0020: mad oPos.z, c15.x, r0.w, c15.y ; oPos.z = z_max*(d-z_min)/(z_max-z_min)
0021:
0022: add r1.w, r0.w, -r0.z
0023: dp4 r1.z, r0.xxyy, r0.xxyy
0024: rcp r1.z, r1.z
0025: mul r0, r0, r0.w
0026: mul r0, r0, r1.w
0027: mul r0, r0, c12.w
0028: mul oPos.xy, r0, r1.z ; oPos.xy = r0.xy*d*(d-r0.z)/(v0.x^2+v0.y^2)
0029:
0034: ; ((L・N) + L.w)*c14 (平行光源のライティング)
0035: dp4 r0.w, v3, c13
0036: mul oD0, r0.w, c14
0037:
0038: ; テクスチャーを張る
0039: mov oT0, v7
最初にビュー空間に座標変換します。
この変換によって、カメラを原点に置いた座標系に移動します。
射影空間に出力する座標は、次の通りです。
zmax d - zmin Z射影空間 = ― ―――― 1 zmax- zmin
を使います。ただし、0~1に補正されるのは、w成分で割った値です。
x、y成分に関しては、先ほど説明した座標を、w成分で割られるときを考え、w成分の値を掛けた座標、
x
X射影空間 = d ― (d - z)
Pxy2
y
Y射影空間 = d ― (d - z)
Pxy2
を、出力します。
特別な方法といえば、xy平面での距離を計算するのに、内積計算で求めている
0023: dp4 r1.z, r0.xxyy, r0.xxyy
r1.z = r0.x*r0.x + r0.x*r0.x + r0.y*r0.y + r0.y*r0.y
= 2 (r0.x2 + r0.y2)
ぐらいでしょうか。
あまり、最適化は考えていないので、もっと高速化は出来ると思います。
いや、勿論あれへの前振りですよ。
といっても、まだ作っていないので、出来るかは不明ですが。
次回のネタが違ったら、間に合わなかったんだと思ってください。