LearnOpenGL学习笔记(四) - 摄像机与入门知识点总结
本文最后更新于 2024年7月15日 晚上
逐渐理解一切…
摄像机
定义一个摄像机,需要获取其世界坐标、观察方向、指向其右侧与上方的方向向量。
-
世界坐标:与观察矩阵所使用的变换相同。
glm::vec3 cameraPos = glm::vec3(0.0f,0.0f,3.0f)
-
观察方向:借助矢量相减,获取摄像机位置与世界原点之间的方向向量。
glm::vec3 cameraDir = glm::normalize(cameraPos - vec3(0.0f,0.0f,0.0f))
- 这样得到的实际上是观察方向的反方向。
-
右轴:将上向量与观察方向叉乘。
1
2glm::vec3 up = glm::vec3(0.0f,1.0f,0.0f);
glm::vec3 cameraRight = glm::normalize(glm::cross(up,cameraDir)); -
上轴:将右轴与观察方向叉乘。
glm::vec3 cameraUp = glm::normalize(glm::cross(cameraRight,cameraDir))
上面的操作定义了一个额外的坐标空间。
这个操作被称为格拉姆-施密特正交化(Gram-Schmidt Process)
LookAt矩阵
坐标空间被定义后,便可以创建一个矩阵。把这个矩阵乘以任意向量,便可以把这个向量变换到我们定义的坐标空间。在摄像机空间中,这个矩阵被称为LookAt
矩阵。
其中,R为相机右轴,U为相机上轴,D为摄像机方向向量(相反,由原点指向相机),P为摄像机位置。
GLM提供了快速创建LookAt矩阵的方法。
1 |
|
一般,我们会把三个参数以全局变量的形式定义。
1 |
|
对于任何仅表示方向的向量,都应该做正交化处理。
Deltatime
Deltatime变量存储了渲染上一帧需要的时间。将该变量引入移动速度的计算,就可以做到每帧的移动速度在各种设备上相对平衡。
1 |
|
视角旋转
欧拉角分为三种:
- 俯仰角(Pitch):如何往上或往下看的角,绕x轴旋转。
- 偏航角(Yaw):往左和往右看的程度,绕y轴旋转。
- 滚转角(Roll):翻滚摄像机的程序,绕z轴旋转。
在Unity和其他引擎中,一般不关心滚转角。
1 |
|
理解上面的算法:
在x/z平面,可以计算水平距离和垂直距离。水平距离是cos(pitch),垂直距离(即最终的y分量)是sin(pitch)。
完成水平距离计算后,还要计算x分量和z分量。从上往下看x/z平面:
可以知道x分量是cos(yaw),z分量是sin(yaw)
对于鼠标输入,其水平移动影响Yaw角,竖直移动影响Pitch角。通过计算每一帧鼠标在垂直和水平方向与上一帧的插值,就可以得到具体的pitch和raw角。
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED)
让当前拥有焦点的窗口隐藏并捕捉鼠标。
捕捉(Capture)意味着,无论鼠标如何移动,都不会离开窗口范围。
定义鼠标移动回调函数:void mouse_callback(GLFWwindow* window, double xpos, double ypos)
,并将其注册到glfwSetCursorPosCallback(window, mouse_callback)
。
1 |
|
视角缩放
缩放作用于FOV,FOV是投影矩阵范畴的概念,所以缩放是改变的投影矩阵。
1 |
|
projection = glm::perspective(glm::radians(fov), 800.0f / 600.0f, 0.1f, 100.0f);
glfwSetScrollCallback(window, scroll_callback);
手动计算
1 |
|
知识点
- 相机空间的施密特正交化:相机位置、方向向量(目标指向相机)、右轴、上轴
- LookAt矩阵与观察矩阵:
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp)
- 此处
cameraFront
为相机指向目标的向量
- 此处
- Deltatime控制速度
1 |
|
- 鼠标输入
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED)
glfwSetCursorPosCallback(window, mouse_callback)
pitch
角的限制,注意弧度转换- 初次鼠标进入时的判断
- 滚轮、FOV与投影矩阵
复习
- OpenGL: 一个定义了函数布局和输出的图形API的正式规范。
除OpenGL外,还有DirectX 11,DirectX 12,Metal,Vulkan等图形API。在教程中,我们使用的OpenGL版本是3.3。
我们使用GLFW管理窗口相关的API。
- GLAD: 一个扩展加载库,用来为我们加载并设定所有OpenGL函数指针,从而让我们能够使用所有(现代)OpenGL函数。
在OpenGL中,所有函数都是运行时动态确定的,因此,IDE没办法给我们提供编译时语法检查和代码补全。为了解决这一问题,我们引入GLAD库,让上述功能成为可能。
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)
用来加载所有OpenGL函数指针。
- 视口(Viewport): 我们需要渲染的窗口。
视口与窗口是不同的概念。视口指的是渲染范围,是最终裁剪变换处理的区域。使用glViewport(x,y,width,height)
初始化并配置视口。
- 图形管线(Graphics Pipeline): 一个顶点在呈现为像素之前经过的全部过程。
基本的图形管线可以定义为:顶点数据->顶点着色器->几何着色器->形状装配->光栅化->片段着色器->测试与混合->输出
- 着色器(Shader): 一个运行在显卡上的小型程序。很多阶段的图形管道都可以使用自定义的着色器来代替原有的功能。
- 标准化设备坐标(Normalized Device Coordinates, NDC): 顶点在通过在剪裁坐标系中剪裁与透视除法后最终呈现在的坐标系。所有位置在NDC下-1.0到1.0的顶点将不会被丢弃并且可见。
经过顶点着色器处理的坐标必定处于NDC范围内。
- 顶点缓冲对象(Vertex Buffer Object): 一个调用显存并存储所有顶点数据供显卡使用的缓冲对象。
glDrawArray
函数直接使用VBO内的数据进行绘制。
- 顶点数组对象(Vertex Array Object): 存储缓冲区和顶点属性状态
在不使用VAO时,我们每次想绘制一个物体,都需要手动绑定对应的VBO,设置顶点属性指针。
VAO可以把这些操作保存,并生成一个VAO对象。之后每次想要绘制这个物体,绑定对应的VAO即可。
- 元素缓冲对象(Element Buffer Object,EBO),也叫索引缓冲对象(Index Buffer Object,IBO): 一个存储元素索引供索引化绘制使用的缓冲对象。
通常,输入的顶点数据不会包含重复的顶点。但对于一些图元来说,它们是共用顶点的。为了简化输入顶点数据,我们使用EBO记录绘制对象所使用的顶点序号。
- Uniform: 一个特殊类型的GLSL变量。它是全局的(在一个着色器程序中每一个着色器都能够访问uniform变量),并且只需要被设定一次。
使用glUniformnx(Location, value)
向某着色器的某个uniform传递值。
location使用glGetUniformLocation(shaderProgram, nameofuniform)
获取。
n
代表参数个数,x
代表数据类型,可以是fv(float数组)、4i(ivec4)等。
向Uniform传值之前,对应的着色器程序必须被use。
- 纹理(Texture): 一种包裹着物体的特殊类型图像,给物体精细的视觉效果。
glGenTexture(cnt, addr)
用于创建纹理对象。
glBindTexture(GL_TEXTURE_2D, textureObj)
用于绑定对象到纹理上下文。
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_X,GL_X)
用于设置纹理参数。参数有GL_TEXTURE_WARP和GL_TEXTURE_MIN_FILTER等。
glTexImage2D(GL_TEXTURE_2D,0,IMAGETYPE,width,height,0,IMAGETYPE,GL_UNSIGNED_BYTE,data)
用于将纹理数据载入上下文。
glGenerateMipmap(GL_TEXTURE_2D)
用于生成当前上下文存储纹理的多级渐远纹理。
glActiveTexture(GL_TEXTUREX)
用于激活X号纹理单元。类似于,让GL_TEXTURE_2D目标换到X号槽。
Active操作以后要Bind。
-
纹理环绕(Texture Wrapping): 定义了一种当纹理顶点超出范围(0, 1)时指定OpenGL如何采样纹理的模式。
-
纹理过滤(Texture Filtering): 定义了一种当有多种纹素选择时指定OpenGL如何采样纹理的模式。这通常在纹理被放大情况下发生。
-
多级渐远纹理(Mipmaps): 被存储的材质的一些缩小版本,根据距观察者的距离会使用材质的合适大小。
-
stb_image.h: 图像加载库。
stbi_load(path, &width, &height,&channels,0)
- 纹理单元(Texture Units): 通过绑定纹理到不同纹理单元从而允许多个纹理在同一对象上渲染。
- 向量(Vector): 一个定义了在空间中方向和/或位置的数学实体。
- 矩阵(Matrix): 一个矩形阵列的数学表达式。
- GLM: 一个为OpenGL打造的数学库。
- 局部空间(Local Space): 一个物体的初始空间。所有的坐标都是相对于物体的原点的。
局部空间类似于Unity的Transform属性的参数。
-
世界空间(World Space): 所有的坐标都相对于全局原点。
-
观察空间(View Space): 所有的坐标都是从摄像机的视角观察的。
-
裁剪空间(Clip Space): 所有的坐标都是从摄像机视角观察的,但是该空间应用了投影。这个空间应该是一个顶点坐标最终的空间,作为顶点着色器的输出。OpenGL负责处理剩下的事情(裁剪/透视除法)。
-
屏幕空间(Screen Space): 所有的坐标都由屏幕视角来观察。坐标的范围是从0到屏幕的宽/高。
-
LookAt矩阵: 一种特殊类型的观察矩阵,它创建了一个坐标系,其中所有坐标都根据从一个位置正在观察目标的用户旋转或者平移。
-
欧拉角(Euler Angles): 被定义为偏航角(Yaw),俯仰角(Pitch),和滚转角(Roll)从而允许我们通过这三个值构造任何3D方向。