Shader Learing(Render Pipeline篇)
硬件相关:
传统的 GPU 使用固定功能的管道(pipelines)实现图形算法,现代的 GPU 则是具有高度灵活性的可编程设备,使用 SIMD 处理器并行处理大量数据,从而实现图形性能。(SIMD:单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集)。GPUvscpu:cpu 大部分面积为控制器和寄存器,与之相比,GPU 拥有更多的 ALU(Arithmetic Logic Unit,逻辑运算单元)用于数据处理,而非数据高速缓存和流控制,这样的结构适合对密集型数据进行并行处理。cpu 擅长控制,GPU 擅长计算,这是由他们的硬件架构设计所决定的。
渲染管线:
GPU 内部的渲染流程图:
对模型进行简化:
下面对渲染管线进行详细解释:
1、 模型创建,配置在虚拟空间里主要是在 cpu 里进行的处理。
2、 几何处理:① 顶点变换:把模型顶点变换到显示屏上的坐标。为了将坐标从一个坐标系转换到另一个坐标系,我们需要用到几个转换矩阵,最重要的几个分别是模型/视图/投影三个矩阵。顶点坐标开始于局部空间,称为局部坐标,然后经过世界坐标,观察坐标,裁剪坐标,并最后以屏幕坐标结束。
② 光照计算与法线变换:
光照将在各个顶点处进行计算,通常称之为逐顶点光照计算,不过目前逐顶点光照已经稍显过时,而更为常见的技术则是逐片元光照。
OpenGL 的光照仅仅使用了简化的模型并基于对现实的光照估计来进行模拟,这样处理起来会更容易一些。其中一个模型被称为冯氏光照模型,冯氏光照模型的主要结构由 3 个元素组成:环境、漫反射和镜面光照。
环境光照:我们使用一个常量(光)颜色添加进物体的照射点的最终颜色里,这看起来就像即使没有直射光源也始终存在着一些发散的光。
漫反射光照:点乘返回一个标量,我们可以用它计算光线对片段颜色的影响,基于不同片段所朝向光源的方向的不同,这些片段被照亮的情况也不同。所以,我们需要些什么来计算漫反射光照?法向量:一个垂直于顶点表面的向量。定向的光线:作为光的位置和片段的位置之间的向量差的方向向量。为了计算这个光线,我们需要光的位置向量和片段的位置向量。
镜面光照:和环境光照一样,镜面光照同样依据光的方向向量和物体的法向量,但是这次它也会依据观察方向,例如玩家是从什么方向看着这个片段的。
③纹理坐标变换与材质状态:纹理坐标的计算,就是计算出在怎样的多边形上,把怎样的材质纹理通过什么样的方式贴上去。还有,实际做纹理映射是在 Piexl Shader 的时候,这里只是纹理映射的准备工作。之后在图形的其它片段上进行片段插值。
如果我们想要在 OpenGL 中模拟多种类型的物体,我们必须为每个物体分别定义材质(Material)属性。材质是一个物体反射的环境,漫反射,镜面反射光照,这些东西设定了物体的颜色。④进入 Vertex Shader 里的是一次一个的顶点,而进入 Geometry Shader 的是一次一批的顶点,Geometry Shader 掌握着这些顶点所组成的图元的信息。Geometry Shader 的处理阶段处于流水线的栅格化之前。Geometry Shader (几何元着色器)是继 Vertex Shader和 Piexl Shader 之后,由 Shader Model 4(第四代显卡着色架构)正式引入的第三个着色器。
3、 光栅化:由图元处理传递过来的图元数据,在此将会被分解成更小的单元并对应帧缓冲区的各个像素,这些单元被称之为片元,一个片元可能包含深度、颜色、纹理坐标等属性。片元的属性则是图元上顶点数据等经过插值而确定的,这里生成的片元将会包含主颜色和次颜色,即使用插值(平滑着色) 或者使用最后一个顶点颜色(平面着色)。
4、 片段处理:
① 上纹理:通过在 Vertex Shader 上得到的纹理坐标,负责从原来的纹理(Texture)读出纹素(Texel)的就是纹理单元(Texture Unit)。通过纹理单元取出的像素颜色,和之前纹理单位的阴影处理算出的像素颜色一同处理,就得到了最终的像素颜色。
② Alpha Test 采用一种只要一个像素的 alpha 不满足条件,那么它就会被 fragment shader舍弃。被舍弃的 fragments 不会对后面的各种 Tests 产生影响;否则,就会按正常方式写入到缓存中,并进行正常的深度检验等等,也就是说,Alpha Test 是不需要关闭ZWrite 的。Alpha Test 产生的效果也很极端,要么完全透明,即看不到,要么完全不透明。
③ 模板测试:模板缓冲区可以为屏幕上的每个像素点保存一个无符号整数值(通常的话是个 8 位整数)。这个值的具体意义视程序的具体应用而定。在渲染的过程中,可以用这个值与一个预先设定的参考值相比较,根据比较的结果来决定是否更新相应的像素点的颜色值。这个比较的过程被称为模板测试。模板测试发生在透明度测试(alpha test)之后,深度测试(depth test)之前。如果模板测试通过,则相应的像素点更新,否则不更新。
④ 深度测试:深度缓冲(Depth Buffer)就像颜色缓冲(Color Buffer)(存储所有的片段颜色:视觉输出)那样存储每个片段的信息,(通常) 和颜色缓冲区有相同的宽度和高度。深度缓冲由窗口系统自动创建并将其深度值存储为 16、 24 或 32 位浮点数。在大多数系统中深度缓冲区为 24 位。
当深度测试启用的时候, OpenGL 测试深度缓冲区内的深度值。OpenGL 执行深度测试的时候,如果此测试通过,深度缓冲内的值将被设为新的深度值。如果深度测试失败,则丢弃该片段。
现在大多数 GPU 都支持一种称为提前深度测试(Early depth testing)的硬件功能。提前深度测试允许深度测试在片段着色器之前运行。明确一个片段永远不会可见的(它是其它物体的后面) 我们可以更早地放弃该片段。
⑤ Alpha Blending 则是一种中庸的方式,它使用当前 fragment 的 alpha 作为混合因子,来混合之前写入到缓存中颜色值。但 Alpha Blending 麻烦的一点就是它需要关闭ZWrite,并且要十分小心物体的渲染顺序。如果不关闭 ZWrite,那么在进行深度检测的时候,它背后的物体本来是可以透过它被我们看到的,但由于深度检测时大于它的深度就被剔除了,从而我们就看不到它后面的物体了。因此,我们需要保证物体的渲染顺序是从后往前,并且关闭该半透明对象的 ZWrite。
⑥ Fog(雾),是根据绘制像素的深度值,调整预先设定的雾的混合颜色来进行处理。越远的地方的像素颜色越接近白色,用作表现场景深处可以看到朦胧感的空气。Fog,深度值越远就有越模糊的像素值的空气远近表现法。
⑦ OpenGL 允许检查所有正面朝向(Front facing)观察者的面,并渲染它们,而丢弃所有背面朝向(Back facing)的面,这样就节约了我们很多片段着色器的命令(它们很昂贵!)。我们必须告诉 OpenGL 我们使用的哪个面是正面,哪个面是反面。OpenGL使用一种聪明的手段解决这个问题——分析顶点数据的连接顺序(Winding order)。
5、 帧缓冲:写入颜色值的颜色缓冲,用于写入深度信息的深度缓冲,以及允许我们基于一些条件丢弃指定片段的模板缓冲。把这几种缓冲结合起来叫做帧缓冲(Framebuffer),它被储存于内存中。OpenGL 给了我们自己定义帧缓冲的自由,我们可以选择性的定义自己的颜色缓冲、深度和模板缓冲。
6、 常见后期处理:
① 反相:我们已经取得了渲染输出的每个颜色,所以在片段着色器里返回这些颜色的
反色(Inversion)并不难。
② 灰度:移除所有除了黑白灰以外的颜色作用,是整个图像成为黑白的。
③ 模糊等。
7、 显示:整个帧缓存对应一帧图像。
可编程管线,顾名思义,就是说管线中的某些环节是可以被控制的。人们可以通过对GPU 中的着色器进行编程的方式,来控制、管理加速卡的渲染效果。人们对着色器进行自定义编程时,这个流水线就叫做可编程管线。同时,还提供默认的着色器程序,当游戏或应用程序完全使用默认着色器程序时,这个流水线就叫做固定管线。
Shader 在哪里?
1、顶点着色器处理每个顶点,将顶点的空间位置投影在屏幕上,即计算顶点的二维坐标。同时,它也负责顶点的深度缓冲(Z-Buffer)的计算。顶点着色器可以掌控顶点的位置、颜色和纹理坐标等属性,但无法生成新的顶点。顶点着色器的输出传递到流水线的下一步。如果有之后定义了几何着色器,则几何着色器会处理顶点着色器的输出数据,否则,光栅化器继续流水线任务。
2、几何着色器可以从多边形网格中增删顶点。它能够执行对 cpu 来说过于繁重的生成几何结构和增加模型细节的工作。Direct3D 版本 10 增加了支持几何着色器的 API,成为Shader Model 4.0 的组成部分。OpenGL 只可通过它的一个插件来使用几何着色器,但极有可能在 3.1 版本中该功能将会归并。几何着色器的输出连接光栅化器的输入。
3、像素着色器(Direct3D),常常又称为片断着色器(OpenGL),处理来自光栅化器的数据。光栅化器已经将多边形填满并通过流水线传送至像素着色器,后者逐像素计算颜色。像素着色器常用来处理场景光照和与之相关的效果,如凸凹纹理映射和调色。名称片断着色器似乎更为准确,因为对于着色器的调用和屏幕上像素的显示并非一一对应。举个例子,对于一个像素,片断着色器可能会被调用若干次来决定它最终的颜色,那些被遮挡的物体也会被计算,直到最后的深度缓冲才将各物体前后排序。
Vertex Shader 和 Pixel Shader 在不同的文档里面有不同的叫法,Nvidia 在自己的 OpenGL扩展中把 Vertex Shader 叫做 Vertex Program、把 Pixel Shader 叫做 Texture Shader,3Dlabs 在自己提出一份 OpenGL 2.0 的提议里面把这两者分别叫做 Vertex Shader 和 Fragment(片元)Shader。
统一着色器模型将上述三种着色器统一起来,发布于 OpenGL 和 Direct3D 10 里面。
原文链接:https://www.f2er.com/javaschema/283285.html