回答:
3Dピッキングを使用します。これがゲームで使用するコードです。
まず、カメラから光線を放ちます。私はマウスを使用していますが、ユーザーが見ているところだけを使用している場合は、ウィンドウの中央を使用できます。これは私のカメラクラスのコードです:
public Ray GetPickRay() {
int mouseX = Mouse.getX();
int mouseY = WORLD.Byte56Game.getHeight() - Mouse.getY();
float windowWidth = WORLD.Byte56Game.getWidth();
float windowHeight = WORLD.Byte56Game.getHeight();
//get the mouse position in screenSpace coords
double screenSpaceX = ((float) mouseX / (windowWidth / 2) - 1.0f) * aspectRatio;
double screenSpaceY = (1.0f - (float) mouseY / (windowHeight / 2));
double viewRatio = Math.tan(((float) Math.PI / (180.f/ViewAngle) / 2.00f)) * zoomFactor;
screenSpaceX = screenSpaceX * viewRatio;
screenSpaceY = screenSpaceY * viewRatio;
//Find the far and near camera spaces
Vector4f cameraSpaceNear = new Vector4f((float) (screenSpaceX * NearPlane), (float) (screenSpaceY * NearPlane), (float) (-NearPlane), 1);
Vector4f cameraSpaceFar = new Vector4f((float) (screenSpaceX * FarPlane), (float) (screenSpaceY * FarPlane), (float) (-FarPlane), 1);
//Unproject the 2D window into 3D to see where in 3D we're actually clicking
Matrix4f tmpView = Matrix4f(view);
Matrix4f invView = (Matrix4f) tmpView.invert();
Vector4f worldSpaceNear = new Vector4f();
Matrix4f.transform(invView, cameraSpaceNear, worldSpaceNear);
Vector4f worldSpaceFar = new Vector4f();
Matrix4f.transform(invView, cameraSpaceFar, worldSpaceFar);
//calculate the ray position and direction
Vector3f rayPosition = new Vector3f(worldSpaceNear.x, worldSpaceNear.y, worldSpaceNear.z);
Vector3f rayDirection = new Vector3f(worldSpaceFar.x - worldSpaceNear.x, worldSpaceFar.y - worldSpaceNear.y, worldSpaceFar.z - worldSpaceNear.z);
rayDirection.normalise();
return new Ray(rayPosition, rayDirection);
}
次に、オブジェクトと交差するまでレイを追跡します。これは、バウンディングボックスなどで行うことができます。これはゲームに固有なので、処理させてください。一般に、これはレイアウトを追跡することによって行われます(レイの方向をその開始点に追加して、何かにぶつかるまで繰り返します)。
次に、どの面が選択されているかを確認したい場合は、キューブ内の三角形を繰り返し処理して、光線がそれらと交差するかどうかを確認します。次の関数はそれを行い、選択した面までの距離を返します。次に、カメラに最も近い交差面を使用します(したがって、背面を選択していません)。
public static float RayIntersectsTriangle(Ray R, Vector3f vertex1, Vector3f vertex2, Vector3f vertex3) {
// Compute vectors along two edges of the triangle.
Vector3f edge1 = null, edge2 = null;
edge1 = Vector3f.sub(vertex2, vertex1, edge1);
edge2 = Vector3f.sub(vertex3, vertex1, edge2);
// Compute the determinant.
Vector3f directionCrossEdge2 = null;
directionCrossEdge2 = Vector3f.cross(R.Direction, edge2, directionCrossEdge2);
float determinant = Vector3f.dot(directionCrossEdge2, edge1);
// If the ray and triangle are parallel, there is no collision.
if (determinant > -.0000001f && determinant < .0000001f) {
return Float.MAX_VALUE;
}
float inverseDeterminant = 1.0f / determinant;
// Calculate the U parameter of the intersection point.
Vector3f distanceVector = null;
distanceVector = Vector3f.sub(R.Position, vertex1, distanceVector);
float triangleU = Vector3f.dot(directionCrossEdge2, distanceVector);
triangleU *= inverseDeterminant;
// Make sure the U is inside the triangle.
if (triangleU < 0 || triangleU > 1) {
return Float.MAX_VALUE;
}
// Calculate the V parameter of the intersection point.
Vector3f distanceCrossEdge1 = null;
distanceCrossEdge1 = Vector3f.cross(distanceVector, edge1, distanceCrossEdge1);
float triangleV = Vector3f.dot(R.Direction, distanceCrossEdge1);
triangleV *= inverseDeterminant;
// Make sure the V is inside the triangle.
if (triangleV < 0 || triangleU + triangleV > 1) {
return Float.MAX_VALUE;
}
// Get the distance to the face from our ray origin
float rayDistance = Vector3f.dot(distanceCrossEdge1, edge2);
rayDistance *= inverseDeterminant;
// Is the triangle behind us?
if (rayDistance < 0) {
rayDistance *= -1;
return Float.MAX_VALUE;
}
return rayDistance;
}
距離が最も短い三角形が、選択した三角形です。また、私のゲームのための恥知らずなプラグイン、あなたはそれをチェックアウトし、それに従って、私が時々出す投票に投票するべきです。ありがとう!http://byte56.com
お探しのテクニックは「ピッキング」または「3Dピッキング」と呼ばれています。それを行うにはいくつかの方法があります。最も一般的なものの1つは、投影変換の逆を使用して、画面上の2D点を目の空間に変換することです。これにより、ビュースペースに光線を生成できます。これを使用して、シーンジオメトリのさまざまなビットの物理表現との衝突をテストし、ユーザーが「ヒット」したオブジェクトを特定できます。
GLがサポートしている「ピッキングバッファー」(または「選択バッファー」)を使用することもできます。これには基本的に、すべてのピクセルの一意のオブジェクト識別子をバッファーに書き込んでから、そのバッファーをテストするだけです。
OpenGL FAQは両方について簡単に説明しています(それは完全にGL機能であるため、選択バッファーに重点を置いています。レイピッキングは、おそらくパイプラインからアクティブな行列を抽出することを除いて、APIに依存しません)。レイピッキングテクニックのより具体的な例を次に示します(iOSの場合ですが、簡単に翻訳できます)。このサイトは、LWJGLに移植されたOpenGL Red Bookの例のいくつかのソースコードがあり、ピッキングデモが含まれています。
SOに関するこの質問も参照してください。