Jak zrobić obracanie kamery 3D bez żadnych ograniczeń używając SDL

0

Cześć! Próbuje zrobić obracanie kamery typu FPS i problemem, który napotkałem to właśnie ograniczenie obracania się w prawo i lewo. Czytałem, że podobno relative mouse mode powinien pomóc i ustawienie xrel i yrel jako pozycje myszki, ale to też nie pomaga.

#define SCREEN_WIDTH 1280
#define SCREEN_HEIGHT 720

float lastX = SCREEN_WIDTH / 2, lastY = SCREEN_HEIGHT / 2;
float pitch = 0.0f, yaw = -90.0f;
float sensitivity = 0.3f;

glm::vec3 MouseMotion(float x, float y);

int main(int argc,char*args[])
{
	SDL_Init(SDL_INIT_EVERYTHING);
	
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 4);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);

	SDL_Window* window = SDL_CreateWindow("fdas", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, SCREEN_WIDTH, SCREEN_HEIGHT, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
	SDL_GLContext context = SDL_GL_CreateContext(window);

	glViewport(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);

	SDL_GL_SetSwapInterval(true);

	glewExperimental = true;
	glewInit();

	glEnable(GL_DEPTH_TEST);
	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);

	SDL_ShowCursor(SDL_DISABLE);
	SDL_SetRelativeMouseMode(SDL_TRUE);

	Shader shader;
	Texture texture;
	GLuint tex = texture.LoadFromFile("wooden.png");

	std::string vertex = shader.LoadFromFile("vertex.shader");
	std::string fragment = shader.LoadFromFile("fragment.shader");
	shader.CreateShaderProgram(vertex.c_str(), fragment.c_str());
	shader.useProgram();

	Mesh mesh;
	mesh.CreateVertexArray();

	bool quit = false;
	SDL_Event event;

	std::cout << "OPENGL version: " << glGetString(GL_VERSION) << std::endl;
	std::cout << "SHADER version: " << glGetString(GL_SHADING_LANGUAGE_VERSION) << std::endl;
	std::cout << "GLEW version: "   << glewGetString(GLEW_VERSION) << std::endl;

	float moveSpeed = 1.5;

	glm::vec3 cameraPos   = glm::vec3(0.0f, 0.0f,  3.0f);
	glm::vec3 cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
	glm::vec3 cameraUp    = glm::vec3(0.0f, 1.0f,  0.0f);

	const Uint8* keyState = SDL_GetKeyboardState(NULL);

	GLint u_textureSampler = shader.GetUniformLocation("u_textureSampler");
	glUniform1f(u_textureSampler, 0);

	Rect destRect = { -0.5f, -0.5f, 1.0f, 1.0f };
	Rect uvRect = { 0.0f, 0.0f, 1.0f, 1.0f };
	ColorRGBA color = { 255, 0, 0, 128 };

	glm::vec3 cubePositions[] = {
		glm::vec3(0.0f,  0.0f,  0.0f),
		glm::vec3(2.0f,  5.0f, -15.0f),
		glm::vec3(-1.5f, -2.2f, -2.5f),
		glm::vec3(-3.8f, -2.0f, -12.3f),
		glm::vec3(2.4f, -0.4f, -3.5f),
		glm::vec3(-1.7f,  3.0f, -7.5f),
		glm::vec3(1.3f, -2.0f, -2.5f),
		glm::vec3(1.5f,  2.0f, -2.5f),
		glm::vec3(1.5f,  0.2f, -1.5f),
		glm::vec3(-1.3f,  1.0f, -1.5f)
	};

	float deltaTime = 0.0f;
	while (!quit)
	{
		deltaTime = 1 / 144.0f;
		moveSpeed = 0.5;

		while (SDL_PollEvent(&event))
		{
			if (event.type == SDL_QUIT)
			{
				exit(0);
			}
			if (event.type == SDL_MOUSEMOTION)
			{
				cameraFront = MouseMotion(event.motion.x, event.motion.y);
				SDL_WarpMouseInWindow(window, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2);
			}
		}

		float time = (float)SDL_GetTicks()/1000;

		if (keyState[SDL_SCANCODE_W]) {
			cameraPos += moveSpeed * cameraFront * deltaTime;
		} else if (keyState[SDL_SCANCODE_S]) {
			cameraPos -= moveSpeed * cameraFront * deltaTime;
		} else if (keyState[SDL_SCANCODE_A]) {
			cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * moveSpeed * deltaTime;
		} else if (keyState[SDL_SCANCODE_D]) {
			cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * moveSpeed * deltaTime;
		} 
		
		if (keyState[SDL_SCANCODE_SPACE]) {
			cameraPos += cameraUp * moveSpeed * deltaTime;
		} else if (keyState[SDL_SCANCODE_LSHIFT]) {
			cameraPos -= cameraUp * moveSpeed * deltaTime;
		}

		if (keyState[SDL_SCANCODE_ESCAPE]) {
			exit(0);
		}

		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		//P
		glm::mat4 projection = glm::mat4(1.0f);
		projection = glm::perspective(glm::radians(45.0f), 16/9.0f, 0.1f, 100.0f);

		GLint u_projection = shader.GetUniformLocation("u_projection");
		glUniformMatrix4fv(u_projection, 1, GL_FALSE, glm::value_ptr(projection));

		//V
		glm::mat4 view = glm::mat4(1.0f);
		view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);

		GLint u_view = shader.GetUniformLocation("u_view");
		glUniformMatrix4fv(u_view, 1, GL_FALSE, glm::value_ptr(view));

		//M
		for (int i = 0; i < 10; ++i)
		{
			for (int j = 0; j < 10; ++j)
			{
				glm::mat4 model = glm::mat4(1.0f);
				model = glm::translate(model, glm::vec3(i * 1.0, 0.0f, j * 1.0));
				model = glm::rotate(model, glm::radians(90.0f), glm::vec3(1.0f, 0.0f, 0.0f));

				GLint u_model = shader.GetUniformLocation("u_model");
				glUniformMatrix4fv(u_model, 1, GL_FALSE, glm::value_ptr(model));

				mesh.CreateQuad(destRect, uvRect, color);
				mesh.Draw(tex);
			}
		}
		//SDL_WarpMouseInWindow(window, SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2); <- próbowałem w 2 różnych miejsach
		SDL_GL_SwapWindow(window);
	}
	return 0;
}
glm::vec3 MouseMotion(float x, float y)
{
	float offsetX = x - lastX;
	float offsetY = lastY - y;
	lastX = x;
	lastY = y;

	yaw += offsetX * sensitivity;
	pitch += offsetY * sensitivity;

	if (pitch > 89.0f)
		pitch = 89.0f;
	if (pitch < -89.0f)
		pitch = -89.0f;

	glm::vec3 front;
	front.x = std::cos(glm::radians(yaw)) * std::cos(glm::radians(pitch));
	front.y = std::sin(glm::radians(pitch));
	front.z = std::sin(glm::radians(yaw)) * std::cos(glm::radians(pitch));

	//std::cout << "Mouse: " << offsetX << ", " << offsetY << std::endl;
	//std::cout << "YAW AND PITCH: " << yaw << ", " << pitch << std::endl;

	return glm::normalize(front);
}

