Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

05.06.2017, 23:16

LWJGL Raypicking

Hallo liebe Spieleprogrammierer.de Community,

ich programmiere derzeit mit LWJGL ein 3D Spiel.
Das klappt soweit auch ganz gut.
Es ist vom Prinzip her Minecraft ähnlich. Allerdings habe ich nicht vor einen Klon zu programmieren.

Ich bin allerdings auf ein Problem gestoßen, welches ich scheinbar nicht durch eigener Überlegungen lösen kann. Auch nicht mit Google.
Dieses Problem nennt sich Raypicking.

Ich habe durchaus auf Google mehrere Anleitungen gefunden. Allerdings sind diese Anleitungen teilweise nicht mehr erreichbar (Domain nicht mehr bezahlt), schlecht geschrieben, oder ich kann diese nicht auf mein Problem anwenden. Es gibt ein Tutorial von ThinMatrix (
), welches ich versucht habe in meinem Spiel umzusetzen.

Bevor ich diese Variante benutzt habe, hatte ich eine eigene. Vom Ergebnis her sind diese beiden eigentlich gleich. Anbei findet ihr Bilder, die das Problem glaube ich ganz gut zeigen (Der bläuliche und dickere Block ist der gepickte).
Es scheint so, dass der Vektor nicht genau in der Mitte der Kamera seinen Ursprung hat und diese Abweichung sich bemerkbar macht.

Nun folgt der Source-Code, der dafür zuständig ist.

Die Klasse, welches das Raypicking handelt:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class RayPicker {
    public Vector3f currentRay;
    
    private Matrix4f projectionMatrix;
    private Matrix4f viewMatrix;
    private ViewCamera viewCamera;
    
    public RayPicker(ViewCamera viewCamera, Matrix4f projectionMatrix) {
        this.viewCamera = viewCamera;
        this.projectionMatrix = projectionMatrix;
        this.viewMatrix = MatrixMath.createViewMatrix(viewCamera);
    }
    
    public void tick() {
        viewMatrix = MatrixMath.createViewMatrix(viewCamera);
        currentRay = calculateMouseRay();
    }
    
    private Vector3f calculateMouseRay() {
        Vector4f clipCoords = new Vector4f(0, 0, -1, 1);
        Vector4f eyeCoords = toEyeCoords(clipCoords);
        return toWorldCoords(eyeCoords);
    }
    
    private Vector3f toWorldCoords(Vector4f eyeCoords) {
        Matrix4f invertedView = Matrix4f.invert(viewMatrix, null);
        Vector4f rayWorld = Matrix4f.transform(invertedView, eyeCoords, null);
        Vector3f mouseRay = new Vector3f(rayWorld.x, rayWorld.y, rayWorld.z);
        mouseRay.normalise();
        return mouseRay;
    }
    
    private Vector4f toEyeCoords(Vector4f clipCoords) {
        Matrix4f invertedProjection = Matrix4f.invert(projectionMatrix, null);
        Vector4f eyeCoords = Matrix4f.transform(invertedProjection, clipCoords, null);
        return new Vector4f(eyeCoords.x, eyeCoords.y, -1, 0);
    }
}


Und der Sourcecode, welcher das Tile berechnet, welches vom Spieler angesehen wird:

Quellcode

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
boolean picked = false;
                for (float i = 0; i < 10; i+=0.1f) {
                    Vector3f stop = new Vector3f(
                            viewCamera.getCameraPosition().x + level.levelCollisionPicker.currentRay.x * i + 0.5f,
                            viewCamera.getCameraPosition().y + level.levelCollisionPicker.currentRay.y * i + 0.5f,
                            viewCamera.getCameraPosition().z + level.levelCollisionPicker.currentRay.z * i + 0.5f
                        );
                    Tile pickedTile = level.getTile(stop);
                    if(pickedTile.block != Blocks.air) {
                        rayPickedTile = stop;
                        picked = true;
                        break;
                    }
                }
                if(!picked) rayPickedTile = null;


Ich hoffe, dass ihr mir weiterhelfen könnt.

MfG
MisterSebigunner
»MisterSebigunner« hat folgende Bilder angehängt:
  • 1.PNG
  • 2.PNG

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »MisterSebigunner« (06.06.2017, 19:10) aus folgendem Grund: Quellcode überarbeitet, damit man ein funktionales Ergebnis hat.


BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

2

06.06.2017, 06:51

Da deine Kamera kein Punkt ist, sondern eine Fläche, wäre es sogar sehr schlecht, wenn der Ray von der Mitte der Kamera ausgehen würde, statt von dort, wo sich der Cursor befindet.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

3

06.06.2017, 07:06

Da deine Kamera kein Punkt ist, sondern eine Fläche, wäre es sogar sehr schlecht, wenn der Ray von der Mitte der Kamera ausgehen würde, statt von dort, wo sich der Cursor befindet.

Die Kamera an sich ist normalerweise schon ein "Punkt" (unten im Bild "Camera origin"). Die Bildebene, die mit der Near Clipping Plane identisch ist, schwebt in Blickrichtung irgendwo davor. Die Kameraposition liegt mit jedem Picking Ray auf einer Geraden. Der einzige Nachteil, wenn man die Kameraposition als Startpunkt für den Picking Ray nimmt (statt den Punkt, wo die Bildebene geschnitten wird), ist, dass man dann Objekte anklicken könnte, die von der Near Clipping Plane weggeschnitten werden.


