接口

``````do{

// ...

// Compute the MVP matrix from keyboard and mouse input
computeMatricesFromInputs();
glm::mat4 ProjectionMatrix = getProjectionMatrix();
glm::mat4 ViewMatrix = getViewMatrix();
glm::mat4 ModelMatrix = glm::mat4(1.0);
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;

// ...
}
``````

• computeMatricesFromInputs()读键盘和鼠标操作，然后计算投影观察矩阵。这一步正是精华所在。
• getProjectionMatrix()返回计算好的投影矩阵。
• getViewMatrix()返回计算好的观察矩阵。

实际代码

``````// position
glm::vec3 position = glm::vec3( 0, 0, 5 );
// horizontal angle : toward -Z
float horizontalAngle = 3.14f;
// vertical angle : 0, look at the horizon
float verticalAngle = 0.0f;
// Initial Field of View
float initialFoV = 45.0f;

float speed = 3.0f; // 3 units / second
float mouseSpeed = 0.005f;
``````

FoV is the level of zoom. 80° = very wide angle, huge deformations. 60° - 45° : standard. 20° : big zoom.

朝向

``````// Get mouse position
int xpos, ypos;
glfwGetMousePos(&xpos, &ypos);
``````

``````// Reset mouse position for next frame
glfwSetMousePos(1024/2, 768/2);
``````

``````// Compute new orientation
horizontalAngle += mouseSpeed * deltaTime * float(1024/2 - xpos );
verticalAngle   += mouseSpeed * deltaTime * float( 768/2 - ypos );
``````

• 1024/2 - xpos表示鼠标离窗口中心点的距离。这个值越大，转动角越大。
• float(…)是浮点数转换，使乘法顺利进行
• mouseSpeed用来加速或减慢旋转，可以任意调整或让用户选择。
• += : 如果没移动鼠标，1024/2-xpos的值为零，horizontalAngle+=0不改变horizontalAngle的值。如果用的是”=”，每帧视角都被强制转回到原始方向，这可不是我们想要的效果。

``````// Direction : Spherical coordinates to Cartesian coordinates conversion
glm::vec3 direction(
cos(verticalAngle) * sin(horizontalAngle),
sin(verticalAngle),
cos(verticalAngle) * cos(horizontalAngle)
);
``````

``````// Right vector
glm::vec3 right = glm::vec3(
sin(horizontalAngle - 3.14f/2.0f),
0,
cos(horizontalAngle - 3.14f/2.0f)
);
``````

``````// Up vector : perpendicular to both direction and right
glm::vec3 up = glm::cross( right, direction );
``````

位置

``````// Move forward
if (glfwGetKey( GLFW_KEY_UP ) == GLFW_PRESS){
position += direction * deltaTime * speed;
}
// Move backward
if (glfwGetKey( GLFW_KEY_DOWN ) == GLFW_PRESS){
position -= direction * deltaTime * speed;
}
// Strafe right
if (glfwGetKey( GLFW_KEY_RIGHT ) == GLFW_PRESS){
position += right * deltaTime * speed;
}
// Strafe left
if (glfwGetKey( GLFW_KEY_LEFT ) == GLFW_PRESS){
position -= right * deltaTime * speed;
}
``````

• 如果您的电脑运行速度快，fps = 60，您每秒将移动60*speed个单位。
• 如果您的电脑运行速度慢，fps = 20，您每秒将移动20*speed个单位。

• 如果您的电脑运行速度快，fps = 60，您每帧将移动1/60speed个单位，每秒移动1speed个单位。
• 如果您的电脑运行速度慢，fps = 20，您每帧将移动1/20speed个单位，每秒移动1speed个单位。

``````double currentTime = glfwGetTime();
float deltaTime = float(currentTime - lastTime);
``````

视野

``````float FoV = initialFoV - 5 * glfwGetMouseWheel();
``````

计算矩阵

``````// Projection matrix : 45&deg; Field of View, 4:3 ratio, display range : 0.1 unit  100 units
ProjectionMatrix = glm::perspective(glm::radians(FoV), 4.0f / 3.0f, 0.1f, 100.0f);
// Camera matrix
ViewMatrix       = glm::lookAt(
position,           // Camera is here
position+direction, // and looks here : at the same position, plus "direction"
up                  // Head is up (set to 0,-1,0 to look upside-down)
);
``````

结果

背面剔除

``````// Cull triangles which normal is not towards the camera
glEnable(GL_CULL_FACE);
``````

练习

• 限制verticalAngle，使之不能颠倒方向
• 创建一个摄像机，使它绕着物体旋转 ( position = ObjectCenter + ( radius * cos(time), height, radius * sin(time) ) )；然后将半径/高度/时间的变化绑定到键盘/鼠标上，诸如此类。
• 玩得开心哦！