Nie ma nałożonego żadnego ograniczenia na oś X a jednak moge tylko obracać się do pewnego stopnia ze względu na to, że mysz dojdzie do krańca ekranu i nie może ruszyć się dalej.

W jaki sposób znieść to ograniczenie?

0

Wyrzuć te WarpMouseInWindow i zamiast event.motion.x i .y używaj event.motion.xrel i .yrel jako różnicy między obecną a poprzednią pozycją myszy.

       glm::mat4 projection = glm::mat4(1.0f);
       projection = glm::perspective(glm::radians(45.0f), 16/9.0f, 0.1f, 100.0f);

jeśli to się nigdy nie zmienia to wyrzuć przed pętlę (chociaż jest szansa że kompilator to zoptymalizuje i nie będzie przeliczać macierzy w każdej iteracji – jednak lepiej pisać od razu wydajnie)

           GLint u_model = shader.GetUniformLocation("u_model");

To też możesz przenieść przed pętlę – wartość zwracana przez GetUniformLocation jest specyficzna dla danego shadera, na pewno się sama nie zmieni.

0

Czyli po prostu zamiast motion.x użyć motion.xrel. Tylko, że wtedy ekran się trzęsie, a nie obraca.

  glm::mat4 projection = glm::mat4(1.0f);
  projection = glm::perspective(glm::radians(45.0f), 16/9.0f, 0.1f, 100.0f);

