Как определить, на какой объект / поверхность указывает пользователь с помощью lwjgl?

9

Название в значительной степени говорит обо всем. Я работаю над простым проектом «давайте привыкнем к lwjgl», включающим манипулирование кубом рубика, и я не могу понять, как определить, на какую сторону / квадрат направлен пользователь.

Flynn1179
источник
Примечание: AFAIK многие движки делают это полностью на процессоре, отдельно от рендеринга и вообще не используя OpenGL, потому что это быстрее (но сложнее).
user253751

Ответы:

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

MichaelHouse
источник
3

Техника, которую вы ищете, называется «сбор» или «сбор 3D». Есть несколько способов сделать это; Одним из наиболее распространенных является преобразование 2D-точки на экране в пространство глаза с помощью обратного преобразования проекции. Это позволит вам сгенерировать луч в пространстве вида, который вы можете использовать для проверки на столкновение с физическим представлением ваших различных битов геометрии сцены, чтобы определить, какой объект пользователь «ударил».

Вы также можете использовать «буфер выбора» (или «буфер выбора»), который поддерживает GL. Это в основном включает запись некоторого уникального идентификатора объекта в буфер для каждого пикселя, а затем просто тестирование этого буфера.

В FAQ по OpenGL кратко обсуждаются оба варианта (он больше фокусируется на буфере выбора, поскольку это полностью функция GL; выбор лучей не зависит от API, за исключением, возможно, извлечения активных матриц из конвейера). Вот более конкретный пример техники выбора лучей (для iOS, но она должна переводиться достаточно легко). На этом сайте есть некоторый исходный код некоторых примеров Красной книги OpenGL, портированных на LWJGL, которые включают демонстрацию выбора.

Смотрите также этот вопрос на SO.

Сообщество
источник
Также обратите внимание, что API выбора OpenGL устарел в GL3 Core. Это все еще доступно в полном профиле, хотя.
аннулировано
Хех, зная, какой термин для поиска имеет огромное значение :) Тем не менее, я просто искал в Google «пример выбора lwjgl», и эта тема была одной из самых популярных!
Flynn1179