Learn OpenGL重点速查
时间:2023年4月2日17:06:27
环境配置
1.创建控制空项目
2.配置属性页
3.添加依赖文件
重要词汇一览表
第一章 入门
- OpenGL: 一个定义了函数布局和输出的图形API的正式规范。
- GLAD: 一个拓展加载库,用来为我们加载并设定所有OpenGL函数指针,从而让我们能够使用所有(现代)OpenGL函数。
- **视口(Viewport)**: 我们需要渲染的窗口。
- **图形管线(Graphics Pipeline)**: 一个顶点在呈现为像素之前经过的全部过程。
- **着色器(Shader)**: 一个运行在显卡上的小型程序。很多阶段的图形管道都可以使用自定义的着色器来代替原有的功能。
- **标准化设备坐标(Normalized Device Coordinates, NDC)**: 顶点在通过在剪裁坐标系中剪裁与透视除法后最终呈现在的坐标系。所有位置在NDC下-1.0到1.0的顶点将不会被丢弃并且可见。
- **顶点缓冲对象(Vertex Buffer Object)**: 一个调用显存并存储所有顶点数据供显卡使用的缓冲对象。
- **顶点数组对象(Vertex Array Object)**: 存储缓冲区和顶点属性状态。
- **元素缓冲对象(Element Buffer Object,EBO),也叫索引缓冲对象(Index Buffer Object,IBO)**: 一个存储元素索引供索引化绘制使用的缓冲对象。
- Uniform: 一个特殊类型的GLSL变量。它是全局的(在一个着色器程序中每一个着色器都能够访问uniform变量),并且只需要被设定一次。
- **纹理(Texture)**: 一种包裹着物体的特殊类型图像,给物体精细的视觉效果。
- **纹理缠绕(Texture Wrapping)**: 定义了一种当纹理顶点超出范围(0, 1)时指定OpenGL如何采样纹理的模式。
- **纹理过滤(Texture Filtering)**: 定义了一种当有多种纹素选择时指定OpenGL如何采样纹理的模式。这通常在纹理被放大情况下发生。
- **多级渐远纹理(Mipmaps)**: 被存储的材质的一些缩小版本,根据距观察者的距离会使用材质的合适大小。
- stb_image.h: 图像加载库。
- **纹理单元(Texture Units)**: 通过绑定纹理到不同纹理单元从而允许多个纹理在同一对象上渲染。
- **向量(Vector)**: 一个定义了在空间中方向和/或位置的数学实体。
- **矩阵(Matrix)**: 一个矩形阵列的数学表达式。
- GLM: 一个为OpenGL打造的数学库。
- **局部空间(Local Space)**: 一个物体的初始空间。所有的坐标都是相对于物体的原点的。
- **世界空间(World Space)**: 所有的坐标都相对于全局原点。
- **观察空间(View Space)**: 所有的坐标都是从摄像机的视角观察的。
- **裁剪空间(Clip Space)**: 所有的坐标都是从摄像机视角观察的,但是该空间应用了投影。这个空间应该是一个顶点坐标最终的空间,作为顶点着色器的输出。OpenGL负责处理剩下的事情(裁剪/透视除法)。
- **屏幕空间(Screen Space)**: 所有的坐标都由屏幕视角来观察。坐标的范围是从0到屏幕的宽/高。
- LookAt矩阵: 一种特殊类型的观察矩阵,它创建了一个坐标系,其中所有坐标都根据从一个位置正在观察目标的用户旋转或者平移。
- **欧拉角(Euler Angles)**: 被定义为偏航角(Yaw),俯仰角(Pitch),和滚转角(Roll)从而允许我们通过这三个值构造任何3D方向。
第二章 光照
- **颜色向量(Color Vector)**:一个通过红绿蓝(RGB)分量的组合描绘大部分真实颜色的向量。一个物体的颜色实际上是该物体所不能吸收的反射颜色分量。
- **冯氏光照模型(Phong Lighting Model)**:一个通过计算环境光,漫反射,和镜面光分量的值来估计真实光照的模型。
- **环境光照(Ambient Lighting)**:通过给每个没有被光照的物体很小的亮度,使其不是完全黑暗的,从而对全局光照进行估计。
- **漫反射着色(Diffuse Shading)**:一个顶点/片段与光线方向越接近,光照会越强。使用了法向量来计算角度。
- **法向量(Normal Vector)**:一个垂直于平面的单位向量。
- **法线矩阵(Normal Matrix)**:一个3x3矩阵,或者说是没有平移的模型(或者模型-观察)矩阵。它也被以某种方式修改(逆转置),从而在应用非统一缩放时,保持法向量朝向正确的方向。否则法向量会在使用非统一缩放时被扭曲。
- **镜面光照(Specular Lighting)**:当观察者视线靠近光源在表面的反射线时会显示的镜面高光。镜面光照是由观察者的方向,光源的方向和设定高光分散量的反光度值三个量共同决定的。
- **冯氏着色(Phong Shading)**:冯氏光照模型应用在片段着色器。
- **Gouraud着色(Gouraud shading)**:冯氏光照模型应用在顶点着色器上。在使用很少数量的顶点时会产生明显的瑕疵。会得到效率提升但是损失了视觉质量。
- **GLSL结构体(GLSL struct)**:一个类似于C的结构体,用作着色器变量的容器。大部分时间用来管理输入/输出/uniform。
- **材质(Material)**:一个物体反射的环境光,漫反射,镜面光颜色。这些东西设定了物体所拥有的颜色。
- **光照属性(Light(properties))**:一个光的环境光,漫反射,镜面光的强度。可以使用任何颜色值,对每一个冯氏分量(Phong Component)定义光源发出的颜色/强度。
- **漫反射贴图(Diffuse Map)**:一个设定了每个片段中漫反射颜色的纹理图片。
- **镜面光贴图(Specular Map)**:一个设定了每一个片段的镜面光强度/颜色的纹理贴图。仅在物体的特定区域显示镜面高光。
- **定向光(Directional Light)**:只有一个方向的光源。它被建模为不管距离有多长所有光束都是平行而且其方向向量在整个场景中保持不变。
- **点光源(Point Light)**:一个在场景中有位置的,光线逐渐衰减的光源。
- **衰减(Attenuation)**:光随着距离减少强度的过程,通常使用在点光源和聚光下。
- **聚光(Spotlight)**:一个被定义为在某一个方向上的锥形的光源。
- **手电筒(Flashlight)**:一个摆放在观察者视角的聚光。
- **GLSL uniform数组(GLSL Uniform Array)**:一个uniform值数组。它的工作原理和C语言数组大致一样,只是不能动态分配内存。
重要函数一览表
第一章
glBufferData
1.4 – 顶点缓冲对象VBO
1 | glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); |
介绍:是一个专门用来把用户定义的数据==复制==到当前绑定缓冲的函数。
- 第一个参数是目标缓冲的类型:顶点缓冲对象当前绑定到GL_ARRAY_BUFFER目标上
- 第二个参数指定传输数据的大小(以字节为单位);用一个简单的
sizeof
计算出顶点数据大小就行。 - 第三个参数是我们希望发送的实际数据。
- 第四个参数指定了我们希望显卡如何管理给定的数据。它有三种形式:
- GL_STATIC_DRAW :数据不会或几乎不会改变。
- GL_DYNAMIC_DRAW:数据会被改变很多。
- GL_STREAM_DRAW :数据每次绘制时都会改变。
- 补充:三角形的位置数据不会改变,每次渲染调用时都保持原样,所以它的使用类型最好是GL_STATIC_DRAW。如果,比如说一个缓冲中的数据将频繁被改变,那么使用的类型就是GL_DYNAMIC_DRAW或GL_STREAM_DRAW,这样就能确保显卡把数据放在能够高速写入的内存部分。
glVertexAttributePointer
1.4 – 6)链接顶点属性
1 | glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0); |
介绍:告诉OpenGL该==如何解析顶点数据==(应用到逐个顶点属性上)
- 第一个参数指定我们要配置的顶点属性
- 我们在顶点着色器中使用
layout(location = 0)
定义了position顶点属性的位置值(Location)。我们希望把数据传递到这一个顶点属性中,所以这里我们传入0
。
- 我们在顶点着色器中使用
- 第二个参数指定顶点属性的大小。顶点属性是一个
vec3
,它由3个值组成,所以大小是3。 - 第三个参数指定数据的类型,这里是GL_FLOAT(GLSL中
vec*
都是由浮点数值组成的)。 - 第四个参数定义我们是否希望数据被标准化(Normalize)。如果我们设置为GL_TRUE,所有数据都会被映射到0(对于有符号型signed数据是-1)到1之间。我们把它设置为GL_FALSE。
- 第五个参数叫做步长(Stride),它告诉我们在连续的顶点属性组之间的间隔。
- 由于下个组位置数据在3个
float
之后,我们把步长设置为3 * sizeof(float)
。(这个参数的意思简单说就是从这个属性第二次出现的地方到整个数组0位置之间有多少字节) - 我们也可以设置为0来让OpenGL决定具体步长是多少(只有当数值是紧密排列时才可用)。
- 由于下个组位置数据在3个
- 最后一个(第六个)参数的类型是
void*
,所以需要我们进行这个奇怪的强制类型转换。- 它表示位置数据在缓冲中起始位置的偏移量(Offset)。由于位置数据在数组的开头,所以这里是0。
每个顶点属性从一个VBO管理的内存中获得它的数据,而具体是从哪个VBO(程序中可以有多个VBO)获取则是通过在调用glVertexAttribPointer时绑定到GL_ARRAY_BUFFER的VBO决定的。由于在调用glVertexAttribPointer之前绑定的是先前定义的VBO对象,顶点属性
0
现在会链接到它的顶点数据。
glDrawArrays
1.4 – 元素缓冲对象 EBO
1 | glDrawArrays(GL_TRIANGLES, 0, 6) |
介绍:使用AVO绘制
- 第一个参数mode,绘制方式,OpenGL2.0以后提供以下参数:GL_POINTS、GL_LINES、GL_LINE_LOOP、GL_LINE_STRIP、GL_TRIANGLES、GL_TRIANGLE_STRIP、GL_TRIANGLE_FAN。
- 第二个参数first,从数组缓存中的哪一位开始绘制,一般为0。
- 第三个参数count,数组中顶点的数量。
glDrawElements
1.4 – 元素缓冲对象 EBO
1 | glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); |
介绍:使用当前绑定的索引缓冲对象中的索引进行绘制
- 第一个参数指定了我们绘制的模式,这个和glDrawArrays的一样
- 第二个参数是我们打算绘制顶点的个数,这里填6,也就是说我们一共需要绘制6个顶点
- 第三个参数是索引的类型,这里是GL_UNSIGNED_INT
- 第四个参数里我们可以指定EBO中的偏移量(或者传递一个索引数组,但是这是当你不在使用索引缓冲对象的时候),我们会在这里填写0。
glTexParameteri
1.6 – 纹理环绕方式 Wrap
1 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); |
介绍:指定Wrap方式
第一个参数指定了纹理目标;
- 我们使用的是2D纹理,因此纹理目标是GL_TEXTURE_2D。
第二个参数需要我们指定设置的选项与应用的纹理轴。我们打算配置的是
WRAP
选项,并且指定S
和T
轴。最后一个参数需要我们传递一个环绕方式(Wrapping),在这个例子中OpenGL会给当前激活的纹理设定纹理环绕方式为
GL_MIRRORED_REPEAT
。如果我们选择
GL_CLAMP_TO_BORDER
选项,我们还需要指定一个边缘的颜色。这需要使用glTexParameter函数的
fv
后缀形式,用GL_TEXTURE_BORDER_COLOR作为它的选项,并且传递一个float数组作为边缘的颜色值:float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
1
2
3
4
5
6
7
8
### glTexImage2D
> 1.6 -- 生成纹理
```c++
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
介绍:使用图片数据生成一个纹理
- 第一个参数指定了==纹理目标==(Target)。
- 设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理(任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
- 第二个参数为纹理指定==多级渐远纹理的级别==,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
- 第三个参数告诉OpenGL我们希望把纹理==储存为何种格式==。我们的图像只有
RGB
值,因此我们也把纹理储存为RGB
值。 - 第四个和第五个参数设置==最终的纹理的宽度和高度==。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
- 下个参数应该总是被设为
0
(历史遗留的问题)。 - 第七第八个参数定义了==源图的格式和数据类型==。我们使用RGB值加载这个图像,并把它们储存为
char
(byte)数组,我们将会传入对应值。 - 最后一个参数是真正的图像数据。
当调用glTexImage2D时,当前绑定的纹理对象就会被==附加==上纹理图像。
- 然而,目前只有基本级别(Base-level)的纹理图像被加载了,如果要==使用多级渐远纹理==,我们必须手动设置所有不同的图像(不断递增第二个参数)。或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
生成了纹理和相应的多级渐远纹理后,释放图像的内存是一个很好的习惯。
1 | stbi_image_free(data); |
texture
1.6 – 应用纹理
1 | FragColor = texture(ourTexture, TexCoord); |
介绍:使用GLSL内建的texture函数来采样纹理的颜色
- 第一个参数是纹理采样器
- 第二个参数是对应的纹理坐标
- 输出:经过插值得到的纹理坐标上对应的颜色
glUniformMatrix4fv
1.7 – GLM数学库
1 | glUniformMatrix4fv(transformLoc, 1, GL_FALSE, glm::value_ptr(trans)); |
介绍:给着色器的uniform矩阵变量传递参数
- 第一个参数是uniform的位置值。
- 第二个参数告诉OpenGL我们将要发送多少个矩阵,这里是1。
- 第三个参数询问我们是否希望对我们的矩阵进行转置(Transpose),也就是说交换我们矩阵的行和列。
- OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局。GLM的默认布局就是列主序,所以并不需要转置矩阵,我们填
GL_FALSE
。
- OpenGL开发者通常使用一种内部矩阵布局,叫做列主序(Column-major Ordering)布局。GLM的默认布局就是列主序,所以并不需要转置矩阵,我们填
- 最后一个参数是真正的矩阵数据。
- 但是GLM并不是把它们的矩阵储存为OpenGL所希望接受的那种,因此我们要先用GLM的自带的函数value_ptr来变换这些数据。
glm::ortho
1.8 – 坐标系统
1 | glm::ortho(0.0f, 800.0f, 0.0f, 600.0f, 0.1f, 100.0f); |
介绍:正射投影矩阵,将处于这些x,y,z值范围内的坐标变换为标准化设备坐标。
- 前两个参数指定了平截头体的左右坐标
- 第三和第四参数指定了平截头体的底部和顶部
- 第五和第六个参数则定义了近平面和远平面的距离
glm::perspective
1.8 – 坐标系统
1 | glm::mat4 proj = glm::perspective(glm::radians(45.0f), (float)width/(float)height, 0.1f, 100.0f); |
介绍:透视投影矩阵
- 第一个参数定义了fov的值,它表示的是视野(Field of View),相当于设置了观察空间的大小。
- 第二个参数设置了宽高比,由视口的宽除以高所得。
- 第三和第四个参数设置了平截头体的近和远平面。
glm::lookAt
1.9 – 摄像机
1 | view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp); |
介绍:相机矩阵
- 第一个参数是相机位置
- 第二个参数是相机目标向量
- 第三个参数是向上向量
第四章
glFramebufferTexture2D
4.5 – 帧缓冲
1 | glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); |
介绍:将纹理附件绑定到帧缓冲对象中(纹理附件可以通过 glTexImage2D 创建)
==glFrameBufferTexture2D==有以下的参数:
target
:帧缓冲的目标(绘制、读取或者两者皆有)attachment
:我们想要附加的附件类型。当前我们正在附加一个颜色附件。注意最后的0
意味着我们可以附加多个颜色附件。我们将在之后的教程中提到。textarget
:你希望附加的纹理类型texture
:要附加的纹理本身level
:多级渐远纹理的级别。我们将它保留为0。
glRenderbufferStorage
4.5 – 帧缓冲
1 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, 800, 600); |
介绍:创建一个深度和模板渲染缓冲对象
- 第一个参数是缓冲目标
- 第二个参数是内部格式,它封装了24位的深度和8位的模板缓冲
- 第三、四个参数是宽高
glFramebufferRenderbuffer
4.5 – 帧缓冲
1 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo); |
介绍:将渲染缓冲对象绑定到帧缓冲中
- target:帧缓冲的目标(绘制、读取或两者皆有)
- attachment:我们想要附加的附件类型。当前附件一个深度模板附件
- buffer target:附加渲染缓冲对象类型
- buffer object:渲染缓冲对象本身
glBufferSubData
4.7 – 高级数据
1 | glBufferSubData(GL_ARRAY_BUFFER, 24, sizeof(data), &data); |
- 第一个参数是缓冲目标
- 第二个参数是偏移量
- 第三个参数是数据大小
- 第四个参数是数据本身
glCopyBufferSubData
4.7 – 高级数据
1 | void glCopyBufferSubData(GLenum readtarget, GLenum writetarget, GLintptr readoffset, |
介绍:从一个缓冲中复制数据到另一个缓冲中
- 第一个和第二个参数是复制源和复制目标的缓冲目标
- 第三个和第四个参数是偏移量
- 第五个参数是数据大小
glGetUniformBlockIndex
4.8 – 高级GLSL
1 | unsigned int lights_index = glGetUniformBlockIndex(shaderA.ID, "Lights"); |
介绍:获取着色器中已定义Uniform块的位置值索引
- 第一个参数是一个程序对象
- 第二个参数是Uniform块的名称
glUniformBlockBinding
4.8 – 高级GLSL
1 | glUniformBlockBinding(shaderA.ID, lights_index, 2); |
介绍:将Uniform块绑定到一个特定的绑定点中
- 第一个参数是一个程序对象
- 第二个参数是Uniform块索引
- 第三个参数是链接到的绑定点
glBindBufferBase
4.8 – 高级GLSL
1 | glBindBufferBase(GL_UNIFORM_BUFFER, 2, uboExampleBlock); |
介绍:将Uniform缓冲对象绑定到绑定点
- 第一个参数是缓冲目标
- 第二个参数是绑定点
- 第三个参数是Uniform缓冲对象
glBindBufferRange
4.8 – 高级GLSL
1 | glBindBufferRange(GL_UNIFORM_BUFFER, 2, uboExampleBlock, 0, 152); |
介绍:将Uniform缓冲对象的指定区域绑定到绑定点
- 第一个参数是缓冲目标
- 第二个参数是绑定点
- 第三个参数是Uniform缓冲对象
- 第四个参数是偏移量
- 第五个参数是数据大小
glDrawArraysInstanced
4.10 – 实例化
1 | glDrawArraysInstanced(GL_TRIANGLES, 0, 6, 100); |
介绍:使用实例化绘制 (VAO)
- 参考 glDrawArrays
- 最后一个参数是实例个数
glDrawElementsInstanced
4.10 – 实例化
1 | glDrawElementsInstanced(GL_TRIANGLES, static_cast<unsigned int>(rock.meshes[i].indices.size()), GL_UNSIGNED_INT, 0, amount); |
介绍:使用实例化绘制 (EBO)
- 参考 glDrawElements
- 最后一个参数指定实例个数
glTexImage2DMultisample
4.11 – 抗锯齿
1 | glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE); |
介绍:创建一个支持储存多个采样点的纹理(参考glTexImage2D)
- 第一个参数是纹理目标GL_TEXTURE_2D_MULTISAPLE。
- 第二个参数设置的是纹理所拥有的样本个数(书上设4)
- 最后一个参数为GL_TRUE,图像将会对每个纹素使用相同的样本位置以及相同数量的子采样点个数。
glRenderbufferStorageMultisample
4.11 – 抗锯齿
1 | glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height); |
介绍:创建一个支持储存多个采样点的纹理(参考glRenderbufferStorage)
- 第二个参数是样本的数量