(Link)

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

4

06.06.2017, 07:20

Ich ging dabei davon aus, dass er eigentlich die Near Plane meint.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

5

06.06.2017, 14:00


Da deine Kamera kein Punkt ist, sondern eine Fläche, wäre es sogar sehr schlecht, wenn der Ray von der Mitte der Kamera ausgehen würde, statt von dort, wo sich der Cursor befindet.


Der Spieler selbst erlebt das Spiel aus einer Firstperson Perspektive.
Daher macht es in meinen Augen Sinn, als Mittelpunkt den Ursprung zu nehmen. Bei OpenGL ist der Ursprung doch in der Mitte des Bildes und daher doch mittig auf der Near clipping plane, oder nicht?


Die Kamera an sich ist normalerweise schon ein "Punkt" (unten im Bild "Camera origin"). Die Bildebene, die mit der Near Clipping Plane identisch ist, schwebt in Blickrichtung irgendwo davor. Die Kameraposition liegt mit jedem Picking Ray auf einer Geraden. Der einzige Nachteil, wenn man die Kameraposition als Startpunkt für den Picking Ray nimmt (statt den Punkt, wo die Bildebene geschnitten wird), ist, dass man dann Objekte anklicken könnte, die von der Near Clipping Plane weggeschnitten werden.


Bis jetzt konnte ich dieses Problem vernachlässigen, da ich ja ein anderes Problem habe ^^

BlueCobold

Community-Fossil

Beiträge: 10 738

Beruf: Teamleiter Mobile Applikationen & Senior Software Engineer

  • Private Nachricht senden

6

06.06.2017, 14:08

Bei OpenGL ist der Ursprung doch in der Mitte des Bildes und daher doch mittig auf der Near clipping plane, oder nicht?
Wenn du wirklich genau das meinst, was da steht, dann nein. Wie ich schon im ersten Post sagte, wäre es fatal, wenn du beim Raypicking von der Mitte der Near-Plane ausgehst. Der Cursor ist schließlich nicht in der Mitte.
Teamleiter von Rickety Racquet (ehemals das "Foren-Projekt") und von Marble Theory

Willkommen auf SPPRO, auch dir wird man zu Unity oder zur Unreal-Engine raten, ganz bestimmt.[/Sarkasmus]

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

7

06.06.2017, 14:11

Wenn du wirklich genau das meinst, was da steht, dann nein. Wie ich schon im ersten Post sagte, wäre es fatal, wenn du beim Raypicking von der Mitte der Near-Plane ausgehst. Der Cursor ist schließlich nicht in der Mitte.

Er drückt sich einfach ungünstig aus. Er meint den Ray der durch die Clipping Planes geht und im Origin der Kamera startet. Das ist ja in dem Fall das was er möchte.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

8

06.06.2017, 14:22

Er drückt sich einfach ungünstig aus. Er meint den Ray der durch die Clipping Planes geht und im Origin der Kamera startet. Das ist ja in dem Fall das was er möchte.


Jetzt wo ich das lese was ich geschrieben habe...
Ja ich hab mich ein bisschen ungünstig ausgedrückt ^^

equinox

Frischling

Beiträge: 56

Beruf: Student

  • Private Nachricht senden

9

06.06.2017, 17:51

Soweit ich mich erinnere musst du um den Punkt von den Screen-Koordinaten in die Welt-Koordinaten zu bringen noch durch die W-Komponente teilen (weiß grad nur nicht mehr warum):

C-/C++-Quelltext

1
2
3
4
5
6
Vector4f tmp = inverseProjection * clipCoords;
tmp = tmp / tmp.w
tmp = inverseView * tmp

ray.origin = eyePos
ray.direction = Vector3f(tmp) //einfach die w-Komponente weglassen


So in der Art löse ich ein ähnliches Problem zumindest in meinem Raytracer :)

Dazu kommt noch, dass du die Kameraposition in deinem Ray (zumindest im gezeigten Code) gar nicht als Ursprung deiner Geraden setzt. Sollte man aber machen.

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »equinox« (06.06.2017, 17:57)


10

06.06.2017, 18:50

Ich hab das Problem jetzt (zumindest augenscheinlich) lösen können:

Nachdem ich mir testweise mal den resultierenden Vektor darstellen lassen habe, welcher sich übrigens als richtig herausgestellt hat, ist mir aufgefallen, dass der, von Anfang an vermutete Versatz, vom Betrag her ungefähr 0.5 [LE] groß ist.
Da fiel mir dann ein, dass die Tiles ihren Ursprung nicht irgendeiner Ecke haben, sondern in der Mitte. Da ein Tile 1 [LE] hoch, lang und breit ist, werden die Vertices um 0.5 [LE] um den Ursprung gerendert.

Und dann habe ich einfach mal noch 0.5f auf den Vektor (In meinem Quellcode die Variable "stop") addiert und das Resultat scheint mir richtig auszusehen. Genaueres werde ich aber später sagen können, wenn ich mal sowas ähnliches wie ein Crosshair einbaue.

Von daher bedanke ich mich schonmal für eure Hilfe :)

Werbeanzeige