「Moller-Trumbore」アルゴリズムです
コード説明
Unityでメッシュの内部からのRayで、交点を取得する必要があったので自前で実装しました。上の方の関数がメッシュフィルターとの判定で、下が一つの三角形との判定です。引数seeFront,seeBackを変更することで、用途に応じて使い分けられます。
参考文献
これらを読んだらコードの中身が理解できます。複雑な計算の実装に関しては(あー大学の授業でやったわこんなん)みたいな感じで、参考文献のものをコピペしました。これを読んでるあなたも、私のコードをコピペするか、wikiのをコピペするといいと思います。
https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
https://shikousakugo.wordpress.com/2012/05/29/ray-intersection/
https://shikousakugo.wordpress.com/2012/06/27/ray-intersection-2/
https://shikousakugo.wordpress.com/2012/07/01/ray-intersection-3/
コード
using UnityEngine;
//https://en.wikipedia.org/wiki/M%C3%B6ller%E2%80%93Trumbore_intersection_algorithm
//https://shikousakugo.wordpress.com/2012/05/29/ray-intersection/
//https://shikousakugo.wordpress.com/2012/06/27/ray-intersection-2/
//https://shikousakugo.wordpress.com/2012/07/01/ray-intersection-3/
public static class MollerTrumboreRayChack
{
public static bool GetRayIntersectMesh(Vector3 rayOrigin, Vector3 rayVector,MeshFilter mf, out Vector3 Intersect, bool seeFront = true, bool seeBack = true)
{
Intersect = Vector3.zero;
Mesh mesh = mf.mesh;
int[] triangles = mesh.triangles;
Vector3[] vertices=mesh.vertices;
Vector3[] verticesWorldPos=new Vector3[vertices.Length];
Matrix4x4 localToWorld = mf.transform.localToWorldMatrix;
for(int i = 0; i < vertices.Length; i++)
{
verticesWorldPos[i]= localToWorld.MultiplyPoint3x4(vertices[i]);
}
for (int i = 0; i < triangles.Length; i+=3)
{
if (RayIntersectsTriangle(rayOrigin, rayVector, verticesWorldPos[triangles[i]], verticesWorldPos[triangles[i+1]], verticesWorldPos[triangles[i+2]], out Vector3 thisout,seeFront,seeBack))
{
Intersect=thisout;
return true;
}
}
return false;
}
public static bool RayIntersectsTriangle(Vector3 rayOrigin,Vector3 rayVector,Vector3 vertex0, Vector3 vertex1, Vector3 vertex2, out Vector3 outIntersectionPoint,bool seeFront=true ,bool seeBack=true)
{
outIntersectionPoint = new Vector3();
const float EPSILON = 0.000001f;
Vector3 edge1, edge2, h, s, q;
float a, f, u, v;
edge1 = vertex1 - vertex0;
edge2 = vertex2 - vertex0;
h = Vector3.Cross(rayVector,edge2);
a = Vector3.Dot(edge1,h);
if (-EPSILON < a && a < EPSILON) return false;//aがに近いとき、平行なので偽
if (a < -EPSILON && !seeBack) return false;//aが0以下の時、判定する面はRayに対して裏面を向いているので、裏面を判定しないならば偽
if (EPSILON < a && !seeFront) return false;//aが0以上の時、判定する面はRayに対して表面を向いているので、表面を判定しないならば偽
f = 1.0f / a;
s = rayOrigin - vertex0;
u = f * Vector3.Dot(s,h);
if (u < 0.0f || u > 1.0f) return false;
q = Vector3.Cross(s,edge1);
v = f * Vector3.Dot(rayVector,q);
if (v < 0.0f || u + v > 1.0f)
return false;
// At this stage we can compute t to find out where the intersection point is on the line.
float t = f * Vector3.Dot(edge2,q);
if (t > EPSILON) // ray intersection
{
outIntersectionPoint = rayOrigin + rayVector * t;
return true;
}
else // This means that there is a line intersection but not a ray intersection.
return false;
}
}