投影(Projection)
把上次作业绘制的cube放置在(-1.5, 0.5, -1.5)位置,要求6个面颜色不一致
因为放置在(-1.5, 0.5, -1.5)时透视投影效果不明显,我将立方体放置在了(-3.5, 2.5, -1.5)位置。用glm::translate即可实现平移。
1 | model = glm::translate(model, glm::vec3(-3.5, 2.5f, -1.5f)); |
正交投影(orthographic projection):实现正交投影,使用多组(left, right, bottom, top, near, far)参数,比较结果差异
先设计一个GUI交互界面,可以选择投影类型,并输入不同的参数:
1 | float left = -12.0f, right = 12.0f, bottom = -9.0f, top = 9.0f, nearDist = 0.1f, farDist = 100.0f; |
效果:
多组参数的结果:
改变left、right、bottom和top,这决定了视口的左右范围和上下范围。数值范围越大,显示的范围也越大,物体也就越小。
如果长宽比与窗口相同,则显示的内容与真实的比例相同,否则会发生比例的变化。
near和far参数是显示的最近距离和最远距离。摄像机的z坐标是15,立方体中心的z坐标是-1.5,立方体的边长为4,立方体的正面离摄像机的距离是14.5。所以当把near设为15时,立方体的正面便不会显示出来,而显示立方体的背面。
透视投影(perspective projection):实现透视投影,使用多组参数,比较结果差异
多组参数的结果:
fov代表了视角大小,视角越小,看到的范围越小,物体就越大。
aspect ratio代表了宽高比,如果与窗口相同,则显示的内容与真实的比例相同,否则会发生比例的变化。
near和far决定了显示的前后范围。当把far设为17时,立方体的后半部分不显示。
当把near设为15时,立方体的前半部分不显示。
视角变换(View Changing)
把cube放置在(0, 0, 0)处,做透视投影,使摄像机围绕cube旋转,并且时刻看着cube中心
使摄像机位置始终在半径为15的圆周上,可以用三角函数实现:
1 | if (ImGui::RadioButton("View Changing", &function, 2)) { |
使用摄像机位置,创建LookAt矩阵,令摄像机始终看着(0, 0, 0)。投影矩阵使用透视投影。
1 | view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::vec3(0.0, 1.0, 0.0)); |
效果:
在现实生活中,我们一般将摄像机摆放的空间View matrix和被拍摄的物体摆设的空间Model matrix分开,但是在OpenGL中却将两个合二为一设为ModelView matrix,通过上面的作业启发,你认为是为什么呢?
因为OpenGL本身并没有摄像机这个概念,我们的摄像机,只是通过将场景中的物体往相反的方向移动模拟出来的。由于实际上只有一个坐标系统,坐标变换本质上都是在物体上进行,所以将View和Model矩阵合并成ModelView矩阵。而在多个摄像机的情况下,只需定义不同的ModelView矩阵,就可以实现摄像机的切换,不需要在多个坐标系统之间进行切换,实现起来比较方便。
Bonus:实现一个camera类,能够使用键盘和鼠标控制视角,实现类似FPS的游戏场景
类的头文件定义如下,具体实现请看源代码。1
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43// default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float FOV = 45.0f;
class Camera {
public:
// Constructor with vectors
Camera(glm::vec3 _position = glm::vec3(0.0f, 0.0f, 15.0f),
glm::vec3 _front = glm::vec3(0.0f, 0.0f, -1.0f),
glm::vec3 _up = glm::vec3(0.0f, 1.0f, 0.0f),
float _yaw = YAW, float _pitch = PITCH);
// Returns the view matrix
glm::mat4 getViewMatrix();
// Move the camera
void moveForward(float distance);
void moveBack(float distance);
void moveRight(float distance);
void moveLeft(float distance);
// Rotate the camera using pitch and yaw
void rotate(float _pitch, float _yaw);
// Change the fov to zoom
void zoom(float yoffset);
// Get the fov
float getFov();
// Reset the camera
void reset();
private:
// Calculates the front vector from the Camera's (updated) Euler Angles
void updateCameraVectors();
// Camera Attributes
glm::vec3 position;
glm::vec3 front;
glm::vec3 up;
glm::vec3 right;
glm::vec3 world_up;
// Euler Angles
float yaw;
float pitch;
// Camera options
float fov;
};
检测键盘输入,调用Camera类的四个移动方法,实现摄像机的移动:
1 | float cameraSpeed = 20.0f * deltaTime; |
在鼠标回调函数中,调用Camera类的rotate方法,实现视角的转动:
1 | void mouse_callback(GLFWwindow* window, double xpos, double ypos) { |
在滚动的回调函数中,调用Camera类的zoom方法,实现放大和缩小:
1 | void scroll_callback(GLFWwindow* window, double xoffset, double yoffset) { |
渲染的时候,通过Camera类获取View Matrix和FOV:
1 | view = camera.getViewMatrix(); |
实现效果: