1
0
mirror of https://github.com/LearnOpenGL-CN/LearnOpenGL-CN.git synced 2025-08-23 12:45:29 +08:00
This commit is contained in:
Meow J
2015-08-09 00:25:47 +08:00
parent 4c6abd0a1a
commit 8df84f97d9
2 changed files with 51 additions and 50 deletions

View File

@@ -1,11 +1,11 @@
# 坐标系统(Coordinate Systems)
// TODO 翻译太生硬,可能还要重写。并且可能还有漏掉的地方。
// 简单说下问题,供再次校对的时候参考:
// 1.术语翻译不统一
// 2.从句还保留着英语语序,翻译出来很奇怪
// 3.To do很多未翻译出来
// 4.某些地方很不通顺,特别是遇上连词的时候
// TODO 翻译太生硬,可能还要重写。并且可能还有漏掉的地方。
// 简单说下问题,供再次校对的时候参考:
// 1.术语翻译不统一
// 2.从句还保留着英语语序,翻译出来很奇怪
// 3.To do很多未翻译出来
// 4.某些地方很不通顺,特别是遇上连词的时候
原文 | [Coordinate Systems](http://learnopengl.com/#!Getting-started/Coordinate-Systems)
---|---

View File

@@ -6,19 +6,19 @@
翻译 | [Django](http://bullteacher.com/)
校对 | Geequlim
前面的教程中我们讨论了视图矩阵以及如何使用视图矩阵移动场景。OpenGL本身没有摄像机的概念但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机这样感觉就像我们在移动而不是场景在移动。
前面的教程中我们讨论了观察矩阵以及如何使用观察矩阵移动场景。OpenGL本身没有摄像机的概念但我们可以通过把场景中的所有物体往相反方向移动的方式来模拟出摄像机这样感觉就像我们在移动而不是场景在移动。
本节我们会讨论如何在OpenGL中模拟一个摄像机。我们讨论一下FPS风格的可自由在3D场景中移动的摄像机。这里也会讨论键盘和鼠标输入,最终完成一个自定义的摄像机类。
本节我们会讨论如何在OpenGL中模拟一个摄像机,将会讨论FPS风格的可自由在3D场景中移动的摄像机。我们也会讨论键盘和鼠标输入,最终完成一个自定义的摄像机类。
## 摄像机/视图空间(Canera/View Space)
### 摄像机/观察空间(Camera/View Space)
当我们论摄像机/视图空间的时候,我们在论以摄像机的透视图作为场景原点时场景中所有可见顶点坐标。视图矩阵把所有的世界坐标变换到观察坐标,这些新坐标是相对于摄像机的位置和方向的。定义一个摄像机,我们需要一个摄像机在世界空间中的位置、观察的方向、一个指向它的右测的向量以及一个指向它上方的向量。细心的读者可能已经注意到我们实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。
当我们论摄像机/观察空间的时候,我们在论以摄像机的透视图作为场景原点时场景中所有可见顶点坐标。观察矩阵把所有的世界坐标变换到观察坐标,这些新坐标是相对于摄像机的位置和方向的。定义一个摄像机,我们需要一个摄像机在世界空间中的位置、观察的方向、一个指向它的右测的向量以及一个指向它上方的向量。细心的读者可能已经注意到我们实际上创建了一个三个单位轴相互垂直的、以摄像机的位置为原点的坐标系。
![](http://learnopengl.com/img/getting-started/camera_axes.png)
### 1.摄像机位置
#### 1.摄像机位置
摄像机位置很简单。摄像机位置简单来说就是世界空间中代表摄像机位置的向量。我们把摄像机位置设置为前面教程中的那个相同的位置:
摄像机位置很简单。摄像机位置简单来说就是世界空间中代表摄像机位置的向量。我们把摄像机位置设置为前面教程中的那个相同的位置:
```c++
glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
@@ -28,9 +28,9 @@ glm::vec3 cameraPos = glm::vec3(0.0f, 0.0f, 3.0f);
不要忘记正z轴是从屏幕指向你的如果我们希望摄像机向后移动我们就往z轴正方向移动。
### 2.摄像机方向
#### 2.摄像机方向
下一个需要的向量是摄像机的方向,比如它指向哪个方向。现在我们让摄像机指向场景原点:0, 0, 0。用摄像机位置向量减去场景原点向量的结果就是摄像机指向向量。由于我们知道摄像机指向z轴负方向我们希望方向向量指向摄像机的z轴正方向。如果我们改变相减的顺序我们就会获得一个指向摄像机正z轴方向的向量(译注:注意看前面的那个图,所说的「方向向量/direction vector」是指向z的正方向的而不是摄像机所注视的那个方向)
下一个需要的向量是摄像机的方向,比如它指向哪个方向。现在我们让摄像机指向场景原点:(0, 0, 0)。用摄像机位置向量减去场景原点向量的结果就是摄像机指向向量。由于我们知道摄像机指向z轴负方向我们希望方向向量指向摄像机的z轴正方向。如果我们改变相减的顺序我们就会获得一个指向摄像机正z轴方向的向量(译注:注意看前面的那个图,所说的「方向向量/Direction Vector」是指向z的正方向的而不是摄像机所注视的那个方向)
```c++
glm::vec3 cameraTarget = glm::vec3(0.0f, 0.0f, 0.0f);
@@ -39,36 +39,36 @@ glm::vec3 cameraDirection = glm::normalize(cameraPos - cameraTarget);
!!! Attention
方向向量Direction Vector并不是最好的名字,因为它正好指向从它到目标向量的相反方向。
方向向量(Direction Vector)并不是最好的名字,因为它正好指向从它到目标向量的相反方向。
### 3.右轴(Right axis)
#### 3.右轴(Right axis)
我们需要的另一个向量是一个右向量它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧定义一个上向量。我们把上向量和第二步得到的摄像机方向向量进行叉乘。两个向量叉乘的结果就是同时垂直于两向量的向量因此我们会得到指向x轴正方向的那个向量如果我们交换两个向量的顺序就会得到相反的指向x轴负方向的向量
我们需要的另一个向量是一个**右向量(Right Vector)**它代表摄像机空间的x轴的正方向。为获取右向量我们需要先使用一个小技巧定义一个**上向量(Up Vector)**。我们把上向量和第二步得到的摄像机方向向量进行叉乘。两个向量叉乘的结果就是同时垂直于两向量的向量因此我们会得到指向x轴正方向的那个向量(如果我们交换两个向量的顺序就会得到相反的指向x轴负方向的向量)
```c++
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up, cameraDirection));
```
### 4.上轴(Up axis)
#### 4.上轴(Up axis)
现在我们已经有了x轴向量和z轴向量获取摄像机的正y轴相对简单我们把右向量和方向向量direction vector进行叉乘:
现在我们已经有了x轴向量和z轴向量获取摄像机的正y轴相对简单我们把右向量和方向向量(Direction Vector)进行叉乘:
```c++
glm::vec3 cameraUp = glm::cross(cameraDirection, cameraRight);
```
在叉乘和一些小技巧的帮助下,我们创建了所有视图/摄像机空间的向量。对于想学到更多数学原理的读者,提示一下,在线性代数中这个处理叫做[Gram-Schmidt葛兰—施密特正交](http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process)。使用这些摄像机向量我们就可以创建一个**LookAt**矩阵了,使用它创建摄像机非常好
在叉乘和一些小技巧的帮助下,我们创建了所有观察/摄像机空间的向量。对于想学到更多数学原理的读者,提示一下,在线性代数中这个处理叫做[Gram-Schmidt(葛兰—施密特)正交](http://en.wikipedia.org/wiki/Gram%E2%80%93Schmidt_process)。使用这些摄像机向量我们就可以创建一个**LookAt**矩阵了,它创建摄像机的时候非常有用
## Look At
### Look At
使用矩阵的好处之一是如果你定义了一个坐标空间里面有3个相互垂直的轴你可以用这三个轴外加一个平移向量来创建一个矩阵你可以用这个矩阵乘以任何向量来变换到那个坐标空间。这正是LookAt矩阵所做的现在我们有了3个相互垂直的轴和一个定义摄像机空间的位置坐标我们可以创建我们自己的LookAt矩阵了
![](../img/look_at.png)
![](../img/look_at_R.png)是右向量,![](../img/look_at_U.png)是上向量,![](../img/look_at_D.png)是方向向量![](../img/look_at_P.png)是摄像机位置向量。注意位置向量是相反的因为我们最终希望把世界平移到与我们自身移动的相反方向。使用这个LookAt矩阵坐标视图矩阵可以很高效地把所有世界坐标变换为观察坐标LookAt矩阵就像它的名字表达的那样它会创建一个视图矩阵looks at看着一个给定目标。
![](../img/look_at_R.png)是右向量,![](../img/look_at_U.png)是上向量,![](../img/look_at_D.png)是方向向量![](../img/look_at_P.png)是摄像机位置向量。注意位置向量是相反的因为我们最终希望把世界平移到与我们自身移动的相反方向。使用这个LookAt矩阵坐标观察矩阵可以很高效地把所有世界坐标变换为观察坐标LookAt矩阵就像它的名字表达的那样它会创建一个观察矩阵looks at(看着)一个给定目标。
幸运的是GLM已经提供了这些支持。我们要做的只是定义一个摄像机位置一个目标位置和一个表示上向量的世界空间中的向量我们使用上向量计算右向量。接着GLM就会创建一个LookAt矩阵我们可以把它当作我们的视图矩阵:
幸运的是GLM已经提供了这些支持。我们要做的只是定义一个摄像机位置一个目标位置和一个表示上向量的世界空间中的向量(我们使用上向量计算右向量)。接着GLM就会创建一个LookAt矩阵我们可以把它当作我们的观察矩阵:
```c++
glm::mat4 view;
@@ -77,11 +77,11 @@ view = glm::lookAt(glm::vec3(0.0f, 0.0f, 3.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
```
`glm::LookAt`函数需要一个位置、目标和上向量。它可以创建一个和前面所说的同样的视图矩阵。
`glm::LookAt`函数需要一个位置、目标和上向量。它可以创建一个和前面所说的同样的观察矩阵。
在开始做用户输入之前,我们来做些有意思的事,把我们的摄像机在场景中旋转。我们的注视点保持在0, 0, 0
在开始做用户输入之前,我们来做些有意思的事,把我们的摄像机在场景中旋转。我们的注视点保持在(0, 0, 0)
我们在每一帧都创建x和z坐标这要使用一点三角学知识。x和z表示一个在一个圆圈上的一点我们会使用它作为摄像机的位置。通过重复计算x和y坐标遍历所有圆圈上的点这样摄像机就会绕着场景旋转了。我们预先定义这个圆圈的半径使用`glfwGetTime`函数不断增加它的值,在每次循环迭代创建一个新的视图矩阵。
我们在每一帧都创建x和z坐标这要使用一点三角学知识。x和z表示一个在一个圆圈上的一点我们会使用它作为摄像机的位置。通过重复计算x和y坐标遍历所有圆圈上的点这样摄像机就会绕着场景旋转了。我们预先定义这个圆圈的半径使用`glfwGetTime`函数不断增加它的值,在每次渲染迭代创建一个新的观察矩阵。
```c++
GLfloat radius = 10.0f;
@@ -96,7 +96,7 @@ view = glm::lookAt(glm::vec3(camX, 0.0, camZ), glm::vec3(0.0, 0.0, 0.0), glm::ve
<video src="http://learnopengl.com/video/getting-started/camera_circle.mp4" controls="controls">
</video>
这一小段代码中,摄像机围绕场景转动。自己试试改变半径和位置/方向参数看看LookAt矩阵是如何工作的。同时这里有源码、顶点和像素着色器。
这一小段代码中,摄像机围绕场景转动。自己试试改变半径和位置/方向参数看看LookAt矩阵是如何工作的。同时这里有[源码](http://learnopengl.com/code_viewer.php?code=getting-started/camera_circle)、[顶点](http://learnopengl.com/code_viewer.php?code=getting-started/coordinate_systems&type=vertex)和[片段](http://learnopengl.com/code_viewer.php?code=getting-started/coordinate_systems&type=fragment)着色器。
## 自由移动
@@ -114,7 +114,7 @@ LookAt函数现在成了
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
```
我们首先设置之前定义的`cameraPos`为摄像机位置。方向direction是当前的位置加上我们刚刚定义的方向向量。这样能保证无论我们怎么移动,摄像机都会注视目标。我们在按下某个按钮时更新`cameraPos`向量。
我们首先设置之前定义的`cameraPos`为摄像机位置。方向(Direction)是当前的位置加上我们刚刚定义的方向向量。这样能保证无论我们怎么移动,摄像机都会注视目标。我们在按下某个按钮时更新`cameraPos`向量。
我们已经为GLFW的键盘输入定义了一个`key_callback`函数,我们来添加几个新按键命令:
@@ -138,7 +138,7 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod
!!! Important
注意,我们对右向量进行了标准化。如果我们没对这个向量进行标准,最后的叉乘结果会根据`cameraFront`变量的大小返回不同的大小。如果我们不对向量进行标准化,我们就得根据摄像机的方位加速或减速移动了,但假如进行了标准化移动就是匀速的。
注意,我们对右向量进行了标准化。如果我们没对这个向量进行标准,最后的叉乘结果会根据`cameraFront`变量的大小返回不同的大小。如果我们不对向量进行标准化,我们就得根据摄像机的方位加速或减速移动了,但假如进行了标准化移动就是匀速的。
如果你用这段代码更新`key_callback`函数,你就可以在场景中自由的前后左右移动了。
@@ -152,6 +152,7 @@ void key_callback(GLFWwindow* window, int key, int scancode, int action, int mod
```c++
bool keys[1024];
```
然后我们必须在`key_callback`函数中设置按下/释放键为`true`或`false`
```c++
@@ -195,7 +196,7 @@ while(!glfwWindowShouldClose(window))
}
```
至此,你可以同时向多个方向移动了,并且当你按下按钮也会立刻运动了。如遇困难查看源码。
至此,你可以同时向多个方向移动了,并且当你按下按钮也会立刻运动了。如遇困难查看[源码](http://learnopengl.com/code_viewer.php?code=getting-started/camera_keyboard)
### 移动速度
@@ -241,15 +242,15 @@ void Do_Movement()
只用键盘移动没什么意思。特别是我们还不能转向。是时候使用鼠标了!
为了能够改变方向,我们必须根据鼠标的输入改变`cameraFront`向量。然而,根据鼠标旋转改变方向向量有点复杂,需要更多的三角学知识。如果你对三角学知之甚少,别担心你可以复制粘贴的;当你想了解更多的时候再回来看。
为了能够改变方向,我们必须根据鼠标的输入改变`cameraFront`向量。然而,根据鼠标旋转改变方向向量有点复杂,需要更多的三角学知识。如果你对三角学知之甚少,别担心你可以跳过这一部分,直接复制粘贴我们的代码;当你想了解更多的时候再回来看。
### 欧拉角
欧拉角是表示3D空间中可以表示任何旋转的三个值由莱昂哈德·欧拉在18世纪提出。有三种欧拉角俯仰角pitch、偏航角yaw和滚转角roll,下面的图片展示了它们的含义:
欧拉角是表示3D空间中可以表示任何旋转的三个值由莱昂哈德·欧拉在18世纪提出。有三种欧拉角俯仰角(Pitch)、偏航角(Yaw)和滚转角(Roll),下面的图片展示了它们的含义:
![](http://www.learnopengl.com/img/getting-started/camera_pitch_yaw_roll.png)
俯仰角是描述我们如何往上和往下看的角它在第一张图中表示。第二张图显示了偏航角偏航角表示我们往左和往右看的大小。滚转角代表我们如何翻滚摄像机。每个欧拉角都有一个值来表示把三个角结合起来我们就能够计算3D空间中任何的旋转了。
**俯仰角**是描述我们如何往上和往下看的角,它在第一张图中表示。第二张图显示了**偏航角**,偏航角表示我们往左和往右看的大小。**滚转角**代表我们如何翻滚摄像机。每个欧拉角都有一个值来表示把三个角结合起来我们就能够计算3D空间中任何的旋转了。
对于我们的摄像机系统来说我们只关心俯仰角和偏航角所以我们不会讨论滚转角。用一个给定的俯仰角和偏航角我们可以把它们转换为一个代表新的方向向量的3D向量。俯仰角和偏航角转换为方向向量的处理需要一些三角学知识我们以最基本的情况开始
@@ -259,7 +260,7 @@ void Do_Movement()
![](http://www.learnopengl.com/img/getting-started/camera_pitch.png)
这个三角形看起来和前面的三角形很像所以如果我们想象自己在xz平面上正望向y轴我们可以基于第一个三角形计算长度/y方向的强度我们往上或往下看多少。从图中我们可以看到一个给定俯仰角的y值等于sinθ:
这个三角形看起来和前面的三角形很像所以如果我们想象自己在xz平面上正望向y轴我们可以基于第一个三角形计算长度/y方向的强度(我们往上或往下看多少)。从图中我们可以看到一个给定俯仰角的y值等于sinθ:
```c++
direction.y = sin(glm::radians(pitch)); // 注意我们先把角度转为弧度
@@ -294,7 +295,7 @@ direction.z = cos(glm::radians(pitch)) * sin(glm::radians(yaw));
偏航角和俯仰角是从鼠标移动获得的,鼠标水平移动影响偏航角,鼠标垂直移动影响俯仰角。它的思想是储存上一帧鼠标的位置,在当前帧中我们当前计算鼠标位置和上一帧的位置相差多少。如果差别越大那么俯仰角或偏航角就改变越大。
首先我们要告诉GLFW应该隐藏光标捕捉它。捕捉鼠标意味着当应用集中焦点到鼠标上的时候光标就应该留在窗口中除非应用拾取焦点或退出。我们可以进行简单的配置:
首先我们要告诉GLFW应该隐藏光标**捕捉(Capture)**它。捕捉鼠标意味着当应用集中焦点到鼠标上的时候光标就应该留在窗口中(除非应用拾取焦点或退出)。我们可以进行简单的配置:
```c++
@@ -303,7 +304,7 @@ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
这个函数调用后无论我们怎么去移动鼠标它都不会显示了也不会离开窗口。对于FPS摄像机系统来说很好
为计算俯仰角和偏航角我们需要告诉GLFW监听鼠标移动事件。我们用下面的原型创建一个回调函数来做这件事和键盘输入差不多
为计算俯仰角和偏航角我们需要告诉GLFW监听鼠标移动事件。我们用下面的原型创建一个回调函数来做这件事(和键盘输入差不多)
```c++
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
@@ -317,12 +318,12 @@ glfwSetCursorPosCallback(window, mouse_callback);
在处理FPS风格的摄像机鼠标输入的时候我们必须在获取最终的方向向量之前做下面这几步
- 计算鼠标和上一帧的偏移量。
- 把偏移量添加到摄像机和俯仰角和偏航角中。
- 对偏航角和俯仰角进行最大和最小值的限制。
- 计算方向向量。
1. 计算鼠标和上一帧的偏移量。
2. 把偏移量添加到摄像机和俯仰角和偏航角中。
3. 对偏航角和俯仰角进行最大和最小值的限制。
4. 计算方向向量。
第一步计算鼠标自上一帧的偏移量。我们必须先储存上一帧的鼠标位置,我们把它的初始值设置为屏幕的中心屏幕的尺寸是800乘600
第一步计算鼠标自上一帧的偏移量。我们必须先储存上一帧的鼠标位置,我们把它的初始值设置为屏幕的中心(屏幕的尺寸是800乘600)
```c++
GLfloat lastX = 400, lastY = 300;
@@ -343,14 +344,14 @@ yoffset *= sensitivity;
注意我们把偏移量乘以了`sensitivity`值。如果我们移除它,鼠标移动就会太大了;你可以自己调整`sensitivity`的值。
下面我们把偏移量加到全局变量pitchyaw上
下面我们把偏移量加到全局变量`pitch`和`yaw`上:
```c++
yaw += xoffset;
pitch += yoffset;
```
第三步我们给摄像机添加一些限制这样摄像机就不会发生奇怪的移动了。对于俯仰角要让用户不能看向高于89度90度时视会逆转所以我们把89度作为极限的地方,同样也不允许小于-89度。这样能够保证用户只能看到天空或脚下但是不能更进一步超越过去。限制可以这样做
第三步我们给摄像机添加一些限制这样摄像机就不会发生奇怪的移动了。对于俯仰角要让用户不能看向高于89度(90度时视会逆转所以我们把89度作为极限)的地方,同样也不允许小于-89度。这样能够保证用户只能看到天空或脚下但是不能更进一步超越过去。限制可以这样做
```c++
if(pitch > 89.0f)
@@ -373,7 +374,7 @@ cameraFront = glm::normalize(front);
这回计算出方向向量,根据鼠标点的移动它包含所有的旋转。由于`cameraFront`向量已经包含在`glm::lookAt`函数中,我们直接去设置。
如果你现在运行代码,你会发现当程序运行第一次捕捉到鼠标的时候摄像机会突然一下。原因是当你的鼠标进入窗口鼠标回调函数会使用这时的`xpos`和`ypos`。这通常是一个距离屏幕中心很远的地方,因而产生一个很大的偏移量,所以就会跳了。我们可以简单的使用一个布尔变量检验我们是否是第一次获取鼠标输入,如果是,那么我们先把鼠标的位置更新为`xpos`和`ypos`,这样就能解决这个问题;最后的鼠标移动会使用进入以后鼠标的位置坐标来计算它的偏移量:
如果你现在运行代码,你会发现当程序运行第一次捕捉到鼠标的时候摄像机会突然一下。原因是当你的鼠标进入窗口鼠标回调函数会使用这时的`xpos`和`ypos`。这通常是一个距离屏幕中心很远的地方,因而产生一个很大的偏移量,所以就会跳了。我们可以简单的使用一个布尔变量检验我们是否是第一次获取鼠标输入,如果是,那么我们先把鼠标的位置更新为`xpos`和`ypos`,这样就能解决这个问题;最后的鼠标移动会使用进入以后鼠标的位置坐标来计算它的偏移量:
```c++
if(firstMouse) // this bool variable is initially set to true
@@ -425,7 +426,7 @@ void mouse_callback(GLFWwindow* window, double xpos, double ypos)
### 缩放
我们还要往摄像机系统里加点东西,实现一个缩放接口。前面教程中我们说`fov`定义了我们可以看到场景中多大的范围。当`fov`变小时可视区域就会减小,产生放大了的感觉。我们用鼠标滚轮来放大。和鼠标移动、键盘输入一样我们需要一个鼠标滚轮的回调函数:
我们还要往摄像机系统里加点东西,实现一个缩放接口。前面教程中我们说视野(Field of View或fov)定义了我们可以看到场景中多大的范围。当视野变小时可视区域就会减小,产生放大了的感觉。我们用鼠标滚轮来放大。和鼠标移动、键盘输入一样我们需要一个鼠标滚轮的回调函数:
```c++
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
@@ -466,19 +467,19 @@ glfwSetScrollCallback(window, scroll_callback);
## 摄像机类
接下来的教程我们会使用一个摄像机来浏览场景,从各个角度观察结果。然而由于一个摄像机会占教程的很大的篇幅,我们会从细节抽象出创建一个自己的摄像机对象。与着色器教程不同我们不会带你一步一步创建摄像机类,如果你想知道怎么工作的的话,只会给你提供一个有完整注释的源码。
接下来的教程我们会使用一个摄像机来浏览场景,从各个角度观察结果。然而由于一个摄像机会占教程的很大的篇幅,我们会从细节抽象出创建一个自己的摄像机对象。与着色器教程不同我们不会带你一步一步创建摄像机类,如果你想知道怎么工作的的话,只会给你提供一个(有完整注释的)源码。
如同着色器对象我们把它写在一个单独的头文件中。你可以[这里找到摄像机类](http://learnopengl.com/code_viewer.php?type=header&code=camera)。你应该能够理解所有的代码。建议至少检查一下这个类,看看如何创建一个自己的摄像机类。
着色器对象一样,我们把摄像机类写在一个单独的头文件中。你可以[这里](http://learnopengl.com/code_viewer.php?type=header&code=camera)找到它。你应该能够理解所有的代码。我们建议至少看一看这个类,看看如何创建一个自己的摄像机类。
!!! Attention
我们介绍的欧拉角FPS风格摄像机系统能够满足大多数情况需要但是在创建不同的摄像机系统比如飞行模拟就要当心。每个摄像机系统都有自己的有点和不足所以确保对它们进行了详细研究。比如这个FPS射线机不允许俯仰角大于90多由于使用了固定的上向量0, 1, 0,我们就不能用滚转角。
我们介绍的欧拉角FPS风格摄像机系统能够满足大多数情况需要但是在创建不同的摄像机系统比如飞行模拟就要当心。每个摄像机系统都有自己的有点和不足所以确保对它们进行了详细研究。比如这个FPS射线机不允许俯仰角大于90多由于使用了固定的上向量(0, 1, 0),我们就不能用滚转角。
使用新的摄像机对象的更新后的版本源码可以[在这里找到](http://learnopengl.com/code_viewer.php?code=getting-started/camera_with_class)。(总而言之这个摄像机实现并不十分完美,你可以看看最终的源码。建议先看[这篇文章](https://github.com/cybercser/OpenGL_3_3_Tutorial_Translation/blob/master/Tutorial%2017%20Rotations.md)对旋转有更深的理解后你就能做出更好的摄像机类不过本文有些内容比如如何防止按键停顿和glfw鼠标事件实现摄像机的注意事项比较重要其它的就要做一定的取舍了)
使用新的摄像机对象的更新后的版本源码可以[在这里找到](http://learnopengl.com/code_viewer.php?code=getting-started/camera_with_class)。(译注:总而言之这个摄像机实现并不十分完美,你可以看看最终的源码。建议先看[这篇文章](https://github.com/cybercser/OpenGL_3_3_Tutorial_Translation/blob/master/Tutorial%2017%20Rotations.md)对旋转有更深的理解后你就能做出更好的摄像机类不过本文有些内容比如如何防止按键停顿和glfw鼠标事件实现摄像机的注意事项比较重要其它的就要做一定的取舍了)
## 练习
- 看看你是否能够变换摄像机类从而使得其能够变- 成一个真正的FPS摄像机(也就是说不能够随意飞行)你只能够呆在xz平面上: [参考解答](http://www.learnopengl.com/code_viewer.php?code=getting-started/camera-exercise1)
- 看看你是否能够变换摄像机类从而使得其能够变- 成一个**真正的**FPS摄像机(也就是说不能够随意飞行)你只能够呆在xz平面上: [参考解答](http://www.learnopengl.com/code_viewer.php?code=getting-started/camera-exercise1)
- 试着创建你自己的LookAt函数使你能够手动创建一个我们在一开始讨论的观察矩阵。用你的函数实现来替换glm的LookAt函数看看它是否还能一样的工作[参考解答](http://www.learnopengl.com/code_viewer.php?code=getting-started/camera-exercise2)