jeśli to się nigdy nie zmienia to wyrzuć przed pętlę (chociaż jest szansa że kompilator to zoptymalizuje i nie będzie przeliczać macierzy w każdej iteracji – jednak lepiej pisać od razu wydajnie)

będę zmieniać przybliżenie pod skrolem, więc niech zostanie tak jak jest.

GLint u_model = shader.GetUniformLocation("u_model");

To też możesz przenieść przed pętlę – wartość zwracana przez GetUniformLocation jest specyficzna dla danego shadera, na pewno się sama nie zmieni.

Racja, zrobione.

0

Czyli po prostu zamiast motion.x użyć motion.xrel. Tylko, że wtedy ekran się trzęsie, a nie obraca.

Nie tak całkiem „po prostu”, xrel to przesunięcie od ostatniego eventu, nie pozycja.

0

Nie tak całkiem „po prostu”, xrel to przesunięcie od ostatniego eventu, nie pozycja.

Aaha, czyli zamiast lastX i lastY użyć xrel i yrel? Tak czy siak nie działa poprawnie.

0

Możesz spróbować tak: SDL_SetRelativeMouseMode(SDL_TRUE); - uwaga, wskaźnik znika i nie da rady zamknąć okna wskaźnikiem gryzonia.

Nie jestem pewien czy te xrel i yrel zadziała, bo wychodząc poza obszar okna sdl nie rejestruje zdarzeń od myszy, a koordynaty położenia wskaźnika liczone są od okna, chyba że da radę pobrać bezwględne koordynaty.

0

Robiłem już z SDL_SetRelativeMouseMode(SDL_TRUE); i efekt jest taki jak mówiłem wyżej.

0

Ja mam tak:

else if (event.type == SDL_MOUSEMOTION)
{
    glm::vec2 diff = 0.01f*mouseSensitive*glm::vec2(event.motion.xrel, event.motion.yrel);

    if (event.button.button == 1)
    {
        camera->changeBeta(diff.y);
        camera->changeAlfa(diff.x);
    }
    else if (event.button.button == 4)
    {
        camera->changeDistance(2.0f*diff.y);
    }
}

z ustawionym SDL_SetRelativeMouseMode(SDL_TRUE); i działa, bez żadnych trzęsień, może za dużą czułość myszy masz ustawioną?

0

Czułość zmieniałem i nadal. Znaczy, problem z xrelem i y relem jest taki, że albo po każdym jednym ruchu wraca na środek ekranu co powoduje, że nie mogę w ogóle obrócić, albo jakieś dziwne obroty się dzieją, np. ciągnę myszkę w dół, a kamera obraca się w prawo (teraz dokładnie nie powiem, bo nie chce mi się kodu zmieniać, ale nie ma to większego znaczenia). Może mam coś źle z metodą, którą obracam kamere? Albo przy lookat-cie

0
SiemkaElkoTszyPiencZerko napisał(a):

Nie tak całkiem „po prostu”, xrel to przesunięcie od ostatniego eventu, nie pozycja.

Aaha, czyli zamiast lastX i lastY użyć xrel i yrel? Tak czy siak nie działa poprawnie.

Nie. xrel to jest różnica między aktualną a poprzednią pozycją.

1 użytkowników online, w tym zalogowanych: 0, gości: 1