OpenGL, First Person Perspektive

Hallo, ich habe ein Spiel in First Person Perspektive, die man ändern kann, indem man die Maus bewegt. Dazu ist sie unsichtbar und wird immer wieder in die Mitte des Displays gesetzt. Und ich prüfe, wie die Maus vor dem Zurücksetzten zum Mittelpunkt steht und setze so die Kamera. Die zwei Punkte der Kamera sind die Kameraposition und die Fokusposition. Der Vektor dieser Punkte ist die Blickrichtung, der Fokuspunkt bewegt sich auf Kreisbahnen um die Kameraposition. Das Problem ist, dass das ganze wackelig und sehr merkwürdig ist. Es lässt sich schwer in Worte fassen, deshalb ein KSKB:

import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_ONE;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.*;

import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;

import javax.swing.JFrame;

import org.lwjgl.LWJGLException;
import org.lwjgl.input.Cursor;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.glu.GLU;


public class Main implements HierarchyBoundsListener{
	
	public static void main(String[] args){
		new Main();
	}

	private JFrame f;
	private Cursor normalCursor;
	private Cursor invisibleCursor;
	private int w, h;
	private float camX = 0;
	private float camY = 3;
	private float camZ = 0;
	private float camFocusX = 5;
	private float camFocusY = 3;
	private float camFocusZ = 0;
	private float upX = 0;
	private float upY = 1;
	private float upZ = 0;
	private boolean gestartet;
	private double camAlphaYZ;
	private float camAlphaXZ;
	private boolean exited;
	
	public Main(){
		f = new JFrame("Krieger und Burgen");
		f.getContentPane().addHierarchyBoundsListener(this);
        f.setPreferredSize(new Dimension(700, 550));
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.setLocation(900, 50);
        f.pack();
        Canvas canvas = new Canvas();
        f.add(canvas);
        try {
        Display.setParent(canvas);
        f.setVisible(true);
        Display.create();
        normalCursor = Mouse.getNativeCursor();
        int[] ints = {
        	0, 0, 0, 0	
        };
		invisibleCursor = new Cursor(1, 1, 0, 0, 1, putIntCoords(ints), putIntCoords(ints));
		Mouse.setNativeCursor(invisibleCursor);
        } catch (LWJGLException e) {
            e.printStackTrace();
        }
        gestartet = true;
        w = Display.getWidth();
        h = Display.getHeight();
        
        new Thread(new Runnable(){

			@Override
			public void run() {
				while(!Display.isCloseRequested()){
					while(Keyboard.next()){
						if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)){
							exited = true;
							try {
								Mouse.setNativeCursor(normalCursor);
							} catch (LWJGLException e) {
								e.printStackTrace();
							}
						}
					}
					if(!exited){
						if(Mouse.getX() < w/2){
							camAlphaXZ -= 0.005;
							camFocusX = getCirclePosition((float)camAlphaXZ, 5.0f, new Point((int)camX, (int)camZ)).x;
							camFocusZ = getCirclePosition((float)camAlphaXZ, 5.0f, new Point((int)camX, (int)camZ)).y;
							Mouse.setCursorPosition(w/2, h/2);
						}
						if(Mouse.getX() > w/2){
							camAlphaXZ += 0.005;
							camFocusX = getCirclePosition((float)camAlphaXZ, 5.0f, new Point((int)camX, (int)camZ)).x;
							camFocusZ = getCirclePosition((float)camAlphaXZ, 5.0f, new Point((int)camX, (int)camZ)).y;
							Mouse.setCursorPosition(w/2, h/2);
						}
						if(Mouse.getY() > h/2){
							camAlphaYZ += 0.005;
							camFocusZ = getCirclePosition((float)camAlphaYZ, 5.0f, new Point((int)camY, (int)camZ)).x;
							camFocusY = getCirclePosition((float)camAlphaYZ, 5.0f, new Point((int)camY, (int)camZ)).y;
							Mouse.setCursorPosition(w/2, h/2);
						}
						if(Mouse.getY() < h/2){
							camAlphaYZ -= 0.005;
							camFocusZ = getCirclePosition((float)camAlphaYZ, 5.0f, new Point((int)camY, (int)camZ)).x;
							camFocusY = getCirclePosition((float)camAlphaYZ, 5.0f, new Point((int)camY, (int)camZ)).y;
							Mouse.setCursorPosition(w/2, h/2);
						}
					}
				}
			}
        	
        }).start();
        Mouse.setCursorPosition(w/2, h/2);
        
        while(!Display.isCloseRequested()){
        	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
            glViewport(0, 0, w, h);
            glEnable(GL_BLEND);
    	    glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            GLU.gluPerspective(45.0f, (float) w / (float) h, 0.1f, 1000);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            GLU.gluLookAt(camX, camY, camZ, camFocusX, camFocusY, camFocusZ, upX, upY, upZ);
    		
            glDisable(GL_BLEND);
    		glViewport( 0, 0, w, h);
    		glColor4f(0, 1, 0, 1);
           	glBegin(GL_TRIANGLES);
    		glVertex3f(25, 0,-25);
    		glVertex3f(-25, 0, -25);
    		glVertex3f(-25, 0, 25);
    		glVertex3f(-25, 0, 25);
    		glVertex3f(25, 0, 25);
    		glVertex3f(25, 0, -25);
           	glEnd();
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE);

            glDisable(GL_DEPTH_TEST);
            glMatrixMode(GL_PROJECTION);
            glLoadIdentity();
            GLU.gluOrtho2D(0, w, h, 0);
            glMatrixMode(GL_MODELVIEW);
            glLoadIdentity();
            glColor4f(1, 1, 1, 1);
            glBegin(GL_TRIANGLES);
    		glVertex3f((w-20)/2+20, (h-20)/2, 0);
    		glVertex3f((w-20)/2, (h-20)/2, 0);
    		glVertex3f((w-20)/2, (h-20)/2+20, 0);
    		glVertex3f((w-20)/2, (h-20)/2+20, 0);
    		glVertex3f((w-20)/2+20, (h-20)/2+20, 0);
    		glVertex3f((w-20)/2+20, (h-20)/2, 0);
    		glEnd();
            glEnable(GL_DEPTH_TEST);
            Display.update();
        }
	}
	
	public static IntBuffer putIntCoords(int[] ints){
		ByteBuffer vbb = ByteBuffer.allocateDirect(ints.length * 4);
	    vbb.order(ByteOrder.nativeOrder());
	    IntBuffer vertexBuffer = vbb.asIntBuffer();
	    vertexBuffer.put(ints);
	    vertexBuffer.position(0);
	    return vertexBuffer;
	}

	@Override
	public void ancestorMoved(HierarchyEvent arg0) {
		
	}

	@Override
	public void ancestorResized(HierarchyEvent arg0) {
		if(gestartet){
			h = Display.getHeight();
			w = Display.getWidth();
		}
	}

	public Point getCirclePosition(float angle, float r, Point mittelPos){
		double x = Math.cos(angle)*r+mittelPos.x;
		double y = Math.sin(angle)*r+mittelPos.y;
		return new Point((int)x, (int)y);
		
	}
}

