はじめに
パースのかかった立方体を描画するプログラムをJavaで作ってみました。プログラミングには、Java3DなどのAPIは使用せず、ホームページなどに貼り付けて簡単に実行できる、Javaアプレットで作成しました。
陰面消去の処理は、凸な立体図形を1つだけ描画する前提で省力化しました。その代わり、透視投影変換でパースをかけて描画し、マウスによる回転・移動・ズームのオペレーションを加えました。
以下に、その実行画面を示します。
対象読者
Javaと線形代数(高校程度)の基礎知識があり、3次元CGに興味のある人。
必要な環境
開発には以下の環境を使用しました。
- Java2 SDK, Standard Edition Version 1.4.1_03(Microsoft Windows版)
また実行環境として、Javaアプレットを実行できるWebブラウザが必要です。
陰面消去
「陰面消去」とは、見えない部分を描画されないようにする処理のことです。今回は描画対象が立方体、すなわち凸な多面体のため、面の法線ベクトルと視線ベクトルを使用した背面消去を利用します。
ここで、面の裏側が視点に向いているとき、法線ベクトルnと視点から面への視線ベクトルeの成す角θは90°より小さくなります。したがって、cosθ>0.0のとき、つまりnとeの内積が0より大きい場合、面は裏側を向いているため、描画する必要はありません。
立方体の場合、視点から見えるのは、表を向いている面だけなので、n・e>0.0となる場合は、面のレンダリングを行いません。
これについて、もう少し詳しく説明します。立方体の6つの面で、視点側に見えるのは多くとも3面までです。このとき、視点側を向いているのは、いずれも表側、つまり法線ベクトルが視点側を向いている面で、残りの面はすべて、これらの面で隠されます。これは、直感的にも分かると思います。したがって、立方体の陰面消去の処理は、背面消去で十分ということになります。これは、凸な多面体ならどのような立体図形でも同じで、zバッファ法などで奥にある面を隠す処理が必要になるのは、2つ以上の立体図形を描画する場合や、表側の面が他の面に隠れるような凹んだ部分のある立体図形を描画する場合だけとなります。
ところで、視点から面への視線ベクトルとは、どのようなベクトルでしょうか。サンプルプログラムでは、視点から面の重心へのベクトルとしていますが、視点から面上の任意の点までのベクトルでかまいません。平らな長方形の板を見ながら考えていただければ分かると思いますが、面の表側を向いているときは、視点から面の任意の点へのベクトルと面の法線ベクトルの成す角は90°以上となっています。これを、だんだん寝かせていき、視点が、面の延長線に来るように動かしてやると、いずれの点へのベクトルも、面に平行、つまり、面の法線ベクトルに垂直になっていきます。そして、視点が完全に面の延長線に来ると、いずれの点へのベクトルも面に平行になります。視点が、面の裏側にあるときも、同じように考えられます。
背面消去の処理を、Javaで書いたソースコードは、以下のようになります。ここで、Vector2d
は2次元ベクトルクラス、Vector3d
は3次元ベクトルクラス、Matrix4x4d
は4×4行列クラス、modelM
はモデル変換行列、rotM
はモデル変換の中の回転変換の行列です。
// M:モデル変換行列、c:面の中心点、Mc:モデル変換後の面の中心点 Vector3d Mc = modelM.multiplyVecRight(center); // n:回転変換後法線ベクトル(n' = Rn) Vector3d n = rotM.multiplyVecRight(normal); n.normalize(); // e:視線ベクトル(e' = Mc - e) Vector3d e = Mc.subtract(eyePos); e.normalize(); // 法線ベクトルと視線ベクトルの成す角が90°以内 // のときはレンダリングをしない // // n e // |θ / // | / // 表 |/ // -------------- // 裏 / // / // eye // 視点 if (n.dot(e) > 0.0) { return; }