一、渲染与着色器
渲染(Render)本质上就是将图形显示在屏幕上的这个过程,最终的目的就是将 3D 的世界用 2D 的像素点表示出来(UI 为特例),这也是渲染管线(Graphics Pipeline)所负责管理的,图形渲染管线可以被划分为两个主要部分:第一部分把你的 3D 坐标转换为 2D 坐标,第二部分是把 2D 坐标转变为实际的有颜色的像素
细分的话,图形渲染管线可以被划分为几个阶段,每个阶段将会把前一个阶段的输出作为输入。所有这些阶段都是高度专门化的,它们都有一个特定的函数,并且很容易并行执行。正是由于它们具有并行执行的特性,当今大多数显卡都有成千上万的小处理核心,它们在GPU上为每一个(渲染管线)阶段运行各自的小程序,从而在图形渲染管线中快速处理你的数据。这些小程序就叫做着色器(Shader),着色器可以允许开发者自己配置
上图就是图形渲染管线的每个阶段的抽象展示,蓝色的部分我们可以注入自定义的着色器
二、渲染管线
- 顶点着色器(Vertex Shader):把一个单独的顶点作为输入,把顶点坐标从模型空间变换到齐次裁剪空间,同时顶点着色器允许我们对顶点属性进行一些基本处理。
- 图元装配(Primitive Assembly):将顶点着色器输出的所有顶点作为输入(几个经典图元:GL_POINTS、GL_TRIANGLES、GL_LINE_STRIP),并将所有的点装配成指定图元的形状
- 几何着色器(Geometry Shader):把图元形式的一系列顶点的集合作为输入,它可以通过产生新顶点构造出新的(或是其它的)图元来生成其他形状
- 光栅化阶段(Rasterization Stage):将图元映射为最终屏幕上相应的像素,生成供片段着色器使用的片段(渲染一个像素所需的所有数据),在片段着色器运行之前还会执行裁剪,将超出视图外的像素全部扔掉
- 片段着色器(Fragment Shader):计算一个像素的最终颜色,最精彩的地方,光照和阴影等都在这个阶段被考虑
- 测试和混合(Alpha & Blending):检测片段的对应的深度值,判断这个像素是其它物体的前面还是后面,决定是否应该丢弃等。这个阶段也会检查alpha值(alpha值定义了一个物体的透明度)并对物体进行混合(Blend),所以,即使在片段着色器中计算出来了一个像素输出的颜色,在渲染多个三角形的时候最后的像素颜色也可能完全不同
一般来讲,我们只需要配置顶点和片段着色器,几何着色器使用默认着色器
三、顶点坐标与标准化输入
OpenGL 是一个 3D 图形库,所以我们在 OpenGL 中指定的所有坐标都是 3D 坐标,但是 OpenGL 有一个标准化设备坐标范围,也就是 [-1, 1],当且仅当坐标落在这个范围内才会显示在屏幕上
除此之外,因为我们最终渲染的结果是在一个 2D 的屏幕上显示内容,所以一般 z 坐标都表示为深度,像我们如果只是简单地画三角形的话,深度值肯定设为 0 就好
- 标准化设备坐标范围:考虑到之前说的视口,既然坐标范围是 [-1, 1],那么很显然 (-1, -1) 就是视口的左下角,而 (0, 0) 则在视口的最中央,下图就是一个形象的解释(当然窗口 ≠ 视口,下图仅是窗口大小和视口一致)
- 深度:可以代表一个像素在空间中和你的距离,如果离你远就可能被别的像素遮挡,你当然就看不到它了,它就会被丢弃,以节省资源
- 屏幕空间坐标:假设你电脑的分辨率为 2k(2560 x 1440),那么屏幕空间坐标 (1280, 720) 对应的就是屏幕的正中央
还记得之前的 glViewport 函数嘛,它便可以将标准化坐标通过视口变换转换为屏幕空间坐标
参考文献:https://learnopengl.com/#!Getting-started/OpenGL