Warum ist das so unkomfortabel zu steuern und was kann man besser machen ?

Ich bin leider noch nicht so weit mit meiner 3D-Engine, aber ich habe gehört, dass Quaternions bei vielen Fehlern in Animationseffekten helfen können.

Schonmal versucht, mit Point2D.Float statt Point zu arbeiten?

Auch mit Point2D.Float funktionierts nicht richtig.

Moin,

Dein Beispiel läuft bei mir nicht:

[xcb] Unknown request in queue while dequeuing
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
java: xcb_io.c:179: dequeue_pending_request: Assertion `!xcb_xlib_unknown_req_in_deq' failed.

Mein Beispiel (OpenGL 1.5):



import static org.lwjgl.opengl.GL11.GL_COLOR_ARRAY;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_FLOAT;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_TRIANGLES;
import static org.lwjgl.opengl.GL11.GL_UNSIGNED_INT;
import static org.lwjgl.opengl.GL11.GL_VERTEX_ARRAY;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glColorPointer;
import static org.lwjgl.opengl.GL11.glDrawElements;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glEnableClientState;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glLoadMatrix;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glVertexPointer;
import static org.lwjgl.opengl.GL15.GL_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_ELEMENT_ARRAY_BUFFER;
import static org.lwjgl.opengl.GL15.GL_STATIC_DRAW;
import static org.lwjgl.opengl.GL15.glBindBuffer;
import static org.lwjgl.opengl.GL15.glBufferData;
import static org.lwjgl.opengl.GL15.glDeleteBuffers;
import static org.lwjgl.opengl.GL15.glGenBuffers;
import static org.lwjgl.util.glu.GLU.gluPerspective;

import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.ContextAttribs;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.PixelFormat;


public final class GL15FirstPersonControls implements Runnable {

    public static final String TITLE = "Old Fixed Function OpenGL 1.5";

    public static final int GL_MAJOR_VERSION = 1;
    public static final int GL_MINOR_VERSION = 5;

    private static final int[] IBO_VALUES = new int[] {

            0, 1, 2, 3, 4, 2, 5, 6, 7, 7, 6, 8

    };

    // x, y, z, r, g, b
    private static final float[] VBO_VALUES = new float[] {

            -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, 0.0f,
            +1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f,
            +0.0f, +1.0f, +0.0f, 0.0f, 0.0f, 1.0f,

            -1.0f, -1.0f, +1.0f, 1.0f, 0.0f, 0.0f,
            +1.0f, -1.0f, +1.0f, 0.0f, 1.0f, 0.0f,

            -10.0f, -1.0f, -10.0f, 1.0f, 0.5f, 0.5f,
            -10.0f, -1.0f, +10.0f, 0.5f, 1.0f, 0.5f,
            +10.0f, -1.0f, -10.0f, 0.5f, 0.5f, 1.0f,
            +10.0f, -1.0f, +10.0f, 1.0f, 0.5f, 0.5f,

    };

    private static final int FLOAT_SIZE = Float.SIZE / Byte.SIZE;

    private static final int WIDTH = 800;
    private static final int HEIGHT = 475;

    private final FloatBuffer viewMatrix = BufferUtils.createFloatBuffer(16);

    private final IBO ibo;
    private final VBO vbo;

    private float rotX, rotY, rotZ, rotV;
    private float posX, posY, posZ;

    private long stepTime;


    public static void main(final String[] args) throws LWJGLException {

        new GL15FirstPersonControls().run();

    }


    public GL15FirstPersonControls() throws LWJGLException {

        final DisplayMode displayMode = new DisplayMode(WIDTH, HEIGHT);
        final PixelFormat pixelFormat = new PixelFormat();
        final ContextAttribs contextAttribs = new ContextAttribs(GL_MAJOR_VERSION, GL_MINOR_VERSION);

        Display.setDisplayMode(displayMode);
        Display.create(pixelFormat, contextAttribs);
        Display.setVSyncEnabled(true);
        Mouse.setGrabbed(true);

        ibo = new IBO(IBO_VALUES);
        vbo = new VBO(VBO_VALUES);

    }


    @Override
    public void run() {

        glInit();

        while (!Display.isCloseRequested() && !Keyboard.isKeyDown(Keyboard.KEY_ESCAPE)) {

            final float deltaTime = -(stepTime - (stepTime = System.nanoTime())) / 100000000.0f;

            firstPersonControls(deltaTime, viewMatrix);

            glDisplay(deltaTime, viewMatrix);
            Display.update();

        }

        glDispose();
        Display.destroy();

    }


    private void glInit() {

        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        gluPerspective(45.0f, (float) WIDTH / (float) HEIGHT, 0.001f, 1000);

        glEnable(GL_DEPTH_TEST);

        glEnableClientState(GL_VERTEX_ARRAY);
        glEnableClientState(GL_COLOR_ARRAY);

        ibo.glInit();
        vbo.glInit();

        posZ = -5;

    }


    private void glDisplay(final float deltaTime, final FloatBuffer viewMatrix) {

        glMatrixMode(GL_MODELVIEW);
        glLoadMatrix(viewMatrix);

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        vbo.glBind();
        glVertexPointer(3, GL_FLOAT, 6 * FLOAT_SIZE, 0 * FLOAT_SIZE);
        glColorPointer(3, GL_FLOAT, 6 * FLOAT_SIZE, 3 * FLOAT_SIZE);

        ibo.glDraw(GL_TRIANGLES);

    }


    private void firstPersonControls(final float deltaTime, final FloatBuffer viewMatrix) {


        final double keySpeed = deltaTime * 0.2;
        final double mouseSpeed = 0.002;

        final float rotVmax = 1;
        final float rotVMin = -1;

        rotY += Mouse.getDX() * mouseSpeed;
        rotV -= Mouse.getDY() * mouseSpeed;

        if (rotV > rotVmax)
            rotV = rotVmax;

        if (rotV < rotVMin)
            rotV = rotVMin;

        if (Keyboard.isKeyDown(Keyboard.KEY_W)) {

            posX -= Math.sin(rotY) * keySpeed;
            posZ += Math.cos(rotY) * keySpeed;

        }

        if (Keyboard.isKeyDown(Keyboard.KEY_S)) {

            posX += Math.sin(rotY) * keySpeed;
            posZ -= Math.cos(rotY) * keySpeed;

        }

        if (Keyboard.isKeyDown(Keyboard.KEY_A)) {

            posX += Math.cos(rotY) * keySpeed;
            posZ += Math.sin(rotY) * keySpeed;

        }

        if (Keyboard.isKeyDown(Keyboard.KEY_D)) {

            posX -= Math.cos(rotY) * keySpeed;
            posZ -= Math.sin(rotY) * keySpeed;

        }

        rotX = (float) (Math.cos(rotY) * rotV);
        rotZ = (float) (Math.sin(rotY) * rotV);

        final float[] mv = new float[16];

        final double sinX = Math.sin(rotX);
        final double sinY = Math.sin(rotY);
        final double sinZ = Math.sin(rotZ);

        final double cosX = Math.cos(rotX);
        final double cosY = Math.cos(rotY);
        final double cosZ = Math.cos(rotZ);

        mv[0] = (float) (cosY * cosZ + sinY * sinX * sinZ);
        mv[1] = (float) (cosX * sinZ);
        mv[2] = (float) (-sinY * cosZ + cosY * sinX * sinZ);
        mv[4] = (float) (-cosY * sinZ + sinY * sinX * cosZ);
        mv[5] = (float) (cosX * cosZ);
        mv[6] = (float) (sinY * sinZ + cosY * sinX * cosZ);
        mv[8] = (float) (sinY * cosX);
        mv[9] = (float) (-sinX);
        mv[10] = (float) (cosY * cosX);

        mv[12] = mv[0] * posX + mv[4] * posY + mv[8] * posZ;
        mv[13] = mv[1] * posX + mv[5] * posY + mv[9] * posZ;
        mv[14] = mv[2] * posX + mv[6] * posY + mv[10] * posZ;
        mv[15] = 1;

        viewMatrix.position(0);
        viewMatrix.put(mv);
        viewMatrix.flip();

    }


    public void glDispose() {

        ibo.glDispose();
        vbo.glDispose();

    }


    private static class IBO {

        private final IntBuffer buffer;

        private int handle = -1;


        public IBO(final int[] indices) {

            buffer = BufferUtils.createIntBuffer(indices.length);
            buffer.put(indices);
            buffer.flip();

        }


        public final void glInit() {

            handle = glGenBuffers();

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

        }


        public final void glDraw(final int type) {

            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle);
            glDrawElements(type, buffer.capacity(), GL_UNSIGNED_INT, 0);

        }


        public final void glDispose() {

            glDeleteBuffers(handle);

        }


    }


    private static class VBO {

        private final FloatBuffer buffer;

        private int handle = -1;


        public VBO(final float[] values) {

            buffer = BufferUtils.createFloatBuffer(values.length);
            buffer.put(values);
            buffer.flip();

        }


        public final void glInit() {

            handle = glGenBuffers();

            glBindBuffer(GL_ARRAY_BUFFER, handle);
            glBufferData(GL_ARRAY_BUFFER, buffer, GL_STATIC_DRAW);
            glBindBuffer(GL_ARRAY_BUFFER, 0);

        }


        public final void glBind() {

            glBindBuffer(GL_ARRAY_BUFFER, handle);

        }


        public final void glDispose() {

            glDeleteBuffers(handle);

        }


    }


}

Gruß
Fancy

Zu deinem Fehler weiß ich nichts, aber zu dem Code hab ich ein Paar fragen:

  1. Wozu benutzt du diese “deltaTime” für das bewegen ? ist das nötig ?
    2.Die “ViewMatrix” versteh ich irgendwie nicht. Welche Zahlen in diesem Buffer bedeuten Was ? Liegt das nicht-funktionieren meines Codes daran und wenn ja, warum ?

Warum Dein Beispiel nicht richtig funktioniert kann ich nicht sagen. Ich bin mir noch nicht mal sicher ob das bei mir nicht läuft, weil in Deinem Code ein Fehler ist oder ob das ein Bug in LWJGL ist. Aber das ist LWJGL 2 und wird imho nicht mehr weiterentwickelt. In LWJGL 3 gibt es das Fenster nur noch über GLFW.

Wenn das Programm auf einem schnellen Rechner läuft, wird jedes Bild nur kurz dargestellt. Wird es auf einem langsamen Rechner ausgeführt eben länger. Damit man sich unabhängig des Rechners gleich schnell bewegt, muss die Zeit zwischen zwei Bildern berücksichtigt werden. Deshalb ja, deltaTime ist notwendig.

Die viewMatrix zu erklären ist etwas schwierig ohne zu wissen wie viel Du über OpenGL Matrizen weißt. Hier gibt es einen Überblick über die OpenGL Pipeline. Ansonsten gibt es viele Tutorials wenn Du nach “OpenGL Modelview Matrix” suchst.

Gruß
Fancy

Auf der Verlinkten Seite, was bedeutet da das w in den Matrizen ? (Da gibs ja 4 mal x,y,z und dann noch 4 mal w)

Das sind homogene Koordinaten. Damit wird es unter anderem möglich Rotation und Translation in einer Matrix abzubilden. Bei einem Vektor ist w immer 0 und bei einem Punkt immer 1, andere Werte haben erst mal keine geometrische Bedeutung.

Homogene Koordinaten haben tolle mathematische Eigenschaften. Für die Modelview Matrix braucht es davon aber nicht viel.

Gruß
Fancy

Danke, jetzt funktioniert das Umsehen auch gut.

Aber ich möchte herausfinden, wo in der Welt der Spieler hinschaut. Dazu brauche ich den Vektor von der Position des Spielers, zum Fokuspunkt, wo er hinsieht, aber wie bekomme ich das jetzt ?

In der Matrix ist das doch der Punkt (m8, m9, m10), oder ?