GAME_OpenGL_Learn_1
OpebGL开发-01
一、前置需求
C++
数学基础-线性代数、几何学
二、入门
1、基础概念
- OpenGL
OpenGL主要地被视为API(application programming interface)。然而,OpenGL 本身又并不是一个 API,而仅仅是一个规范,由Khronos Group开发和维护。
OpenGL 规范准确地指定了每个函数的结果/输出应该是什么以及它应该如何执行。然后,由实现该规范的开发人员提出该函数应如何运行的解决方案。由于OpenGL规范没有给出实现细节,因此OpenGL的实际开发版本可以有不同的实现,只要它们的结果符合规范。
开发实际 OpenGL 库的人通常是显卡制造商。您购买的每张显卡都支持特定版本的 OpenGL,这些版本是专门为该卡(系列)开发的 OpenGL 版本。当使用 Apple 系统时,OpenGL 库由 Apple 自己维护,而在 Linux 下,存在图形供应商的版本和爱好者对这些库的改编的组合。这也意味着每当 OpenGL 显示出不应该出现的奇怪行为时,这很可能是显卡制造商(或开发/维护该库的任何人)的错误。
由于大多数实现都是由显卡制造商构建的,因此每当实现中出现错误时,通常可以通过更新显卡驱动程序来解决;这些驱动程序包括您的卡支持的最新版本的 OpenGL。这就是为什么总是建议偶尔更新图形驱动程序的原因之一。
Khronos 公开托管所有 OpenGL 版本的所有规范文档。3.3版的OpenGL规范供参考。 - core-profile vs immediate mode
Immediate mode: 易于使用、功能隐藏在库中、效率低、开发人员没有太多控制权
Core-profile: OpenGL的division、删除了所有弃用的功能、灵活且高效、学习困难
从 3.3 开始的所有未来版本的 OpenGL 都在不改变 OpenGL 核心机制的情况下为 OpenGL 添加了额外的有用功能;新版本只是引入了稍微更有效或更有用的方法来完成相同的任务。结果是所有概念和技术在现代 OpenGL 版本中都保持不变。 - OpenGL对扩展的支持
- 状态机
OpenGL本身是一个大型的状态机。内置变量集合,定义了OpenGL应该如何运行。OpenGL的状态通常指OpenGL context。
举个例子。每当我们告诉 OpenGL 我们现在想要绘制直线而不是三角形时,我们都会通过更改一些设置 OpenGL 应如何绘制的上下文变量来更改 OpenGL 的状态。一旦我们通过告诉 OpenGL 它应该绘制线条来更改上下文,下一个绘制命令现在将绘制线条而不是三角形。 - Objects
一个Object是一个集合,代表了OpenGL状态的子集。
使用这些对象的好处在于,我们可以在应用程序中定义多个对象,设置它们的选项,并且每当我们启动使用 OpenGL 状态的操作时,我们都会将对象与我们的首选设置绑定。拥有多个对象允许我们指定许多模型,每当我们想要绘制特定模型时,我们只需在绘制之前绑定相应的对象即可,而无需再次设置其所有选项。
2、创建窗口前
绘图之前,我们需要创建一个OpenGL context和要绘制的应用程序窗口。这些操作是特定于每个操作系统的,OpenGL尝试从这些操作中抽象出来。我们可以用库来实现这些操作系统特定的工作,包括创建context、application window和处理用户输入的程序。
- GLFW
提供了在屏幕上渲染的基础必需品。它允许我们创建OpenGL context、定义窗口参数并且处理用户输入。 - GLAD
因为 OpenGL 实际上只是一个标准/规范,所以由驱动程序制造商将规范实施到特定显卡支持的驱动程序。由于OpenGL驱动程序有很多不同的版本,其大部分函数的位置在编译时是未知的,需要在运行时查询。然后,开发人员的任务是检索他/她需要的函数的位置并将它们存储在函数指针中以供以后使用。检索这些位置是特定于操作系统的。在 Windows 中,它看起来像这样:
值得庆幸的是,也有用于此目的的库,其中GLAD是一个流行且最新的库。
3、创建窗口
通过观察一个示例来将窗口运行起来。在这个示例项目中,展示了窗口创建的基本流程,涉及到GLFW、GLAD等库中函数的使用。
项目演示移步当前目录下的相应博文:game_openGL_project1。
或查看源频道
4、创建三角形
接下来的内容涉及到OpenGL的核心原理,请注意!
由于在OpenGL中everything都在三维空间,但屏幕窗口仅能展示二维像素矩阵。所以OpenGL的一项大工作就是将3D坐标系转为二维像素矩阵以在屏幕上显示。这个工作由OpenGL中的graphics pipeline管理。
graphics pipeline的工作可以大致分为两部分:第一部分将 3D 坐标转换为 2D 坐标,第二部分将 2D 坐标转换为实际的彩色像素。
graphics pipeline的实际工作过程要更加复杂。它的处理过程会分为几步,并且每步需要前一步的输出作为输入。所有步骤都执行其特定功能并且可以并行执行。
接下来给出shader的概念。为了传达的准确性,Because of their parallel nature, graphics cards of today have thousands of small processing cores to quickly process your data within the graphics pipeline. The processing cores run small programs on the GPU for each step of the pipeline. These small programs are called shaders.
一些shaders可以被开发者自由定义配置,仅需了解GLSL(OpenGL Shading Language)。
下图展示了graphics pipeline所有步骤的抽象表示。注意其中蓝色部分表示我们可以向其中插入自己的shaders。
首先介绍vertex data。这是一个vertex的集合。而一个vertex又是一堆称为vertex attributes数据的集合。vertex attributes是所有顶点的属性数据,包括但不仅限于3D坐标和颜色值。为了让OpenGL知道如何处理vertex data[],也就是使用数据渲染成为点集合、三角形集合或是一条长线。我们需要在调用绘图命令时传递提示给OpenGL。这些提示被称作primitives。例如,GL_POINTS、GL_TRIANGLES和GL_LINE_STRIP。
vertex shader取单个vertex作为输入,其主要目的是将 3D 坐标转换为不同的 3D 坐标(稍后详细介绍),还允许我们对vertex attributes进行一些基本处理。
vertex shader的输出有选择性的被送往geometry shader,这意味着geometry shader将会以vertices作为输入。顺理成章,geometry shader的工作就是将点集合生成为图元(形状)。
shape assembly阶段的工作有点迷,等待更新!
rasterization将上一阶段生成的图元映射为最终屏幕上对应的像素。产生fragment shader使用的fragment(指OpenGL渲染一个像素需要的所有数据)。另外,在进入下一阶段前会进行裁剪,会忽略超出视野范围的所有fragaments。
fragement shader是去计算一个像素的最终颜色,这是所有高级OpenGL效果发生的阶段。通常fragament中包含了计算一个像素颜色的数据。例如,灯光、阴影、灯光颜色等。
最后是alpha test and blending,此阶段检查fragement的相应深度值,并使用这些值来检查生成的片段是否位于其他对象的前面或后面,并相应地丢弃。该阶段还检查α值(alpha 值定义对象的不透明度)和混合相应的对象。
最后的话,图形管道是一个相当复杂的整体,包含许多可配置的部分。然而,对于几乎所有情况,我们只需要使用顶点和片段着色器。几何着色器是可选的,通常保留其默认着色器。还有我们没有在这里描述的曲面细分阶段和变换反馈循环,但这是稍后的内容。
项目演示移步当前目录下的对应博文game_opengl_project2,注意:项目演示中涉及有OpenGL的核心原理。
或查看源频道
5、shaders
- GLSL
全称OpenGL Shading Lanuage,是类似C的语言,是专门为图形定制的,包含针对向量和矩阵运算的特征。
着色器总是以版本声明开始,后面是输入和输出变量、uniform及其主函数。其中输入变量指一个vertex的attributes。一个典型的渲染器有以下结构:
vertex attributes的最大数量总是受到硬件的限制,但OpenGL一般要确保至少要有16个4分量的顶点属性,可以通过以下方式查询: - 谈到语言就必须先讲到支持的数据类型
GLSL除了支持继承自C语言的基本的数据类型(int、float、double、uint、bool)外,还支持两种特殊的容器数据类型:vectors和matrices。 - vector
vector是一个容器类型的数据类型,内含2、3或4个基本数据类型。可以通过下列方式声明,其中n代表容量,只能取2、3或4。
接下来将会介绍shaders的语法规则,还会演示uniforms关键词的用法。以及如何处理更多的vertex attributes。
项目演示移步当前目录下的game_opengl_project3。
6、transformation
学过计算机图形学吗?学过就懂了!
8、 coordinate systems
openGL总是希望在每个vertex shader之后,所有我们希望显示的点落在NDC(normalized device coordinate)上,即x、y和z都落在-1到1之间。
将所有输入点转换到NDC上是一步一步完成的,期间要经过多个坐标系的转换。原因是:不同的坐标系能够分别便捷地完成某些工作。共有以下5中不同的坐标系
- Local Space/Object Space
- World Space
- View Space
- Clip Space
- Screen Space
global picture
通过这张图,我们对顶点坐标的处理流程有了大致了解。我们之所以将顶点变换到各个不同的空间的原因是有些操作在特定的坐标系统中才有意义且更方便。例如,当需要对物体进行修改的时候,在局部空间中来操作会更说得通;如果要对一个物体做出一个相对于其它物体位置的操作时,在世界坐标系中来做这个才更说得通,等等。如果我们愿意,我们也可以定义一个直接从局部空间变换到裁剪空间的变换矩阵,但那样会失去很多灵活性。
local space
9、相机
OpenGL 本身并不熟悉相机的概念,但我们可以尝试通过反向移动场景中的所有对象来模拟相机,给人一种我们正在移动的错觉。在本章将会讨论如何在OpenGL中建立一个相机,一种允许你在三维空间中自由移动的fly style camera。还会讨论如何处理键盘和鼠标输入、一个自定义的相机类。
- Camera/View space
我们所谈论的camera/view space是以相机为场景原点来观察所有空间内的对象。为了定义一个相机,我们需要相机的位置、它看的方向、指向右侧的向量和指向上侧的向量。也就是一个以相机为原点的三维坐标系。如下图:
通过定义camera的位置和三个向量,OpenGL就会自动帮我们建立摄像机和观察矩阵,至于观察矩阵的细节,或许不了解也是可以的
项目演示移步当前目录下的对应博文。