直線と点の距離
三次元形状を扱うプログラムを作成している場合、直線と点の距離を求める処理が必要になることが多々あります。 例えば、マウスクリックによってオブジェクトやその頂点を選択できるようにしたい場合などがそうでしょう。 二次元空間内であれば、直線と点の距離は
などに掲載されている式で求めることができますが、これを三次元に拡張した場合、平面と点の距離を求めることになってしまうため、目的の用途に使うことはできません。 この問題を正しく解くためには、直線と球の交点を求める式から出発する必要があります。 以下、図と式を簡略化するため、二次元内の直線と円の交点を求める方法をもとに解説を行っていきますが、最終的に得られる式の形に違いはないので、次元の違いには拘らずに以下を読み進めてください。
まず、直線 L を、それが含む任意の点 p (px, py) と、方向ベクトル d (dx, dy) および、媒介変数 t を用いた以下の式で表現します。
[2] y =dy t + py
続いて、円 C を、その中心点 c (cx, cy) および半径 r を用いた以下の式で表現します。
[1] および [2] を [3] に代入すると、
となり、これを t について整理すれば、
というお馴染みの二次関数が得られます。 ここでさらに、p - c を g (gx, gy) と置き換えると、
と記述することができるように。 これで、三次元でも同じ式を使って計算できるようになりました。
覚えていますか「判別式」
さて、この方程式に二次方程式の「解の公式」を当てはめる前に、解が持つ図形的な意味を考えてみましょう。 特別な考察をせずとも明らかなように、直線 L と 円 (球) C は、その位置関係に応じて、0, 1, または2点で交差します。 そのうち、解がただ一つに定まるケースは、両者が「接している」状況に対応。 このとき、その接点と C の中心点 c を結ぶ線分は、L に対して垂直であり、かつ、その長さは C の半径と等しくなります。 従って、このときの C の半径 r こそが、直線 L と 点 c の「距離」に他なりません。
解が一つに定まる必要にして十分な条件は、「解の公式」の根号の中 (いわゆる「判別式」) が 0 であることなので、
直線 L の方向ベクトル d を正規化しておけば、|d| = 1 となり、この式を以下のように簡略化できて嬉しいので、是非やっておきましょう。
これを r (≥ 0) について解けば、
が得られます。
「深度」も計ろう
[5′] 式を [4′′] に代入すると、
となりますが、この方程式は重解をもつことが分かっているため、苦もなく以下の形に因数分解することができます。
従って、以下の解が得られます。
これで求められた t の値は、点 c から直線 L に降ろした垂線の足が、点 p から見て、方向ベクトル d の「何倍」(この係数は当然スカラー量になる) に相当するか、を表します。
この計算は、最初に挙げた「オブジェクトをマウスクリックで選択する」などの処理を行う際に必要となります。 それは、そうした操作では通常、クリックした点から一定の半径内にある頂点のうち最も「手前」にあるものを選ぶわけですが、どれが最も「手前」、言い換えれば視点 p に近いかを判定するための基準 (視界深度) が、この t の値に他ならないため。 値が負になった場合は、対象点が視点の「後方」にあることを意味します。
この処理がイイカゲンだと、手前のオブジェクトを無視して奥のオブジェクトを選択してしまったり、視点の背後にある「見えない」オブジェクトを選択してしまったりと、色々おかしな現象が発生します。 また、ゲームで直線状に飛ぶ弾丸やビームなどの当たり判定などを行う場合にも、射出点から最も近い標的を探し出すために同様の処理が必要になりますが、ここの判定がきちんとできていないと、手前の敵をすり抜けて奥の敵に着弾したり、背後のエフェクトが発生していない場所にいる敵が突然爆発したりして、某所へ作品がノミネートされる可能性が出てくることでしょう。
コメント