Comment déterminez-vous quel objet / surface l'utilisateur pointe vers lwjgl?

9

Le titre dit à peu près tout. Je travaille sur un projet simple «permet de s'habituer à lwjgl» impliquant la manipulation d'un cube rubik, et je ne sais pas comment dire de quel côté / carré l'utilisateur pointe.

Flynn1179
la source
Remarque: de nombreux moteurs AFAIK le font entièrement sur le processeur, séparément du rendu et sans utiliser OpenGL du tout, car c'est plus rapide (mais plus compliqué).
user253751

Réponses:

8

Vous voudrez utiliser la cueillette 3D. Voici un code que j'utilise dans mon jeu.

J'ai d'abord lancé un rayon depuis ma caméra. J'utilise la souris, mais si vous n'utilisez que l'endroit où l'utilisateur regarde, vous pouvez simplement utiliser le centre de la fenêtre. Voici ce code de ma classe d'appareil photo:

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

Ensuite, je suis le rayon jusqu'à ce qu'il intersecte avec un objet, vous pouvez le faire avec des boîtes englobantes ou quelque chose de similaire, car cela est spécifique à votre jeu, je vous laisse gérer cela. Généralement, cela se fait en suivant le rayon (en ajoutant la direction du rayon à son point de départ encore et encore 'jusqu'à ce que vous tombiez sur quelque chose).

Ensuite, vous voulez voir quelle face est sélectionnée, vous pouvez le faire en itérant sur les triangles de votre cube pour voir si le rayon les intersecte. La fonction suivante fait cela et renvoie la distance au visage sélectionné, puis j'utilise simplement le visage intersecté le plus proche de la caméra (donc vous ne choisissez pas la face arrière).

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

Le triangle avec la distance la plus courte est le triangle choisi. Aussi, plug sans vergogne pour mon jeu, vous devriez le vérifier, le suivre et voter dans les sondages que je publie occasionnellement. Merci! http://byte56.com

MichaelHouse
la source
3

La technique que vous recherchez est appelée "cueillette" ou "cueillette 3D". Il y a plusieurs façons de le faire; l'une des plus courantes consiste à transformer un point 2D sur l'écran en espace oculaire en utilisant l'inverse de la transformation de projection. Cela vous permettra de générer un rayon dans l'espace de vue, que vous pouvez utiliser pour tester la collision avec la représentation physique de vos différents bits de géométrie de scène afin de déterminer quel objet l'utilisateur a `` touché ''.

Vous pouvez également utiliser un "tampon de sélection" (ou "tampon de sélection") que GL prend en charge. Cela implique essentiellement d'écrire un identifiant d'objet unique dans un tampon pour chaque pixel, puis de simplement tester ce tampon.

La FAQ OpenGL a de brèves discussions sur les deux (elle se concentre davantage sur le tampon de sélection car il s'agit entièrement d'une fonctionnalité GL; la sélection de rayons est indépendante de l'API, sauf peut-être pour extraire les matrices actives du pipeline). Voici un exemple plus spécifique de la technique de sélection de rayons (pour iOS, mais elle devrait se traduire assez facilement). Ce site contient du code source pour certains des exemples OpenGL Red Book portés sur LWJGL, qui incluent une démo de sélection.

Voir aussi cette question sur SO.

Communauté
la source
Notez également que l'API de sélection OpenGL est déconseillée dans GL3 Core. Il est toujours disponible dans le profil complet.
nul
Hé, savoir quel terme rechercher fait une énorme différence :) Cependant, je viens de chercher sur google pour 'lwjgl picking example', et ce fil a été l'un des meilleurs hits!
Flynn1179