总目录
LearnOpenGL从入门到入魔(1):OpenGL简介
LearnOpenGL从入门到入魔(2):绘制2D图形
LearnOpenGL从入门到入魔(3):绘制纹理
LearnOpenGL从入门到入魔(4):绘制3D图形
LearnOpenGL从入门到入魔(5):简单滤镜效果
LearnOpenGL从入门到入魔(6):光照(光照基础,材质)
LearnOpenGL从入门到入魔(7):光照(光照贴图,投光物)
1. OpenGL基本概念
1.1 图像渲染管线
在OpenGL中,任何事物都在3D空间中,而屏幕和窗口却是2D像素数组,这导致OpenGL的大部分工作都是关于把3D坐标转变为适应你屏幕的2D像素。3D坐标转为2D坐标的处理过程是由OpenGL的图形渲染管线
(GraphicsPipeline,或称管线)管理的,该过程可表述为将一堆原始图形数据途经一个输送管道
,期间经过各种变化处理最终出现在屏幕的过程。图形渲染管线可以被划分为两个主要部分:第一部分将输入的3D坐标转换为2D坐标
,第二部分是把2D坐标转变为实际的有颜色的像素
。下图表示一个图形渲染管线的每个阶段的抽线表示:
蓝色部分着色器允许我们自定义,即可编程着色器,包括
顶点着色器
、几何着色器
和片段着色器
,而几何着色器通常使用默认的就可以了,其它部分也是用默认的即可。在现代OpenGL中,我们必须定义至少一个顶点着色器和一个片段着色器,因为GPU中没有默认的顶点/片段着色器。
图形渲染管线各阶段功能如下:
- 顶点着色器(Vertex Shader)
顶点着色器接收一组顶点数组作为输入,这些顶点数据使用顶点属性表示,它会将每个顶点的3D坐标转换为另一种3D坐标,同时对该顶点的顶点属性
进行一些基本处理,然后再将经过处理后的顶点数组。
- 顶点属性
顶点属性指定了每个顶点的各种属性,包括坐标(postion)、颜色(color)、法线(normal)以及纹理(Texture)等。下面演示了如何定义一组顶点数据,其中每个顶点包含了坐标、颜色和纹理属性:
GLfloat vertex[ 4*(3+4+2)] = { //x,y,z, r,g,b,a s,t(纹理) -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 1.0f, // 第1个顶点 0.5f, 0.5f, 0.0f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, // 第2个顶点 -0.5f, -0.5f, 0.0f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, // 第3个顶点 0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 1.0f, 1.0f, 1.0f, // 第4个顶点 };
在OpenGL中,顶点属性数据存储在一段连续的内存空间。下图展示了3个顶点的存储情况,其中每个顶点的坐标和颜色属性分别占3个float,即12字节(BYTE):
- 形状(图元)装配(SHAPE/Primitive Assembly)
图元(Primitive),或称形状,是指OpenGL的渲染类型,当我们将输入的坐标和颜色渲染成具体的某种表示时,需要在调用OpenGL的指令时指定图元,比如GL_POINTS
表示点,GL_TRIANGLES
表示三角形以及GL_LINE_STRIP
表示一系列的连续直线等。因此,图元装配的作用时将顶点着色器输出的所有顶点作为输入
,输出制定的基本图元。
- 几何着色器(Gemometry Shader)
几何着色器把基本图元形式的顶点的集合作为输入,可以通过产生新顶点构造出新的(或是其他的)基本图元来生成其他形状。
- 光栅化(Rasterization Stage)
光栅化阶段将几何着色器输出图元映射为最终屏幕上相应的像素,生成供片段着色器使用的片段(Fragment)
。在片段着色器运行之前会执行裁切,而裁切的目的即为丢弃超出我们视图以外的所有像素,以便提升执行效率。
片段(Fragment):指OpenGL渲染一个像素所需的所有数据。
- 片段着色器(Fragment Shader)
片段着色器以光栅化阶段生成的片段作为输入,它的作用是计算一个像素的最终颜色,这也是所有OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据(比如光照、阴影、光的颜色等等),这些数据可以被用来计算最终像素的颜色值。
- 测试与混合(Test & Blend)
该阶段的目的为检测片段对应的深度值,用它们来判断这个像素是其它物体的前面还是后面,决定是否应该丢弃。同时,也会检查用于定义物体透明度的alpha值
,并对物体进行混合。所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个有重叠的物体的时候最后的像素颜色也可能完全不同。
1.2 着色器
图形渲染管线接受一组3D坐标,然后把它们转变为屏幕上的有色2D像素输出。图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的(它们都有一个特定的函数),并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序叫做着色器(Shader)
。OpenGL的着色器使用着色器语言GLSL(OpenGL Shading Language)
编写,通过编译顶点着色器的源码,就可以在程序中使用它。
1.2.1 顶点着色器
顶点着色器接收一组顶点数据
(VertextData)作为输入,这些顶点数据使用顶点属性表示,它会将每个顶点的3D坐标转换为另一种3D坐标(标准化设备坐标
),同时对该顶点的顶点属性
进行一些基本处理,需要注意的是,顶点着色器依次处理顶点集合中的顶点数据。比如,我们要绘制一个三角形,那么就需要向顶点着色器输入3个顶点数据,且每个顶点的坐标(postion)是3D的。下列代码描述一个非常简单的顶点着色器源码:
// 注释(1)
#version 330 core
// 注释(2)
layout (location = 0) in vec3 aPos;
void main()
{
// 注释(3)
// GLSL中一个向量最多有四个分量,使用vec4表示
// 每个分量值表示空间中的坐标,即
// vec.x 表示x轴坐标;
// vec.y 表示y轴坐标;
// vec.z 表示z轴坐标;
// vec.w 不用做表达空间的位置,主要应用再透视除法(后续解释)
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}
注释(1) 用于指明OpenGL的使用版本为3.3,采用核心模式;注释(2) 有两个作用,一是使用in
关键字在顶点着色器中声明所有的输入顶点属性(注:本例中只关心顶点的位置position数据
),二是通过layout (location = 0)
设定输入变量的位置值Location;注释(3) 用于设置顶点着色器的输出,即将位置数据赋值给预定义变量gl_Position
,该变量的类型为vec4,这是一个包含4个分量的向量。
- 标准化设备坐标
当顶点坐标经过顶点着色器处理后,将会被转换为
标准化设备坐标
,所谓标准化设备坐标
,是一个x、y和z值在-1.0~1.0
的一小段空间。任何落在范围外的坐标都会被丢弃或者裁剪,不会显示在设备屏幕上。下图展示了在标准化坐标中定义一个2D三角形,即忽略z轴。
1.2.2 片段着色器
片段着色器用于计算一个像素的最终颜色值
,该阶段是OpenGL高级效果产生的地方。通常,片段着色器包含3D场景的数据,比如光照、阴影、光的颜色等等,这些数据将被用来计算最终像素的颜色值。在计算机图形中,一个像素的颜色值由4个分量组成,即R(红色)、G(绿色)、B(蓝色)和A(透明度),就是我们熟知的RGBA
。在OpenGL中,颜色的每个分量的取值范围为0.0f~1.0f
,颜色程度由浅色到深色。下列代码演示了如何创建一个简单的片段着色器:
// 指明OpenGL的使用版本为3.3,并采用核心模式
#version 330 core
// 定义片段着色器的输出
// 输出一个vec4类型的变量,该变量表示像素的颜色值
out vec4 FragColor;
void main()
{
// 对变量进行赋值
// 即每个像素的颜色为红色
FragColor = vec4(1.0f, 0.0f, 0.0f, 1.0f);