基礎の基礎編
その2 小技集めておきました
ここでは、ベクトル演算を中心とした小技を集めてみることにしました。この章は何かあるたびに更新していく予定です。
・ 3Dで平行なベクトルの外積は?
・ ある点から平面までの距離
・ 2Dでのある点から直線までの距離
・ 斜面に立つには?
・ ポリゴン上に点が含まれるかを判定
・ 内分点
・ 2直線間の最短距離
2Dの外積は数値ですが、3Dでは法線ベクトルになります。2つのベクトルが並行ではない場合、法線は一意に決まりますが、平衡だったらどうなるのか?簡単ですから確認しておきましょう。
3Dで平行なベクトルは、互いに定数倍になっているので、
v1 = (x,y,z)
v2 = (c*x, c*y, c*z)
となります。この外積を計算すると、
v = v1×v2 = (y*c*z-z*c*y, z*c*x-x*c*z, x*c*y-y*c*x) = (0, 0, 0)
とゼロベクトルになってしまいます。大きさゼロのベクトルです。2Dの場合でも並行なベクトルの外積はゼロになってしまいますから、「平行なベクトルの外積の大きさはゼロ」ということで良さそうです。これは、平行なベクトルの判定に使えます。
3D空間のある位置から平面までの距離を求める作業は絶対に必要です。キャラクタを地面に立たせるのには、少なくともキャラクタと地面との鉛直距離が必要でしょう。
まず「平面」は、法線ベクトルと平面上のある1点で表現されます。ポリゴンであればそれを構成する頂点の座標からそれらを求めることも可能です。今法線ベクトルvnが下図のように平面上の1点P0から伸びているとしましょう(ベクトルは方向を表すので空間のどこにあっても本来はOKです)。一方空間内のある点をP1とします。始点P0から終点P1(x1,
y1, z1)へ向けたベクトルv1=(x1-x0, y1-y0, z1-z0)を作ると、P0を頂点として平面に鉛直な直角三角形がそこに見えてきます:
ここで思い出すのが内積。内積Dot(vn,v1)は|vn||v1|cosθと等しいのでした(θはvnとv1が作る角度)。一方、求めたいある点から平面までの距離dは|v1cos(θ)|です(直角三角形の底辺d/斜辺v1がcosθの定義です)。ここから、
d = |v1||cos(θ)|
d * |vn| = |v1||vn||cos(θ)|=|Dot(vn,v1)|
∴d = |Dot(vn,v1)|/ |vn|
で計算ができます。絶対値をつけないと、θが鈍角(P1が平面の裏にある)場合に答えがマイナスになってしまいますので注意です。
2Dである点から直線までの垂直距離を求めたいこともあるかもしれません。直線は始点となる点P0とそこから伸びるベクトルvがあれば定まります。
求めたい距離は上図のdです。これはv1を青いベクトルとすると|v1|sin(θ)に相当します。sin(θ)と来れば、これは外積です。結局、
d = |v×v1|/ |v|
が求める点と直線の距離となります。絶対値を付けて下さいね。
3Dのゲームで野山を駆け巡る時、平坦ではないポリゴン地面にキャラクタを垂直(重力方向)に経たせる必要があります。これを実現するには、斜めになった平面に対してキャラクタの立つ位置を求める必要があります。
まずは下の図をご覧下さい。
キャラクタは青い平面上のP1に立ちます。一方キャラクタの位置はXY平面である平面OのC(cx,cy,0)で表されるとしましょう。平面Oの法線ベクトルはGです。青い平面は法線ベクトルvと平面上の一点P0で表現できます。今求めたいのはP1の座標ですが、cx,cyはすでに与えますので、z1が本当に求めたい値です。
上の図で法線ベクトルvとP0を始点P1を終点とするベクトルv1は直角関係にあることに注目です。ベクトルが直角であるということは、その内積は0になります。すなわち、
v・v1=(vx*vx1 +vy*vy1 + vz*vz1)/|v||v1| = 0
です。右辺がゼロなので、ベクトルの大きさの項は消すことができますから、各成分の掛け算の項がゼロになれば良いわけです。今v1=(cx-x0, cy-y0, z1-z0)ですから、上の式に代入すると、
vx*(cx-x0) + vy*(cy-y0) + vz*(z1-z0) = 0
z1以外は全て分かっている値なので、左辺にz1を残せば、欲しい立ち位置P1(cx, cy, z1)が決定します。
z1 = z0 + (-vx*(cx-x0) - vy*(cy-y0)) / vz
このことから、キャラクタの位置をXY平面Oで定めておけば、その立ち位置というのは一瞬で求まることがわかりますね!ただし、1つ注意があります。平面上の1点P0が(cx, cy, z1)と重なってしまうと、上式では計算ができません。そういう場合は平面上の別の点を基準にして下さい。
「斜面に立つには?」では平面上に立つ方法を述べましたが、平原などデコボコした大地(ポリゴン)を想定する場合は、キャラクタがどの平原ポリゴンの上にいるかを判定しなければなりません。
平面を確定するために、ここでは三角ポリゴンに限定します。下の図をご覧下さい。
三角ポリゴンの辺を左回りにベクトルが囲っています。こうすると、このポリゴンの法線ベクトルは画面の奥からこちら側へ向かう「右手系」になります。さて、点がポリゴンに含まれている場合、色分けしたように、辺のベクトルから見て内側のベクトルが必ず左回りになります。しかし、点が外にある場合、茶色の矢印のように右回りが発生します。
左回りの場合、法線は画面の奥からこちら側になるのに対して、右回りではこちらから奥へ向かいます。つまり、色分けしたベクトルの外積を計算して、元のポリゴンと法線が同じが異なるかで、点が含まれているかどうかを判定できます。計算方法は単なる外積なので省略します。これは、左手系にした場合でも同じように判定ができます。
内分点とは線分を分ける点です。簡単なのでここでまとめておきます。線分の両端の座標をP0(x0,y0,z0)、P1(x1,y1,z1)としておきましょう。求めたい内分点はP2(x2,y2,z2)と置きます。今、
(P0P2間):(P1P2間)=a: 1-a, 0≦a≦1
と置きます。原点を始点、これらの点を終点とするベクトルをv0,v1,v2としておきます。この時v2は、
v2 = (1-a)*v0 + a*v1
と計算することができます。これが内分の式です。この式をちょっと変形すると、
v2 = v0 + a(v1-v0)
v2-v0 = a(v1-v0)
から、
|v2-v0| = |a(v1-v0)|
a = |v2-v0| / |v1-v0|
とすることで、線分上にある3つの点から内分比も求めることができます。
空間に引いた2直線の最短距離を考えて見ます。これは、実は筒同士の衝突判定で必要になります。どうするのか?最短距離とは片方の直線から垂直にもう片方の直線に降りればよいのです。つまり、その降りる線は、2直線の方向ベクトルからなる法線です。
らしく描いてみました。緑と青のベクトルv1,v2がそれぞれの直線の方向を表します。青い点は直線上の一点です。ポイントはv2を含んでいる平面。これは法線ベクトルnとP2があれば定まります。法線ベクトルnはv1とv2の外積から求まりますね。つまり、2直線の最短距離は空間上の点P1から平面までの距離の問題に帰着します。これ、実はこの章内の「ある点から平面までの距離」でもうやっているんですよね。
まとめると、
P1(x1, y1, z1), P2(x2, y2, z2)とし、
ベクトルv12=P2-P1、法線ベクトルn = v1×v2とすると、
求めたい最短距離dは、
d = |n・v12|/|n|
と計算できます。法線を標準化すれば、さらに
d = |n・v12|
と簡略化できます。