微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

Shader Learing(Render Pipeline篇)

Shader Learing(Render Pipeline篇)

硬件相关:

传统的 GPU 使用固定功能的管道(pipelines)实现图形算法,现代的 GPU 则是具有高度灵活性的可编程设备,使用 SIMD 处理器并行处理大量数据,从而实现图形性能。(SIMD:单指令多数据流,能够复制多个操作数,并把它们打包在大型寄存器的一组指令集)。
GPUvscpucpu 大部分面积为控制器和寄存器,与之相比,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 里面。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


迭代器模式(Iterator)迭代器模式(Iterator)[Cursor]意图:提供一种方法顺序访问一个聚合对象中的每个元素,而又不想暴露该对象的内部表示。应用:STL标准库迭代器实现、Java集合类型迭代器等模式结构:心得:迭代器模式的目的是在不获知集合对象内部细节的同时能对集合元素进行遍历操作
高性能IO模型浅析服务器端编程经常需要构造高性能的IO模型,常见的IO模型有四种:(1)同步阻塞IO(BlockingIO):即传统的IO模型。(2)同步非阻塞IO(Non-blockingIO):默认创建的socket都是阻塞的,非阻塞IO要求socket被设置为NONBLOCK。注意这里所说的N
策略模式(Strategy)策略模式(Strategy)[Policy]意图:定义一系列算法,把他们封装起来,并且使他们可以相互替换,使算法可以独立于使用它的客户而变化。应用:排序的比较方法、封装针对类的不同的算法、消除条件判断、寄存器分配算法等。模式结构:心得:对对象(Context)的处理操作可
访问者模式(Visitor)访问者模式(Visitor)意图:表示一个作用于某对象结构中的各元素的操作,它使你在不改变各元素的类的前提下定义作用于这些元素的新操作。应用:作用于编译器语法树的语义分析算法。模式结构:心得:访问者模式是要解决对对象添加新的操作和功能时候,如何尽可能不修改对象的类的一种方
命令模式(Command)命令模式(Command)[Action/Transaction]意图:将一个请求封装为一个对象,从而可用不同的请求对客户参数化。对请求排队或记录请求日志,以及支持可撤消的操作。应用:用户操作日志、撤销恢复操作。模式结构:心得:命令对象的抽象接口(Command)提供的两个
生成器模式(Builder)生成器模式(Builder)意图:将一个对象的构建和它的表示分离,使得同样的构建过程可以创建不同的表示。 应用:编译器词法分析器指导生成抽象语法树、构造迷宫等。模式结构:心得:和工厂模式不同的是,Builder模式需要详细的指导产品的生产。指导者(Director)使用C
设计模式学习心得《设计模式:可复用面向对象软件的基础》一书以更贴近读者思维的角度描述了GOF的23个设计模式。按照书中介绍的每个设计模式的内容,结合网上搜集的资料,我将对设计模式的学习心得总结出来。网络上关于设计模式的资料和文章汗牛充栋,有些文章对设计模式介绍生动形象。但是我相信“一千个读者,一千个
工厂方法模式(Factory Method)工厂方法模式(Factory Method)[Virtual Constructor]意图:定义一个用于创建对象的接口,让子类决定实例化哪一个类,使一个类的实力化延迟到子类。应用:多文档应用管理不同类型的文档。模式结构:心得:面对同一继承体系(Produc
单例模式(Singleton)单例模式(Singleton)意图:保证一个类只有一个实例,并提供一个访问它的全局访问点。应用:Session或者控件的唯一示例等。模式结构:心得:单例模式应该是设计模式中最简单的结构了,它的目的很简单,就是保证自身的实例只有一份。实现这种目的的方式有很多,在Java中
装饰者模式(Decorator)装饰者模式(Decorator)[Wrapper]意图:动态的给一个对象添加一些额外的职责,就增加功能来说,比生成子类更为灵活。应用:给GUI组件添加功能等。模式结构:心得:装饰器(Decorator)和被装饰的对象(ConcreteComponent)拥有统一的接口