您如何确定用户使用lwjgl指向的对象/表面?


9

标题几乎说明了一切。我正在研究一个简单的“让我习惯使用lwjgl”项目,其中涉及对魔方的操纵,但我不知道如何辨别用户所指的是哪一边/哪一边。


注意:AFAIK许多引擎完全独立于渲染而完全在CPU上执行此操作,并且根本不使用OpenGL,因为它速度更快(但更复杂)。
user253751

Answers:


8

您将要使用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


3

您正在寻找的技术称为“拾取”或“ 3D拾取”。有几种方法可以做到这一点。较常见的一种方法是使用投影变换的逆函数将屏幕上的2D点变换为眼睛空间。这将允许您在视图空间中生成射线,可用于测试与场景几何图形各个位的物理表示是否发生冲突,从而确定用户“命中”了哪个对象。

您还可以使用GL支持的“选择缓冲区”(或“选择缓冲区”)。基本上,这涉及将一些唯一的对象标识符写入每个像素的缓冲区中,然后简单地测试该缓冲区。

OpenGL FAQ在这两者上都有简短的讨论(由于它完全是GL功能,因此更多地集中在选择缓冲区上;光线拾取与API无关,除了可能从管道中提取活动矩阵之外)。这是射线拾取技术的一个更具体的示例(对于iOS,但它应该足够容易地转换)。该站点提供了一些移植到LWJGL的OpenGL Red Book示例的源代码,其中包括一个挑选演示。

另请参见此问题


另请注意,GL3 Core中不推荐使用OpenGL Picking API。完整个人资料中仍然可用。
无效

嘿,知道要搜索的术语会产生很大的不同:)但是,我只是在google上搜索了“ lwjgl选择示例”,因此该主题是热门歌曲之一!
Flynn1179
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.