사용자가 lwjgl로 가리키는 객체 / 표면을 어떻게 결정합니까?


9

제목은 거의 모든 것을 말합니다. 나는 루빅스 큐브 조작과 관련된 간단한 'lwjgl에 익숙해지기'프로젝트를 진행 중이며 사용자가 어느면 / 사각을 가리키는 지 알 수 없습니다.


참고 : AFAIK 많은 엔진은 렌더링과는 별도로 OpenGL을 전혀 사용하지 않고 CPU에서이 작업을 수행합니다. 속도는 더 빠르지 만 복잡합니다.
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


3

찾고있는 기술을 "피킹"또는 "3D 피킹"이라고합니다. 여러 가지 방법이 있습니다. 가장 일반적인 것 중 하나는 투영 변환의 역수를 사용하여 화면의 2D 점을 눈 공간으로 변환하는 것입니다. 이를 통해 뷰 공간에 광선을 생성 할 수 있으며,이를 통해 다양한 장면 형상 비트의 물리적 표현과의 충돌을 테스트하여 사용자가 '적중 한'오브젝트를 결정할 수 있습니다.

GL이 지원하는 "피킹 버퍼"(또는 "선택 버퍼")를 사용할 수도 있습니다. 여기에는 기본적으로 모든 픽셀의 고유 객체 식별자를 버퍼에 쓴 다음 해당 버퍼를 테스트하는 것이 포함됩니다.

OpenGL FAQ는 둘 다에 대한 간단한 토론을 가지고 있습니다. (선택 버퍼는 전체 GL 기능이므로 선택 버퍼에 더 중점을 둡니다. 레이 피킹은 파이프 라인에서 활성 매트릭스를 추출하는 경우를 제외하고 API와 무관합니다). 다음은 광선 따기 기법의보다 구체적인 예입니다 (iOS의 경우, 쉽게 번역해야 함). 이 사이트 에는 LWJGL로 포팅 된 일부 OpenGL Red Book 예제에 대한 소스 코드가 있으며 여기에는 픽킹 데모가 포함됩니다.

SO에 대한이 질문 도 참조하십시오 .


또한 OpenGL 선택 API는 GL3 Core에서 더 이상 사용되지 않습니다. 그래도 전체 프로필에서 사용할 수 있습니다.
무효

Heh, 검색 할 용어를 아는 것이 큰 차이를 만듭니다 :) 그러나 방금 구글에서 'lwjgl picking example'을 검색 했으며이 스레드는 최고의 인기 중 하나였습니다!
Flynn1179
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.