摄像机
需要三个向量:
摄像机位置:
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);- 一个从世界原点指向摄像机的向量
摄像机方向:
1 2
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f); glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
指向摄像机的z轴正方向(与摄像机面对的方向刚好相反)
右轴:
需要一个右向量代表摄像机的x轴,利用叉乘
1 2
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f); glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
up即为向空间正上方的一个向量(大部分情况下这么做够用了)
Look At矩阵
一个变换矩阵,可以将位置变换到摄像机空间
创建一个看着给定目标的矩阵
1 2 3 4
glm::mat4 view; view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),//相机位置 glm::vec3(0.0f, 0.0f, 0.0f), //目标 glm::vec3(0.0f, 1.0f, 0.0f));//上向量
- 输入需要一个位置、目标、上向量
自由移动
定义一些全局变量来代表摄像机的状态
1 2 3
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);
于是,LookAt函数现在变成了:
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);方向是摄像机位置向量加上摄像机注视着的方向(Look At函数以世界而不是摄像机为参考系)
处理按键输入
1 2 3 4 5 6 7 8 9 10 11 12 13 | void processInput(GLFWwindow *window) { ... float cameraSpeed = 0.05f; // adjust accordingly if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) cameraPos += cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) cameraPos -= cameraSpeed * cameraFront; if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) cameraPos -= glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) cameraPos += glm::normalize(glm::cross(cameraFront, cameraUp)) * cameraSpeed; } |
移动速度
deltaTime变量:当前帧与上一帧的时间差lastFrame变量:上一帧的时间- 将
cameraSpeed乘以deltaTime值可以平衡摄像机速度
1 2 3 4 5 6 7 8 9 10 11 12 | float deltaTime = 0.0f; // 当前帧与上一帧的时间差 float lastFrame = 0.0f; // 上一帧的时间 float currentFrame = glfwGetTime(); deltaTime = currentFrame - lastFrame; lastFrame = currentFrame; void processInput(GLFWwindow *window) { float cameraSpeed = 2.5f * deltaTime; ... } |
欧拉角
俯仰角
Pitch、偏航角Yaw、滚转角Roll1 2 3
direction.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); // 译注:direction代表摄像机的前轴(Front),这个前轴是和本文第一幅图片的第二个摄像机的方向向量是相反的 direction.y = sin(glm::radians(pitch)); direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
鼠标输入
设置GLFW使其隐藏光标并捕捉
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
创建回调函数
先注册回调函数
glfwSetCursorPosCallback(window, mouse_callback);全局变量储存鼠标上一帧的位置
float lastX = 400, lastY = 300;储存俯仰角
float yaw = 0,pitch = 01 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
void mouse_callback(GLFWwindow* window, double xpos, double ypos) { float xoffset = xpos - lastX; float yoffset = lastY - ypos; // 注意这里是相反的,因为y坐标是从底部往顶部依次增大的 lastX = xpos; lastY = ypos; float sensitivity = 0.05f; xoffset *= sensitivity; yoffset *= sensitivity; yaw += xoffset; pitch += yoffset; //确保玩家看不到自己的屁股 if(pitch > 89.0f) pitch = 89.0f; if(pitch < -89.0f) pitch = -89.0f; //计算得到方向向量 glm::vec3 front; front.x = cos(glm::radians(pitch)) * cos(glm::radians(yaw)); front.y = sin(glm::radians(pitch)); front.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw)); cameraFront = glm::normalize(front); }
防止鼠标跳跃
全局变量
bool firstMouse = true;1 2 3 4 5 6
if(firstMouse) // 这个bool变量初始时是设定为true的 { lastX = xpos; lastY = ypos; firstMouse = false; }
缩放
使用鼠标滚轮回调函数
先注册回调函数
glfwSetScrollCallback(window, scroll_callback);1 2 3 4 5 6 7 8 9
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { if(fov >= 1.0f && fov <= 45.0f) fov -= yoffset; if(fov <= 1.0f) fov = 1.0f; if(fov >= 45.0f) fov = 45.0f; }
渲染循环中
projection = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);