Shader
GPU上的渲染程序。
渲染管线的流程:
几何阶段:
- 顶点着色器(Vertex Shader):
主要工作是坐标变换,逐顶点光照和输出后续阶段所需的数据。
所有的几何体,以顶点的形式,先经过顶点着色器,进行顶点的坐标转换,把顶点坐标从模型空间转换到齐次裁剪空间,正向渲染(Forward Render)会对顶点进行光照计算,得到顶点的光照。
VBO(Vertex Buffer Object):在显存中申请一块空间,存储顶点的各类属性信息,在渲染时,直接从显存的VBO中读取顶点的属性信息,不需要从CPU传输数据,执行效率更高。
- 裁剪处理(Clipping):
完全在视野内的图元会继续传递到下一个阶段,完全在视野外的图元不会继续向下传递,因为他们不需要渲染。而部分在视野内需要进行一个处理,这就是裁剪。例如,一条线段的一个顶点在视野内,另一个顶点在视野外,那么在视野外部的顶点应该使用一个新的顶点来代替,这个新的顶点位于这条线段和视野边界的交点处。
- 屏幕映射(Screen Mapping):
主要任务是把每个图元的x和y坐标转换到屏幕坐标系。
光栅化阶段:
重要的目标就是计算每个片元覆盖了哪些像素,以及为这些像素计算他们的颜色。
- 三角形设置。
上一阶段输出的是三角形网格的顶点,即我们得到的是三角形每条边的两个端点。但如果要得到整个三角形网格对像素的覆盖情况,就必须计算每条边上的像素坐标。为了能够计算边界像素的坐标信息,就需要得到三角形边界的表示方式。这样一个计算三角形网格表示数据的过程叫三角形设置。
- 三角形遍历。
根据上一阶段的计算结果来判断一个三角形网格覆盖了哪些像素,被覆盖的像素,就会对应生成一个片元(fragment),并使用三角网格的3个顶点的顶点信息对整个覆盖区域的像素进行插值,得到片元中的状态。这一步输出的就是一个片元序列。需要注意的是,一个片元并不是真正意义上的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色。包含了屏幕坐标,深度信息,以及从几何阶段输出的顶点信息,例如法线、纹理坐标等。
- 片元着色器(Fragment Shader):
为了在片元着色器中,进行纹理采样,通常在顶点着色器阶段输出每个顶点对应的纹理坐标,然后经过光栅化阶段对三角形网格的三个顶点对应的纹理坐标进行插值,得到其覆盖的片元的纹理坐标。
- 逐片元操作,或是光栅化操作(Per-Fragment Operations):
1. 决定每个片元的可见性。例如模板测试、深度测试等。
2. 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行混合。
模板测试(Stencil Test):
如果开启了模板测试,GPU会首先读取(使用读取掩码)模板缓冲区中该片元位置的模版缓冲值(Stencil buffer),然后将该值和读取到的参考值(Reference buffer)进行比较,比较函数由开发者指定,例如小于时舍弃该片元等。模板测试通常用于限制渲染的区域。还有些高级用法,渲染阴影、轮廓渲染等。
深度测试(Depth Test):
如果开启了深度测试,GPU将会把该片元的深度值与已经存在于深度缓冲区的深度值进行比较。比较函数由开发者指定。通常比较函数是小于等于的关系,才会让它通过测试,即如果这个片元的深度值大于等于当前缓冲区的值,就会舍弃它。因为我们只想显示出离摄像机最近的物体,那些被其他物体遮挡的就不需要出现在屏幕上。如果通过了测试,开发者可以通过开启/关闭深度写入,指定是否要用这个片元的深度值覆盖原有的深度值。
混合(Blend):
对于不透明物体,关闭混合操作,这样片元着色器计算得到的颜色值就可以直接覆盖掉已经存在于颜色缓冲区的颜色值。对于半透明物体,需要使用混合操作,让物体看起来是透明的。开启混合功能,GPU会把片元着色器计算得到的颜色值和已经存在于颜色缓冲区的颜色值,进行混合。
默认情况下OpenGL使用帧缓冲区作为渲染的目的地。帧缓冲区(FrameBuffer),包括深度缓冲区(depth),模板缓冲区(stencil)和颜色缓冲区(color buffers)。颜色缓冲区是必不可少的,其他缓冲区可以存在可以不存在。
最后,为了避免我们看到正在进行光栅化的图元,GPU会使用双重缓冲的策略。对场景的渲染是放在幕后的,当场景被渲染到后置缓冲中,GPU就会交换后置缓冲区和前置缓冲区中的内容,而前置缓冲区是之前