3D表示で「等倍表示」を考える 後編

というわけで後編です。前編からの必要な情報だけまとめておきましょう。

ビュー変換(原点からZ軸方向にZeだけ後ろに下がった位置からZ軸正方向を向いている状態)

カメラ位置ベクトル (0,0,-Ze)
カメラ注視点ベクトル (0,0,Zp) Zp > -ZeとなるZpはすべて有効
カメラ上方ベクトル (0,1,0)

射影変換(ビュー変換後の座標を([-1 <= x’ <= 1],[-1 <= y’ <= 1],[0 <= z’ <= 1])となる直方体の中に入れる変換)

Y方向視野角 ¥theta 0 < ¥theta < ¥pi
アスペクト比 aspect aspect = width / height
近クリップZ座標 N = Zmin + Ze Zminはワールド座標上の最小Z座標
遠クリップZ座標 F = Zmax + Ze Zmaxはワールド座標上の最大Z座標
X方向変換率 Sx = ¥cot(¥frac{¥theta}{2}) ¥div aspect ¥cot¥theta = ¥frac{1}{¥tan¥theta}
Y方向変換率 Sy = ¥cot(¥frac{¥theta}{2}) ¥cot¥theta = ¥frac{1}{¥tan¥theta}
Z方向変換率 Sz = ¥frac{F}{F - N} = ¥frac{F}{Zmax - Zmin}

という定義で変換を行うとワールド空間上の座標(X,Y,Z)を射影空間上の座標(X’,Y’,Z’)に変換した時、

X’ = SxX / (Z + Ze) (-1 <= X’ <= 1)
Y’ = SyY / (Z + Ze) (-1 <= Y’ <= 1)
Z’ = (Sz(Z + Ze) – SzN) / (Z + Ze) (0 <= Z’ <= 1)

となる

というものでした。ここまでは復習です。

それぞれの変数の関連性を考えてみる

わかりやすく背景画のような画面いっぱいに表示される絵で考えてみます。背景画のサイズを(width,height)としてワールド空間上に座標をとって、

その変換された座標が射影空間座標で直方体のそれぞれの辺に張り付くことになります。そうすると、

位置 ワールド座標 テクスチャ座標 射影空間座標
左上 (-aspect * Y,Y,Z) (0,0) (-1,1,Z’)
右上 (aspect * Y,Y,Z) (width,0) (1,1,Z’)
左下 (-aspect * Y,-Y,Z) (0,height) (-1,-1,Z’)
右下 (aspect * Y,-Y,Z) (width,height) (1,-1,Z’)

という状態ができあがります。ワールド座標と射影空間座標の符号の付き方からX座標、Y座標とも正の値となる右上の座標の変換を使って対応を考えてみます。

Y座標の変換(X座標の変換)

Y座標の変換です。X座標の変換はアスペクト比が乗算されていて変換時に除算されるのでY座標の変換式と変わりません。

Y座標の変換式をちょっと変形してみると

Y¥cot(¥frac{¥theta}{2}) = Y’ ¥times (Z + Ze)

Y’ = 1より

Ze = Y¥cot(¥frac{¥theta}{2}) - Z

となります。

適当に値を決めて計算してみる

シンプルになるように値を決めてみます。Y = 1,Z = 1,Zmin = 0,Zmax = 10,¥theta = ¥pi / 3が適当でしょうか・・・。

thetaは三角関数との絡みになるので、直角三角形で有名な比率となる角度を持ってくると手計算がやりやすいです。

必要な値を計算してみます。

