Como você determina para qual objeto / superfície o usuário está apontando com lwjgl?

9

Título praticamente diz tudo. Estou trabalhando em um projeto simples 'vamos nos acostumar com o lwjgl' envolvendo a manipulação de um cubo de rubik, e não consigo descobrir como saber de que lado / quadrado o usuário está apontando.

Flynn1179
fonte
Nota: AFAIK muitos mecanismos fazem isso inteiramente na CPU, separadamente da renderização e sem usar o OpenGL, porque é mais rápido (mas mais complicado).
usar o seguinte comando

Respostas:

8

Você vai querer usar a seleção 3D. Aqui está um código que eu uso no meu jogo.

Primeiro, lancei um raio da minha câmera. Estou usando o mouse, mas se você estiver usando apenas para onde o usuário está olhando, basta usar o centro da janela. Este é o código da minha classe de câmera:

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

Então eu sigo o raio até ele se cruzar com um objeto, você pode fazer isso com caixas delimitadoras ou algo semelhante, já que isso é específico para o seu jogo, eu vou deixar você lidar com isso. Geralmente, isso é feito seguindo o raio para fora (adicionando a direção do raio ao ponto de partida várias vezes até que você esbarre em algo).

Em seguida, você deseja ver qual face está sendo escolhida, iterando sobre os triângulos em seu cubo para ver se o raio os intercepta. A função a seguir faz isso e retorna a distância para o rosto escolhido, então apenas uso o rosto cruzado mais próximo da câmera (para que você não escolha o rosto de trás).

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

O triângulo com a menor distância é o triângulo escolhido. Além disso, plug descarado para o meu jogo, você deve conferir, segui-lo e votar nas pesquisas que eu ocasionalmente publico. Obrigado! http://byte56.com

MichaelHouse
fonte
3

A técnica que você está procurando é chamada de "picking" ou "picking 3D". Há várias maneiras de o fazer; um dos mais comuns é transformar um ponto 2D na tela em espaço ocular usando o inverso da transformação da projeção. Isso permitirá que você gere um raio no espaço de visualização, que você pode usar para testar a colisão com a representação física de seus vários bits da geometria da cena para determinar qual objeto o usuário 'bateu'.

Você também pode usar um "buffer de picking" (ou "buffer de seleção") que o GL tenha suporte. Isso basicamente envolve escrever um identificador de objeto exclusivo em um buffer para cada pixel e simplesmente testar esse buffer.

O FAQ do OpenGL tem breves discussões sobre ambos (ele se concentra mais no buffer de seleção, pois esse é um recurso GL; a seleção de raios é independente da API, exceto talvez para extrair as matrizes ativas do pipeline). Aqui está um exemplo mais específico da técnica de seleção de raios (para iOS, mas deve ser traduzida com bastante facilidade). Este site possui algum código fonte para alguns dos exemplos do OpenGL Red Book portados para o LWJGL, que incluem uma demonstração de picking.

Veja também esta pergunta no SO.

Comunidade
fonte
Observe também que a API de seleção do OpenGL foi descontinuada no GL3 Core. Ainda está disponível no perfil completo.
void
Heh, saber qual termo procurar faz uma enorme diferença :) No entanto, eu apenas procurei no google por 'lwjgl picking example', e esse tópico foi um dos principais hits!
Flynn1179