Aquacolor

Aquacolor



LearningOpenGL【9】

Gumdrop · 2025-09-04 · 13浏览 · 未分类


先上代码。

/// <summary>
/// 实现render loop。
/// </summary>
FrameState::lastTime = 0.0f;
FrameState::lastX = cwindow.width / 2.0;
FrameState::lastY = cwindow.height / 2.0;
FrameState::yaw = -90.0f;
FrameState::pitch = 0.0f;
FrameState::cameraPos = glm::vec3(0.0f, 0.0f, 10.0f);
FrameState::cameraFront = glm::vec3(0.0f, 0.0f, -1.0f);
FrameState::cameraUp = glm::vec3(0.0f, 1.0f, 0.0f);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetFramebufferSizeCallback(window,
	[](GLFWwindow* window, int width, int height) {glViewport(0, 0, width, height); });
while (!glfwWindowShouldClose(window))
{
	FrameState::currentTime = glfwGetTime();
	FrameState::deltaTime = FrameState::currentTime - FrameState::lastTime;

	processInput(window);

	FrameState::lastTime = FrameState::currentTime;

	glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
	glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

	shader.use();

	glActiveTexture(GL_TEXTURE0);
	glBindTexture(GL_TEXTURE_2D, texture0);
	glActiveTexture(GL_TEXTURE1);
	glBindTexture(GL_TEXTURE_2D, texture1);

	glm::mat4 transform = glm::mat4(1.0f);
	//transform = glm::translate(transform, glm::vec3(0.5f, -0.5f, 0.0f));
	//transform = glm::scale(transform, glm::vec3(0.5f, 1.0f, 1.0f));
	//transform = glm::rotate(transform, (float)glm::radians(glfwGetTime()*18), glm::vec3(0.0f, 1.0f, 1.0f));

	glBindVertexArray(VAO);
	glm::mat4 model = glm::mat4(1.0f);
	model = glm::rotate(model, glm::radians(30.0f), glm::vec3(1.0f, 0.0f, 0.0f));
	model = glm::rotate(model, glm::radians(30.0f), glm::vec3(0.0f, 1.0f, 0.0f));
	glm::mat4 view = glm::mat4(1.0f);
	view = glm::lookAt(FrameState::cameraPos, FrameState::cameraPos + FrameState::cameraFront, FrameState::cameraUp);
	glm::mat4 projection = glm::mat4(1.0f);
	projection = glm::perspective(glm::radians(45.0f), (float)cwindow.width / cwindow.height, 0.1f, 100.0f);
	shader.setInt("texture0", 0);
	shader.setInt("texture1", 1);
	//shader.setMat4("transform", glm::value_ptr(transform));
	shader.setMat4("view", glm::value_ptr(view));
	shader.setMat4("projection", glm::value_ptr(projection));
	for (int x = -1; x <= 1; ++x) {
		for (int y = -1; y <= 1; ++y)
		{
			auto tmodel = glm::translate(glm::mat4(1.0f), glm::vec3(x * 2.0f, y * 2.0f, 0.0f));
			shader.setMat4("model", glm::value_ptr(tmodel));
			//glDrawElements(GL_TRIANGLES, sizeof(indices) / sizeof(float), GL_UNSIGNED_INT, 0);
			glDrawArrays(GL_TRIANGLES, 0, 36);
		}
	}

	glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
	glfwSwapBuffers(window);
	glfwPollEvents();
}

void processInput(GLFWwindow* window) {
	constexpr float cameraSpeed = 4.0f;

	if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS) {
		glfwSetWindowShouldClose(window, true);
	}
	if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
		FrameState::cameraPos += cameraSpeed * (float)FrameState::deltaTime * FrameState::cameraFront;
	}
	if(glfwGetKey(window,GLFW_KEY_S) == GLFW_PRESS) {
		FrameState::cameraPos -= cameraSpeed * (float)FrameState::deltaTime * FrameState::cameraFront;
	}
	if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
		FrameState::cameraPos -= glm::normalize(glm::cross(FrameState::cameraFront, FrameState::cameraUp)) * cameraSpeed * (float)FrameState::deltaTime;
	}
	if(glfwGetKey(window,GLFW_KEY_D) == GLFW_PRESS) {
		FrameState::cameraPos += glm::normalize(glm::cross(FrameState::cameraFront, FrameState::cameraUp)) * cameraSpeed * (float)FrameState::deltaTime;
	}
	if(glfwGetKey(window,GLFW_KEY_LEFT_SHIFT) == GLFW_PRESS) {
		FrameState::cameraPos -= cameraSpeed * (float)FrameState::deltaTime * FrameState::cameraUp;
	}
	if(glfwGetKey(window,GLFW_KEY_SPACE) == GLFW_PRESS) {
		FrameState::cameraPos += cameraSpeed * (float)FrameState::deltaTime * FrameState::cameraUp;
	}


void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
	constexpr float sensitivity = 0.01f;

	FrameState::currentX = xpos;
	FrameState::currentY = ypos;
	FrameState::deltaX = FrameState::currentX - FrameState::lastX;
	FrameState::deltaY = FrameState::currentY - FrameState::lastY;

	FrameState::yaw += FrameState::deltaX * sensitivity;
	FrameState::pitch -= FrameState::deltaY * sensitivity;
	FrameState::pitch = FrameState::pitch > 89.99f ? 89.99f : FrameState::pitch;
	FrameState::pitch = FrameState::pitch < -89.99f ? -89.99f : FrameState::pitch;

	FrameState::cameraFront = glm::normalize(glm::vec3(
		cos(glm::radians(FrameState::pitch)) * cos(glm::radians(FrameState::yaw)),
		sin(glm::radians(FrameState::pitch)),
		cos(glm::radians(FrameState::pitch)) * sin(glm::radians(FrameState::yaw))
	));

	FrameState::lastX = FrameState::currentX;
	FrameState::lastY = FrameState::currentY;
}

摄像头运动的重点在于将摄像头的状态参数化,并且选择的参数要能和输入设备对应。由于使用了GLFW封装窗口事件模型,所以根据其特性键盘输入和鼠标输入分别在processInputglfwPollEvents中处理,即键盘输入不作为事件。

摄像头状态由摄像头位置、摄像头方向确定,摄像头方向用欧拉角的模型,使用左右角yaw和俯仰角pitch确定。上方向始终不变动,这样在用glm::lookAt计算view trans时,得到的摄像头上方向和正方向所在平面始终垂直于水平面(即正视)。

在算法实现上也有一些技巧。为了避免processInput传入过多参数或者需要通过全局变量来为回调函数传参,可以使用静态成员变量或者在类中为函数声明友元。目前使用的是最少抽象的方式。

struct FrameState
{
	static inline double lastTime;
	static inline double deltaTime;
	static inline double currentTime;
	static inline glm::vec3 cameraPos;
	static inline glm::vec3 cameraFront;
	static inline glm::vec3 cameraUp;
	static inline double lastX;
	static inline double lastY;
	static inline double deltaX;
	static inline double deltaY;
	static inline double currentX;
	static inline double currentY;
	static inline double yaw;
	static inline double pitch;
};

(如果写成一个复杂的类的话可能还更难看懂吧)



©

comment 评论区

添加新评论

face表情



  • ©2026 bilibili.com

textsms
内容不能为空
昵称不能为空
email
邮件地址格式错误
web
beach_access
验证码不能为空
keyboard发表评论


star_outline 咱快来抢个沙发吧!




©2026 Aquacolor

Theme Romanticism2.2 by Akashi
Powered by Typecho