Ze = 1 ¥times ¥cot(¥frac{¥pi}{6}) - 1 ¥¥= ¥sqrt{3} - 1 Sy = ¥cot(¥frac{¥pi}{6}) ¥¥= ¥sqrt{3}
N = 0 + ¥sqrt{3} - 1 ¥¥= ¥sqrt{3} - 1 Sx = ¥frac{¥cot(¥frac{¥pi}{6})}{aspect} ¥¥= ¥frac{¥sqrt{3}}{aspect}
F = 10 + ¥sqrt{3} - 1 ¥¥= 9 + ¥sqrt{3} Sz = ¥frac{9 + ¥sqrt{3}}{10 - 0} ¥¥= ¥frac{9 + ¥sqrt{3}}{10}
Z’ = ¥frac{¥frac{9 + ¥sqrt{3}}{10} ¥times (1 + (¥sqrt{3} - 1) - (¥sqrt{3} - 1))}{1 + (¥sqrt{3} - 1)} ¥¥ = ¥frac{3 + 9 ¥sqrt{3}}{30}

Z’は変換後の座標を直接指定する時に前後関係が必要となるときにちょっと使います。描画座標をD3DFVF_XYZRHWで考える時です。

この状態を仮定してDirect3Dでプログラムを組んでみると

こんな感じですか。

D3DXMATRIX matProj; D3DXMATRIX matView; float aspect,sqrt3; sqrt3 = (float)sqrt(3.0); aspect = (float)width / (float)height;
D3DXVECTOR3 eye(0.0f,0.0f,-(sqrt3 - 1.0f)); D3DXVECTOR3 at(0.0f,0.0f,sqrt3 - 1.0f); D3DXVECTOR3 up(0.0f,1.0f,0.0f);
D3DXMatrixLookAtLH(&matView,&eye,&at,&up);
D3DXMatrixPerspectiveFovLH(&matProj,(float)M_PI / 3.0f,aspect,sqrt3 - 1.0f,sqrt3 + 9.0);
GetDirect3DDevice()->SetTransform(D3DTS_VIEW,&matView);
GetDirect3DDevice()->SetTransform(D3DTS_PROJECTION,&matProj);

としておくと、

typedef struct vertex{
	float fx,fy,fz;
	float fu,fv;
} VERTEX;
VERTEX v[4]; float texwidth,texheight;
v[0].fx = -texwidth / height; v[0].fy =  texheight / height; v[0].fz = 1.0f; v[0].fu = 0.0f; v[0].fv = 0.0f;
v[1].fx =  texwidth / height; v[1].fy =  texheight / height; v[1].fz = 1.0f; v[1].fu = 1.0f; v[1].fv = 0.0f;
v[2].fx = -texwidth / height; v[2].fy = -texheight / height; v[2].fz = 1.0f; v[2].fu = 0.0f; v[1].fv = 1.0f;
v[3].fx =  texwidth / height; v[3].fy = -texheight / height; v[3].fz = 1.0f; v[3].fu = 1.0f; v[1].fv = 1.0f;

という頂点座標を用いて描画を行うとテクスチャが中央に「等倍で」表示されます。 (texwidth / height = texwidth / width * aspectに注意)

3D空間上であってもテクスチャの等倍表示というのを考えることができるわけですね。ちょっと計算が面倒ですが・・・

この設定でほかの位置で等倍となる座標の計算は?

変換に必要な値はすでに決まっているのでこの場合の変数はY,Z,Z’のいずれかとなり、そのうちの1つを決めると等倍となる残りの座標は計算できることになります。

仮に(X2,Y2,Z2) => (1,1,Z2′)の変換でY2 = 2とすればY => Y’の変換式を使って

1 = ¥frac{¥sqrt{3} ¥times 2}{Z2 + ¥sqrt{3} - 1} ¥¥ Z2 = ¥sqrt{3} + 1 ¥quad (¥le Zmax)

となるので、(aspect * 2,2,√3 + 1)をワールド座標に使ってもテクスチャは等倍のまま描画ができます。

なお、Z2のところでZmax以下であることを確認していますが、ZがZmaxより大きくなると無限遠と見なされて描画されなくなりますので注意が必要です。

というわけで3D表示での「等倍表示」を考えてみました。

いまの表示に3Dを使っているADVシステムならばこれくらいはおそらく常識だと思いますが、どうなのでしょうか?

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

この記事のトラックバック用URL