先上代码。
/// <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封装窗口事件模型,所以根据其特性键盘输入和鼠标输入分别在processInput和glfwPollEvents中处理,即键盘输入不作为事件。
摄像头状态由摄像头位置、摄像头方向确定,摄像头方向用欧拉角的模型,使用左右角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 评论区
star_outline 咱快来抢个沙发吧!