Come si determina a quale oggetto / superficie l'utente punta con lwjgl?


9

Il titolo dice praticamente tutto. Sto lavorando a un semplice progetto "abituiamoci a lwjgl" che prevede la manipolazione di un cubo di Rubik e non riesco a capire come dire a quale lato / quadrato punta l'utente.


Nota: AFAIK molti motori lo fanno interamente sulla CPU, separatamente dal rendering e senza usare OpenGL, perché è più veloce (ma più complicato).
user253751

Risposte:


8

Ti consigliamo di utilizzare la raccolta 3D. Ecco un po 'di codice che uso nel mio gioco.

Per prima cosa ho lanciato un raggio dalla mia macchina fotografica. Sto usando il mouse, ma se stai usando solo dove l'utente sta guardando, puoi semplicemente usare il centro della finestra. Questo è quel codice della mia classe di telecamere:

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

Quindi seguo il raggio fino a quando non si interseca con un oggetto, puoi farlo con delimitatori o qualcosa di simile, poiché questo è specifico per il tuo gioco, ti lascerò gestire. Generalmente questo viene fatto seguendo il raggio (aggiungendo la direzione del raggio al suo punto di partenza ancora e ancora 'fino a quando non ci si imbatte in qualcosa).

Successivamente, vuoi vedere quale faccia viene selezionata, puoi farlo ripetendo i triangoli nel cubo per vedere se il raggio li interseca. La seguente funzione lo fa e restituisce la distanza dalla faccia selezionata, quindi uso solo la faccia intersecata più vicina alla fotocamera (quindi non stai selezionando la faccia posteriore).

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

Il triangolo con la distanza più breve è il triangolo selezionato. Inoltre, spina spudorata per il mio gioco, dovresti verificarlo, seguirlo e votare nei sondaggi che faccio occasionalmente. Grazie! http://byte56.com


3

La tecnica che stai cercando si chiama "picking" o "picking 3D". Ci sono molti modi per farlo; uno dei più comuni è trasformare un punto 2D sullo schermo nello spazio degli occhi usando l'inverso della trasformazione della proiezione. Ciò ti consentirà di generare un raggio nello spazio di visualizzazione, che puoi utilizzare per verificare la collisione con la rappresentazione fisica dei vari bit della geometria della scena per determinare quale oggetto "colpisci" l'utente.

Puoi anche utilizzare un "buffer di selezione" (o "buffer di selezione") per il quale GL ha il supporto. Ciò implica fondamentalmente la scrittura di un identificatore di oggetto univoco in un buffer per ogni pixel e quindi semplicemente il test di quel buffer.

Le FAQ di OpenGL hanno una breve discussione su entrambi (si concentra maggiormente sul buffer di selezione poiché è interamente una funzione GL; il ray picking è indipendente dalle API, tranne forse per l'estrazione delle matrici attive dalla pipeline). Ecco un esempio più specifico della tecnica di ray picking (per iOS, ma dovrebbe tradursi abbastanza facilmente). Questo sito ha del codice sorgente per alcuni degli esempi di OpenGL Red Book portati su LWJGL, che includono una demo di prelievo.

Vedi anche questa domanda su SO.


Si noti inoltre che l'API di prelievo OpenGL è stata deprecata in GL3 Core. È comunque disponibile nel profilo completo.
Vuoto

Heh, sapendo quale termine cercare fa una grande differenza :) Tuttavia, ho appena cercato su Google "esempio di raccolta", e questa discussione è stata una delle hit migliori!
Flynn1179,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.