Làm thế nào để bạn xác định đối tượng / bề mặt mà người dùng trỏ vào với lwjgl?


9

Tiêu đề khá nhiều nói lên tất cả. Tôi đang làm việc với một dự án 'cho phép làm quen với lwjgl' đơn giản liên quan đến việc thao túng khối rubik và tôi không thể tìm ra cách để người dùng chỉ vào phía nào / vuông.


Lưu ý: AFAIK nhiều công cụ thực hiện việc này hoàn toàn trên CPU, tách biệt với kết xuất và hoàn toàn không sử dụng OpenGL, vì nó nhanh hơn (nhưng phức tạp hơn).
dùng253751

Câu trả lời:


8

Bạn sẽ muốn sử dụng chọn 3D. Đây là một số mã tôi sử dụng trong trò chơi của mình.

Đầu tiên tôi chiếu một tia từ máy ảnh của tôi. Tôi đang sử dụng chuột, nhưng nếu bạn chỉ sử dụng nơi người dùng đang tìm kiếm, bạn có thể chỉ cần sử dụng trung tâm của cửa sổ. Đây là mã từ lớp máy ảnh của tôi:

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);
}

Sau đó, tôi theo dõi tia cho đến khi nó giao nhau với một đối tượng, bạn có thể làm điều này với các hộp giới hạn hoặc một cái gì đó tương tự, vì điều này là cụ thể cho trò chơi của bạn, tôi sẽ cho phép bạn xử lý việc đó. Nói chung, điều này được thực hiện bằng cách theo dõi tia sáng (thêm hướng của tia tới điểm bắt đầu lặp đi lặp lại 'cho đến khi bạn va vào thứ gì đó).

Tiếp theo bạn muốn xem mặt nào đang được chọn, bạn có thể làm điều đó bằng cách lặp qua các hình tam giác trong khối lập phương của bạn để xem liệu tia đó giao nhau với chúng. Chức năng sau đây thực hiện điều đó và trả về khoảng cách đến mặt được chọn, sau đó tôi chỉ sử dụng mặt giao nhau gần camera nhất (vì vậy bạn không chọn mặt sau).

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;
}

Tam giác có khoảng cách ngắn nhất là tam giác được chọn. Ngoài ra, không biết xấu hổ cho trò chơi của tôi, bạn nên kiểm tra nó, theo dõi nó và bỏ phiếu trong các cuộc thăm dò mà tôi đưa ra. Cảm ơn! http://byte56.com


3

Kỹ thuật bạn đang tìm kiếm được gọi là "chọn" hoặc "chọn 3D". Có một số cách để làm điều đó; một trong những cách phổ biến nhất là biến đổi một điểm 2D trên màn hình thành không gian mắt bằng cách sử dụng nghịch đảo của phép biến đổi hình chiếu. Điều này sẽ cho phép bạn tạo ra một tia trong không gian xem, mà bạn có thể sử dụng để kiểm tra sự va chạm với biểu diễn vật lý của các bit hình học cảnh khác nhau của bạn để xác định đối tượng nào người dùng 'nhấn'.

Bạn cũng có thể sử dụng "bộ đệm chọn" (hoặc "bộ đệm lựa chọn") mà GL có hỗ trợ. Điều này về cơ bản liên quan đến việc viết một số định danh đối tượng duy nhất vào một bộ đệm cho mỗi pixel và sau đó chỉ cần kiểm tra bộ đệm đó.

Câu hỏi thường gặp về OpenGL có các cuộc thảo luận ngắn gọn về cả hai (nó tập trung nhiều hơn vào bộ đệm lựa chọn vì đó hoàn toàn là một tính năng GL; chọn tia là bất khả tri API ngoại trừ việc trích xuất các ma trận hoạt động từ đường ống). Dưới đây là một ví dụ cụ thể hơn về kỹ thuật chọn tia (đối với iOS, nhưng nó nên dịch đủ dễ dàng). Trang web này có một số mã nguồn cho một số ví dụ về Sách đỏ OpenGL được chuyển đến LWJGL, bao gồm một bản demo chọn.

Xem thêm câu hỏi này trên SO.


Cũng lưu ý rằng API chọn OpenGL đã không được dùng trong GL3 Core. Nó vẫn có sẵn trong hồ sơ đầy đủ mặc dù.
khoảng trống

Heh, biết thuật ngữ tìm kiếm nào tạo ra sự khác biệt lớn :) Tuy nhiên, tôi chỉ tìm kiếm trên google cho 'ví dụ chọn lwjgl', và chủ đề này là một trong những lượt truy cập hàng đầu!
Flynn1179
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.