Adobe Flash非公式テクニカルノート 3次元空間における面の向きを調べる
3次元座標空間で面を扱うとき、その向きを知りたいことがあります。面の向きは、面に対する垂線のベクトルで定められます。これを「法線ベクトル」と呼びます。なお、本稿では面をDisplayObjectインスタンス、または3頂点の3次元座標で表される3角形で考えることにします。 01 ベクトルの外積から法線ベクトルを導く 外積A×Bのベクトルの向きは、ベクトルAからBに右ネジを回して進む先になります(図001左図)[*1]。また、その大きさは、ふたつのベクトルAとBを2辺とする平行四辺形の面積に等しいです(図001右図)。なお、3次元空間におけるベクトルの外積について詳しくは、「Vector3D.crossProduct()メソッド」をお読みください。 図001■ベクトルの外積3頂点の座標をVector3Dクラスのインスタンスとして生成すると、外積はVector3D.crossProduct()メソッドを使って求めることができます。以下のスクリプト001は、そのやり方で法線ベクトルを導く簡単な計算例です。3頂点のxyz座標は、頂点0(0, 0, 0)と頂点1(100, 0, 0)および頂点2(100, 100, 0)です(スクリプト第1〜3行目)。また、座標はMatrix3Dオブジェクトの変換行列により、z軸で90度、y軸で45度、さらにx軸で45度回転します(スクリプト第4〜7行目)。 スクリプト001■3角形の3頂点から外積により法線ベクトルを求める
Vector3Dインスタンスの座標にMatrix3Dオブジェクトの行列変換を加えるのが、Matrix3D.transformVector()メソッドです。変換された座標をもつ新たなVector3Dオブジェクトが返されます(スクリプト第8〜10行目)。これらの新たな座標が納められた3頂点のVector3Dオブジェクトから外積により、行列で変換された3角形の法線ベクトルを求めます。 まず、頂点0から1と頂点0から2へのふたつのベクトルを求めます。2点を結ぶベクトルは、終点から始点を差引いて導きます。引き算のメソッドは、Vector3D.subtract()です(スクリプト第11〜12行目)。つぎに、得られたふたつのベクトルから、外積により法線ベクトルを計算します。3角形の3頂点は、0-1-2の順に時計回りです。面は初め、視点に対する手前向きと想定します(図002)。すると、右ねじの向きは視点から見て反時計回りですから、終点が頂点2のベクトル(vector3D_2)に対して、終点が頂点1のベクトル(vector3D_1)の外積を求めます(スクリプト第13行目)。 図002■3角形の面の向きは視点に対する手前向きとする 得られた外積のベクトル(vector3D_z0)は、面の向きを調べるうえではそのままでも問題はありません。しかし、大きさが1の単位ベクトルにしておくと、使いやすくなります。そこで、Vector3D.normalize()メソッドにより、単位法線ベクトルに直しました(スクリプト第14行目)。この法線単位ベクトルのVector3Dオブジェクトを[出力]すると、位置ベクトルとしてのxyz座標値は少し誤差を含むものの、(√2/2, 0.5, -0.5)になります(スクリプト第15行目)。
02 回転行列からz軸の方向を取出す それでは、回転行列で各座標軸のベクトルがどう変換されるかを、まず2次元平面で確かめてみましょう。2次元平面で原点(0, 0)を中心に角度θ回す回転行列は、つぎのような2次(2行×2列)の正方行列です。この回転行列に2次元の位置ベクトルを掛合わせると、座標が回転されます。変換行列と位置ベクトルの乗算について詳しくは、「変換行列を数学的に捉える」をお読みください。
x軸およびy軸がこの回転行列でどのように変換されるかは、それぞれの単位ベクトル(1, 0)と(0, 1)を以下のように掛合わせてみればわかります。回転行列にx軸の単位行列(1, 0)を乗じると、行列の第1列の成分である(cosθ, sinθ)が積つまり変換された位置ベクトルになります。y軸の単位ベクトル(0, 1)も同じように、回転行列の第2列の成分(-sinθ, cosθ)に変換されます。
これは何も行列計算をしなくても、x軸の点(1, 0)とy軸の点(0, 1)が角度θ回転したら単位円上のどの位置に移るかを三角関数で考えても同じ結果に至ります(図004)[*2]。 図004■単位円上のx軸の点(1, 0)とy軸の点(0 1)を角度θ回す 3次元座標空間の変換行列は、基本が3次正方行列になります。回転行列によるxyz軸の変換については、2次元平面のときと変わりません[*3]。回転行列の第1列の成分がx軸、第2列の成分がy軸の変換された単位ベクトルを示します。z軸の単位ベクトル(0, 0, 1)も、つぎのように回転行列の第3列成分に変換されます。
これで回転行列からz軸の単位行列がどの向きに変換されたかを知ることができます。つまり、面の向きがわかるのです。ただし、Matrix3Dオブジェクトを扱う場合には、注意がふたつあります。 第1に、Matrix3Dオブジェクトが表す変換行列は4次正方行列です。これは平行移動の変換が加えられるように、正方行列の次数をひとつ増やしたことによります[*4]。けれど、回転が3行×3列の9成分(a11〜a33)で定まることには変わりありません。
第2に、「面は初め、視点に対する手前向きと想定」しました。z軸は奥向きが正ですので、「手前向き」のz軸の単位行列は(0, 0, -1)としなければなりません。つまり、回転行列の第3列の成分から取出したz軸の向きを逆にする必要があるのです。具体的には、z軸の方向を示す位置ベクトルに-1を乗じます。 それでは、前掲スクリプト001に加えるかたちで、Matrix3Dオブジェクトから面の方向を示すベクトルを取出してみましょう(スクリプト002)。スクリプト001より少ない処理で、同じ結果が得られます[*5][*6]。 スクリプト002■回転行列から面の向きのベクトルを取出す
Matrix3Dオブジェクトが表す変換行列の全成分は、Matrix3D.rawDataプロパティで数値(Number)のVectorオブジェクトとして得られます(スクリプト第1行目)。成分は列の順序でVectorインスタンスのエレメントに納められますので、インデックス8〜10が変換行列の第3列の3成分になります(スクリプト第2行目)。Vector3Dオブジェクトの3次元ベクトルに数値(スカラー)を乗じるには、Vector3D.scaleBy()メソッドにその数値を渡します(スクリプト第3行目)。 得られたVector3Dオブジェクトを[出力]すると、スクリプト001とわずかな誤差はあるものの、ほぼ同じ値が表示されます(スクリプト第4行目)。なお、回転の変換は、座標の原点からの距離を変えません。したがって、単位行列の大きさも変わらないので、ベクトルの正規化は要りません。 スクリプト002の第5行目は、得られた面の方向のVector3Dオブジェクトをスクリプト001と比べています。メソッドVector3D.nearEquals()は、参照するVector3Dオブジェクトと第1引数のVector3Dオブジェクトとを、第2引数の許容値(誤差)の範囲で等しいかどうかを返します。
03 回転以外にも変換が加わった行列 つぎに、伸縮は一般に面の向きも変えてしまいます。変換行列の第3列目の成分で単純に面の方向のベクトルは定まりません。ただ、xyzの3軸すべてに同じ伸縮率を与えた場合のみ、面の向きは変わらず、変換行列の第3列目の成分が方向のベクトルを示します(ただし、正規化はされていません)。 作成者: 野中文雄 Copyright © 2001-2012 Fumio Nonaka. All rights reserved. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||