OpenGL: Kamera bewegen



  • Mir ist schon bewusst, dass es nicht gerade die beste Art ist, euch einfach den Code hinzuklatschen. Allerdings habe ich, wie schon geschrieben, bereits alles versucht und keine Loesung gefunden.

    Zur Speicherung der Rotation benutze ich eine 3x3 Matrix, die ich bei jeder Rotation veraendere. Jeden Frame erweitere ich diese 3x3 Matrix mit der Position der Kamera auf eine 4x4 Matrix (glm::mat4 from_rot_pos(glm::mat3 rot, glm::vec3 pos)), multipliziere diese dann mit der Projektionsmatrix und uebergebe sie anschliessend an den Vertex-Shader.

    Mein Problem ist nun, die richtige Veraenderung dieser Rotationsmatrix zu berechnen. Dazu habe ich mir aus Wikipedia die allgemeine Rotationsmatrix um eine Achse kopiert und in die Funktion glm::mat3 rotate_axis(glfloat angle, glm::vec3 axis) gepackt. Ich bin mir ziemlich sicher, dass ich diese Achsen, um die gedreht werden soll, einfach nicht richtig berechne.



  • dd++: Was hat die Projektionsmatrix mit der View-/Camera-Matrix zu tun?



  • Hallo 🙂

    Kellerautomat man muss sich einfach entscheiden ob man ein Objekt dreht
    ( zb die Wetterfahne auf einem FESTSTEHENDEM Turm )
    oder ob man eine Kamera simuliert.
    ( Wetterfahne und Turm bewegen sich gemeinsam )
    Wenn man das einmal geistig verinnerlicht hat kann man auch Beides gemeinsam realisieren.

    Fürs "Objekt drehen" haben Dir dot und Nathan Hintergrund und Beispiel geliefert.

    Für die Kamera hat Dir dd++ den Hintergrund geliefert
    und sollte Dir mein Beispiel zu alt sein gibts reichlich im Netz
    denn eine Kamera birgt noch mehr Tücken als nur die Projektion.

    Gibts jetzt Kekse ? 😃



  • Ehm... mir ist der Unterschied zwischen eine Kameradrehung und einer Objektdrehung schon klar...
    Das Problem liegt in der Implementierung der Kamera, da ich keine Rotation um deren lokale Achsen hinbekomme...



  • Wenn Du eine kamera rotierst kann Dein Wuerfel sich aus dem Sichtfeld bewegen
    ist das wirklich was Du haben willst ?



  • Ja, das wollte ich von Anfang an. Jedenfalls hab ich das Problem jetzt dank cookys IRC-Hilfe geloest. Das Problem war, wie schon gedacht, das berechnen der Rotationsachsen. Das mache ich nun so:

    auto rotx = glm::vec3{ camrot[0][0], camrot[1][0], camrot[2][0] };
    		auto roty = glm::vec3{ camrot[0][1], camrot[1][1], camrot[2][1] };
    		auto rotz = glm::vec3{ camrot[0][2], camrot[1][2], camrot[2][2] };
    

    Und auf die Tastendruecke reagiere ich dann hiermit:

    if(glfwGetKey('W'))
    			camrot *= rotate_axis(camera_rotation, rotx);
    
    		if(glfwGetKey('S'))
    			camrot *= rotate_axis(-camera_rotation, rotx);
    
    		if(glfwGetKey('A'))
    			camrot *= rotate_axis(camera_rotation, roty);
    
    		if(glfwGetKey('D'))
    			camrot *= rotate_axis(-camera_rotation, roty);
    
    		if(glfwGetKey('Q'))
    			camrot *= rotate_axis(camera_rotation, rotz);
    
    		if(glfwGetKey('E'))
    			camrot *= rotate_axis(-camera_rotation, rotz);
    


  • Asche über mein Haupt ><

    Bis zum letzten Post hätt ich schwören können
    da ist ein bewegter Würfel im Spiel.

    Dann muss ich wohl die Kekse rüberreichen 🙂



  • Der Wuerfel ist auch bewegt, nur dass die entsprechende Zeile auskommentiert ist 😉



  • Du musst einfach die richtige Rotationsreihenfolge verwenden. In deinem Fall dürfte das ZXY sein. Multiplizier nicht bei Tatstendrücken Rotationen zusammen, sondern merk dir die Rotationswinkel und bau die Matrix dann damit...



  • Was heisst denn 'richtige' Rotationsreihenfolge?



  • Was genau ist daran unklar? Die Reihenfolge, in der Rotationen angewendet werden, macht einen Unterschied!? Ergo ist es absolut nicht egal, in welcher Reihenfolge du deine X, Y, Z Rotationen zusammenmultiplizierst...



  • Das ist mir schon klar, aber warum "In deinem Fall dürfte das ZXY sein.". Und warum nicht zusammenmultiplizieren? Damit vermeide ich das Problem doch?!



  • Drück mal ein Weilchen auf A, dann ein Weilchen auf W und dann ein Weilchen auf Q und sag mir, ob die beobachtete Bewegung deiner Kamera dem entspricht, was du haben willst.



  • Im Moment: Ja. Warum?



  • Ah, du berechnest nun die Achsen ständig neu, das ist mir irgendwie entgangen. Numerisch ist das ganze vermutlich suboptimal, aber wenns für dich funktioniert, dann 👍



  • Alternativen?
    Und sind Quaternionen eigentlich einen Blick wert?



  • Kellerautomat schrieb:

    Alternativen?

    Wie gesagt, die Winkel addieren und die Matrix dann entsprechend der Sequenz ZYX aus absoluten Winkeln erzeugen, anstatt inkrementelle Rotationsmatritzen aufzumultiplizieren...

    Kellerautomat schrieb:

    Und sind Quaternionen eigentlich einen Blick wert?

    Ja, aber in dem konkreten Fall werden sie kaum was bringen.



  • Hm. Irgendwie funktioniert das jetzt doch nicht so ganz. Ich habe mal ein paar Wuerfel zufaellig generiert. So sieht das Ding aus: http://img5.fotos-hochladen.net/uploads/screenshot201mpjokgwani.png
    Wenn ich nun die Rotationstasten verwende, dreht sich nicht die Kamera, sondern der grosse Wuerfel. Ich weiss nicht, wieso mir das erst jetzt auffaellt, aber das liegt wohl daran, dass ich etwas verwirrt bin, was das ganze angeht.
    Um es noch einmal klar zu machen: Ich moechte, dass die Kamera sich selbst dreht, da ich ein Minecraft-aehnliches Spiel machen moechte.
    Hier mein aktueller Code:

    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    #include <iostream>
    
    #include <glm/glm.hpp>
    #include <glm/gtc/matrix_transform.hpp>
    
    #include "cube_model.hpp"
    #include "error.hpp"
    #include "gl.hpp"
    #include "shader.hpp"
    #include "shader_list.hpp"
    #include "shader_program.hpp"
    #include "types.hpp"
    
    #include "buffer.hpp"
    #include "stopwatch.hpp"
    
    glm::mat4 from_rot_pos(glm::mat3 rot, glm::vec3 pos)
    {
    	return glm::mat4
    	{
    		rot[0][0], rot[0][1], rot[0][2], 0.0f,
    		rot[1][0], rot[1][1], rot[1][2], 0.0f,
    		rot[2][0], rot[2][1], rot[2][2], 0.0f,
    		   pos[0],    pos[1],    pos[2], 1.0f,
    	};
    }
    
    glm::mat3 rotate_axis(glfloat angle, glm::vec3 axis)
    {
    	auto sin = std::sin(angle);
    	auto cos = std::cos(angle);
    
    	auto x = axis.x;
    	auto y = axis.y;
    	auto z = axis.z;
    
    	auto omc = 1 - cos;
    
    	return glm::mat3
    	{
    		x * x * omc + cos,     x * y * omc - z * sin, x * z * omc + y * sin,
    		x * y * omc + z * sin, y * y * omc + cos,     y * y * omc - x * sin,
    		x * z * omc - y * sin, y * z * omc + x * sin, z * z * omc + cos,
    	};
    }
    
    int main()
    {
    	int const width = 1024;
    	int const height = 768;
    
    	int const world_width = 10;
    	int const world_height = 10;
    	int const world_depth = 10;
    
    	std::srand(std::time(0));
    
    	create_context(width, height, "0 fps");
    
    	glEnable(GL_DEPTH_TEST);
    	glDepthFunc(GL_LESS);
    
    	glEnable(GL_CULL_FACE);
    
    	shader_list l;
    	l += shader("test.vs", shader_type::vertex);
    	l += shader("test.fs", shader_type::fragment);
    	shader_program sp(l);
    
    	gluint vao;
    	glGenVertexArrays(1, &vao);
    	glBindVertexArray(vao);
    
    	buffer cube_vertex_buffer(cube_vertices, sizeof cube_vertices, GL_ARRAY_BUFFER);
    	buffer cube_index_buffer(cube_indices, sizeof cube_indices, GL_ELEMENT_ARRAY_BUFFER);
    
    	glEnableVertexAttribArray(0);
    	glEnableVertexAttribArray(1);
    	glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(glfloat), nullptr);
    	glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 8 * sizeof(glfloat), reinterpret_cast<void*>(4 * sizeof(glfloat)));
    
    	glClearColor(0.5f, 0.5f, 0.5f, 10.f);
    
    	auto uf_proj_cam_mat = sp.uniform("proj_cam_mat");
    	auto uf_obj_transf_mat = sp.uniform("obj_transf_mat");
    
    	auto projection_matrix = glm::perspective(90.0f, static_cast<glfloat>(width) / height, 0.1f, 20.0f);
    
    	glm::vec3 campos;
    	glm::mat3 camrot;
    
    	std::vector<glm::mat4> cubes;
    
    	for(int i = 0; i != world_width; ++i)
    		for(int j = 0; j != world_height; ++j)
    			for(int k = 0; k != world_depth; ++k)
    				if(std::rand() % 2)
    					cubes.push_back(glm::translate(glm::mat4{}, glm::vec3{ i - world_width / 2, j - world_height / 2, k - world_depth / 2 }));
    
    	glfloat const camera_movement_speed = 2.0f;
    	glfloat const camera_rotation_speed = 0.5f;
    
    	stopwatch<> sw;
    
    	glfloat prev_fps;
    
    	while(!glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam(GLFW_OPENED))
    	{
    		check_errors();
    
    		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
    		auto proj_cam_mat = projection_matrix * from_rot_pos(camrot, campos);
    		glUniformMatrix4fv(uf_proj_cam_mat, 1, GL_FALSE, &proj_cam_mat[0][0]);
    
    		for(auto& cube : cubes)
    		{
    			glUniformMatrix4fv(uf_obj_transf_mat, 1, GL_FALSE, &cube[0][0]);
    			glDrawElements(GL_TRIANGLES, sizeof cube_indices / sizeof *cube_indices, GL_UNSIGNED_BYTE, nullptr);
    		}
    
    		glfwSwapBuffers();
    
    		auto elapsed = duration_cast<glfloat>(sw.tick());
    		auto fps = static_cast<unsigned>(1.0f / elapsed);
    
    		if(fps != prev_fps)
    		{
    			auto title = std::to_string(fps) + " fps";
    			glfwSetWindowTitle(title.c_str());
    			prev_fps = fps;
    		}
    
    		auto camera_translation = elapsed * camera_movement_speed;
    		auto camera_rotation = elapsed * camera_rotation_speed;
    
    		if(glfwGetKey(GLFW_KEY_UP))
    			campos += glm::vec3{ 0.0f, 0.0f, camera_translation };
    
    		if(glfwGetKey(GLFW_KEY_DOWN))
    			campos += glm::vec3{ 0.0f, 0.0f, -camera_translation };
    
    		if(glfwGetKey(GLFW_KEY_LEFT))
    			campos += glm::vec3{ camera_translation, 0.0f, 0.0f };
    
    		if(glfwGetKey(GLFW_KEY_RIGHT))
    			campos += glm::vec3{ -camera_translation, 0.0f, 0.0f };
    
    		auto rotx = glm::vec3{ camrot[0][0], camrot[1][0], camrot[2][0] };
    		auto roty = glm::vec3{ camrot[0][1], camrot[1][1], camrot[2][1] };
    		auto rotz = glm::vec3{ camrot[0][2], camrot[1][2], camrot[2][2] };
    
    		if(glfwGetKey('W'))
    			camrot *= rotate_axis(camera_rotation, rotx);
    
    		if(glfwGetKey('S'))
    			camrot *= rotate_axis(-camera_rotation, rotx);
    
    		if(glfwGetKey('A'))
    			camrot *= rotate_axis(camera_rotation, roty);
    
    		if(glfwGetKey('D'))
    			camrot *= rotate_axis(-camera_rotation, roty);
    
    		if(glfwGetKey('Q'))
    			camrot *= rotate_axis(camera_rotation, rotz);
    
    		if(glfwGetKey('E'))
    			camrot *= rotate_axis(-camera_rotation, rotz);
    	}
    
    	destroy_context();
    }
    

    Die Vertizes und Indizes habe ich in den Header "cube_model.hpp" ausgelagert und sind unveraendert. Hier auch noch mein Vertexshader, nur um sicher zu gehen.

    #version 330
    
    layout(location = 0) in vec4 position;
    layout(location = 1) in vec4 color;
    
    uniform mat4 proj_cam_mat;
    uniform mat4 obj_transf_mat;
    
    smooth out vec4 fragment_color;
    
    void main()
    {
        gl_Position = proj_cam_mat * obj_transf_mat * position;
        fragment_color = color;
    }
    


  • Das liegt daran dass du, wie bereits gesagt wurde, eigentlich die Inverse der Matrix, die du als Kameramatrix verwendest, verwenden solltest... 😉



  • ???
    Ich hab jetzt mal ein inverse() hinzugefuegt, und in der Anfangsposition verhaelt sich die Kamera auch richtig. Aber sobald ich in eine Richtung rotiere, habe ich wieder den Effekt, dass die Rotationsachsen mit den Tasten vertauscht sind...


Anmelden zum Antworten