OpenGL: Kamera bewegen



  • 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...



  • Eine Rotationsmatrix wie du sie gebaut hast, transformiert aus lokalen Koordinaten in das Koordinatensystem in dem sich die Achsen befinden, in deinem Fall also Weltkoordinaten. Eine Kameramatrix sollte dagegen von Weltkoordinaten in Kamerakoordinaten (Kamera im Ursprung und blickt z.B. entlang der z-Achse) transformieren, was also genau der umgekehrten Transformation deiner Rotationsmatrix entspricht. Genau das meinte pVoid wohl auch in der ersten Antwort hier, von wegen dass man "in OpenGL nicht die Kamera bewegt, sondern den Raum": Eine Bewegung der Kamera ist äquivalent der inversen Bewegung der Welt; du kannst entweder deinen Kopf um 30° nach links drehen oder die ganze Welt um dich herum um 30° nach rechts drehen und wirst in beiden Fällen das selbe Ergebnis erhalten. Wir wollen am Ende ein Bild am Bildschirm ausgeben und dafür brauchen wir Koordinaten relativ zum Bildschirm, was bedeutet, dass wir die Welt um den Bildschirm herum bewegen müssen, da wir den Bildschirm selbst ja nicht bewegen können...



  • Okay, danke fuer die Erklaerung. Offenbar ist es jetzt doch noch 'falscher'. Schon 'langsam' verlier ich auch die Lust an diesem ganzen OpenGL Kram. Ich sitze hier seit Tagen an diesem bekloppten Ding und habe
    - Ein paar Wuerfel, zufaellig generiert
    - Eine nicht funktionierende Kamera
    Ist es zu viel verlangt, dass das Zeug *ein einziges Mal* funktioniert, so wie ich mir das vorstelle?





  • Ich will keine Engine verwenden, ich will dass der Mist einfach funktioniert 😡



  • Kellerautomat schrieb:

    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.

    W - Welt,
    V - View.
    R - Rotation

    VW - wir sind Kamerasystem (lokal -> World -> View)

    VWR - rotiert objekt lokal (lokal -> drehen -> World -> View)

    VRW - rotiert Welt ( lokal -> Welt -> drehen -> View) - beachte das ist analog dazu die Kamera um den Weltursprung zu rotieren (Orbit).

    RVW - rotiert kamera! (lokal -> Welt -> View -> drehen) - rotiert kamera um den lokalen Kamera Ursprung.

    die Reihenfolge ist wichtig. Aber tatsächlich heisst das auch, dass du die Kamera wie jedes andere Objekt auch drehts, von links dran.



  • Kellerautomat schrieb:

    Ich will keine Engine verwenden, ich will dass der Mist einfach funktioniert 😡

    Engine? I see no engine if I take a look in nehe´s tutorial section.

    💡 💡

    Texture Filters, Lighting & Keyboard Control
    http://nehe.gamedev.net/tutorial/texture_filters,_lighting_&_keyboard_control/15002/

    It´s legacy, I know but it should help you anyway.



  • Kellerautomat schrieb:

    Ich will keine Engine verwenden, ich will dass der Mist einfach funktioniert 😡

    Warum eigentlich nicht? Mit sowas wie Irrlicht müsstest du dich nicht dauernd mit Low-Level-Angelegenheiten herumschlagen, vieles gäbe es schon fertig, und du bräuchtest massiv weniger Code.

    Indem du rohes OpenGL benutzt, nimmst du entsprechende Mühseligkeiten geradezu in Kauf 😉



  • Irrlicht ist ein haessliches Drecksstueck, die anderen Engines sind da auch nicht besser und wenn doch viel zu ueberladen.


Anmelden zum Antworten