2018年3月18日日曜日

グルグル回転している長方形(Rectangle)と線(Ray)の当たり判定

3Dで回転しているキューブとRayの当たり判定の背後にある数式がいまいちよく分からなかったのでJavaScriptのCanvasを使って図解してみた。2Dも3Dもテストする軸(Axis)の数が変わるだけで内容は一緒。コードは以下より取得してもらいたい。
JSFiddle
GitHub(上に同じ)

数式の背後にある考えは以下。
Ray Box Intersection

以下図解。
右下のRay OriginがRayの起点になっている。directionでRayの方向を指定している。
const rayCast = new Ray(origin=new Vector(400, 400), direction=new Vector(-0.8, convertYUp(1)));

左上のオレンジと黄色の線で囲われている領域がくるくる回転しているRectangleとなる。
const obb = new RotatingRectangle(pos=new Vector(150, 200), size=new Vector(80, 50));

右下のRay Originから左上のRectangleに伸びている線(Vector)はRayとRectangleの距離を算出するためのものなので、投げかけているRayとは別物なので注意してほしい。
const vectorFromOriginOfRayToCenterOfRect = obb.pos.subtract(rayCast.origin);

当たっているとき(左下に「YES!」と出る)


当たっていないとき(左下に「NO Intersection」と出る)


この数式の肝はSlab(オレンジと黄色の線)とRayがいつ(Time)交わるかなので、Timeを算出しないといけない。Timeを求める式は以下。
Time = (Axis上に投影されたRay OriginとRectangle間の距離 +/- Rectangleのサイズ) / Axis上に投影されたRay Directionの長さ
const t1 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnXAxis + obb.size.x) / lengthOfDirectionOfRayProjectedOnXAxis;
const t2 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnXAxis - obb.size.x) / lengthOfDirectionOfRayProjectedOnXAxis;
const t3 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnYAxis + obb.size.y) / lengthOfDirectionOfRayProjectedOnYAxis;
const t4 = (lengthBetweenOriginOfRayAndCenterOfRectProjectedOnYAxis - obb.size.y) / lengthOfDirectionOfRayProjectedOnYAxis;

Timeを求める元々の式は以下。
+/- Rectangleのサイズ = Axis上に投影されたRay OriginとRectangle間の距離 + Time * Axis上に投影されたRay Directionの長さ
Slabの位置を求める式からTimeを求める式に変換すれば前述の式になる。

Dot Product
投影されたほにゃららの長さを求めるにはDot Productを使用すればよい。


~まとめ~
同一軸上に投影すれば後は単純な長さの比較になるのでTimeが求まると理解するまで悶々と苦しんだけど、今はスッキリ。こんな投げやりな解説で伝わるかははなはだ心もとないけれど、誰かの理解の一助になれば幸い。分からないことありましたらコメントください。