需求:在没有碰撞体的前提下检测到鼠标点击的mesh。
思路:获得所有的mesh,然后通过算法比较mesh中每一个三角形面片与射线是否相交,将所有相交点与射线origin之间的长度进行排序,最短的即为需要获得的mesh。
上干活,首先是射线与三角形相交算法,通过u,v或者t 可以算到相交点
/// <summary> /// 射线与三角形相交 Vector3版本 /// 射线: orig + dir * t /// 三角形:(1 - u - v)v0 + u * v1 + v * v2 /// </summary> /// <param name="orig">起点</param> /// <param name="dir">射线方向</param> /// <param name="v0">三角形点1</param> /// <param name="v1">三角形点2</param> /// <param name="v2">三角形点2</param> /// <param name="t">结果 t</param> /// <param name="u">结果 u </param> /// <param name="v">结果 v</param> /// <returns></returns> public static bool IsRayIntersectTriangle(Vector3 orig, Vector3 dir, Vector3 v0, Vector3 v1, Vector3 v2, out float t, out float u, out float v) { t = -1; u = -1; v = -1; // E1 Vector3 E1 = v1 - v0; // E2 Vector3 E2 = v2 - v0; // P Vector3 P = Vector3.Cross(dir, E2); // determinant float det = Vector3.Dot(E1, P); // keep det > 0, modify T accordingly Vector3 T; if (det > 0) { T = orig - v0; } else { T = v0 - orig; det = -det; } // If determinant is near zero, ray lies in plane of triangle float espX = 0.00001f; if (det < espX) return false; // Calculate u and make sure u <= 1 u = Vector3.Dot(T, P); if (u < -espX || u > det) return false; // Q Vector3 Q = Vector3.Cross(T, E1); // Calculate v and make sure u + v <= 1 v = Vector3.Dot(dir, Q); if (v < -espX || u + v > det+ espX) return false; // Calculate t, scale parameters, ray intersects triangle t = Vector3.Dot(E2, Q); float fInvDet = 1.0f / det; t *= fInvDet; u *= fInvDet; v *= fInvDet; return true; }
接下来是与mesh相交
public static bool IsRayIntersectMesh(Mesh mesh, Vector3 org, Vector3 dir, Transform fatherTransform,out List<Vector3> dis) { dis = new List<Vector3>(); //Matrix4x4 translate = fatherTransform.localToWorldMatrix; int[] triangles = mesh.triangles; Vector3[] vertices = mesh.vertices; //Matrix4x4 x = Matrix4x4.TRS(fatherTransform.position, fatherTransform.rotation, fatherTransform.lossyScale); for (int i = 0; i < vertices.Length - 1; i++) { vertices[i] = fatherTransform.TransformPoint( vertices[i]); //NormalSurface.CreateBall(vertices[i], "1"); } for (int i = 0; i < triangles.Length-2; i += 3) { float t; float u; float v; try { bool bb = GenesisWinForm.MathG3D. Function.IsRayIntersectTriangle(org, dir, vertices[triangles[i]], vertices[triangles[i+1]], vertices[triangles[i+2]], out t, out u, out v); if (bb) { Vector3 cp = (1 - u - v) * vertices[triangles[i]] + u * vertices[triangles[i+1]] + v * vertices[triangles[i+2]]; dis.Add(cp) ; //return true; } } catch { //Debug.Log(i); } } if (dis.Count > 0) return true; return false; }
返回的dis是相交点,当然cp还可以用 org+ dir*t 来计算(上面是忘记了所以用uv算的)。
通过比较t的大小就可以得到想要的mesh了~