Unity Shader学习------GPU渲染流水线详解

GPU流水线即渲染流水线中的几何阶段光栅化阶段

Unity Shader学习------GPU渲染流水线详解_第1张图片

绿色---完全可编程控制 黄色---可配置不可编程 蓝色---GPU固定实现

GPU渲染流水线接受顶点数据作为输入。这些数据是应用阶段加载到显存中,再由DC指定的

几何阶段:

顶点着色器(Vertex Shader)完全可编程:用于实现顶点的空间变换,顶点着色等功能

流水线第一阶段,输入来自于CPU。顶点着色器的处理单位是顶点,输入进来的每个顶点都会调用一次顶点着色器

顶点着色器本身不可以创建或销毁任何顶点,且无法得到顶点之间的关系(如是否属于同一个网格)

因为这样的相互独立性,GPU可以利用本身的特性并行化处理每一个顶点(速度更快)

主要工作:坐标变换和逐顶点光照。

坐标变换:把顶点坐标从模型空间转换到齐次裁剪空间,由硬件做透视除法后得到归一化的设备坐标(Normalized Device Coordinates)

应用:可以通过在这一阶段改变顶点位置来模拟水面、布料等---即顶点动画

-曲面细分着色器(Tessellation Shader)可选着色器:用于细分图元

-几何着色器(Geometry Shader)可选着色器:用于执行逐图元着色操作或产生更多的图元

裁剪(Clipping)可配置 将不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片

可以使用自定义的裁剪平面来配置裁剪区域,可以通过指令控制裁剪三角图元的正面还是背面

屏幕映射(Screen Mapping)不可配置和编程:把每个图元的坐标转换到屏幕坐标系中

这一步输入的坐标仍是三维坐标系下的坐标,此阶段的任务是把每个图元的x、y坐标转换到屏幕坐标系(Screen Coordinates)(二维坐标系)下

屏幕坐标系和z坐标一起构成了一个坐标系---窗口坐标系(Window Coordinates),这些值会被传递到光栅化阶段

光栅化阶段:

上一个阶段输出的信息是屏幕坐标系下的顶点位置以及它们相关的额外信息(如深度值(z坐标)、法线方向、视角方向等)

光栅化两个最重要的目标:计算每个图元覆盖了那些像素、为这些像素计算它们的颜色

三角形设置(Triangle Setup)GPU固定实现:计算光栅化一个三角形网格所需要的信息

上阶段输出的都是三角网格的顶点,我们得到的是三角网格每条边上的两个顶点

要得到整个三角网格对像素的覆盖情况---需计算每条边上的像素坐标

计算边界像素的坐标信息---需得到三角形边界的表示方式

这样计算一个三角网格表示数据的过程就叫三角形设置,其输出是为下阶段做准备

三角形遍历(Triangle Traveral)GPU固定实现:检查每个像素是否被一个三角网格所覆盖

如果被覆盖就会生成一个片元(fragment),找到哪些像素被三角网格覆盖的过程就是三角形遍历,也称为扫描变换(Scan Conversion)

 

(根据几何阶段的顶点信息得到三角网络覆盖的像素范围,对应像素生成一个片元,片元中的状态是对3个顶点的信息进行插值得到的)

这一步的输出就是一个片元序列

片元不是真正意义上的像素,是一个包含了很多状态的集合,这些状态用于计算每个像素的最终颜色,状态包括不限于(屏幕坐标,深度值,几何阶段输出的顶点信息、法线、纹理坐标等)

片元着色器(Fragment Shader)完全可编程:用于实现逐片元的着色操作

前面的光栅化阶段产生一系列的数据信息来表述一个三角网格是怎样覆盖每个像素的,每个片元负责存储这样的一系列数据

片元着色器得到的输入是上一个阶段对顶点信息插值得到的结果,输出的是一个或多个颜色值

这一阶段可以完成很多重要的渲染技术,如纹理采样。

为了在片元着色器中进行纹理采样,通常会在顶点着色器阶段输出每个顶点对应的纹理坐标,经过光栅化阶段对三角网格的3个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标了

局限性:仅可以影响单个片元,即不可以将自己的任何结果直接发送给它的邻居们。例外就是片元着色器可以访问到导数信息时

逐片元操作(Per-Fragment Operations) 在DX中称为:Output Merger 可配置:修改颜色、深度缓冲、进行混合等

主要任务:

(1)决定每个片元的可见性。要涉及很多测试工作,如模板测试、深度测试

(2)如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区的颜色进行合并(混合)

 

 

模板测试(Stencil Test):

与之相关的是模板缓冲(Stencil Buffer),和颜色缓冲、深度缓冲几乎是一类东西

开启模板测试后,GPU首先读取模板缓冲区中该片元位置的模板值(使用读取掩码),然后将该值和读取(使用读取掩码)到的参考值(reference value)进行比较

比较函数可由开发者指定,如片元没有通过这个测试就会被舍弃

模板测试通常用于限制渲染的区域。高级用法还有渲染阴影轮廓渲染

(不管片元是否通过了模板测试,都可以根据模板测试和深度测试的结果来修改模板缓冲区---由开发者指定。开发者可以设置不同结果下的修改操作

如:失败时报纸模板缓冲区不变,通过时将模板缓冲区对应位置的值加1等。)

 

深度测试(Depth Test):

开启深度测试后,GPUI会把该片元的深度值和已经存在于深度缓冲区的深度值进行比较,比较函数可由开发者指定

我们总想只显示出;离摄像机最近的物体,而被其它物体遮挡的就不需要出现在屏幕上。片元没有通过测试就会被舍弃

与模板测试不同的是,片元没有通过深度测试就无权更改缓冲区中的值,这是通过开启/关闭深度写入来做到的

透明效果和深度测试以及深度写入的关系十分密切

 

合并(混合):一个片元通过了上面所有的测试就会来到合并功能的前面

为什么要合并?

渲染过程是一个物体接着一个物体画到屏幕上的,而每个像素的颜色信息都被存储在一个名为颜色缓冲的地方,因此当我们执行这次渲染时,颜色缓冲中往往已经有了上次渲染之后的颜色结果,此时我们是使用这次渲染得到的颜色完全覆盖之前的结果还是进行其他的处理,这就是合并要解决的问题

对于不透明的物体,可以关闭混合(Blend)操作,这样片元着色器计算的颜色值就会覆盖掉颜色缓冲区中的像素值。但半透明效果就需要使用混合操作来实现

 

 

 从逻辑上讲测试是在片元着色器之后进行的,但对于大多数GPU来说,它们会尽可能在执行片元着色器之前就进行这些测试,想象一下GPU在片元着色阶段花了较大成本计算出片元的颜色后,却发现片元没有通过测试,即这个片元被舍弃了,那么之前的计算成本就浪费了

Unity给出的渲染流水线中,深度测试是在片元着色器之前的。这种将深度测试提前的技术被称为Early-Z技术

测试提前,其检验结果可能会与片元着色器中的一些操作冲突。

如:在片元着色器中进行了透明度测试,而这个片元没有通过透明度测试,我们会在着色器中调用API(如clip函数)来手动将其舍弃掉。这就导致GPU无法提前执行各种测试

(当片元着色器中的操作和我们要进行的深度测试发生冲突的时候就不允许你再进行提前测试,即GPU的执行逻辑)

因此现代GPU会判断片元着色器中的操作是否和提前测试发生冲突,存在冲突就会禁用提前测试,这样也会导致性能下降,因为有更多的片元需要被处理---透明度测试导致性能下降的原因

 

当模型的图元经过层层的计算和测试后就会显示到屏幕上,屏幕显示的就是颜色缓冲区的颜色值

为了避免我们看到正在进行光栅化的图元,GPU使用双重缓冲(Double Buffering)的策略。

意味着对场景的渲染都是在幕后发生的,即在后置缓冲中(Back Buffer)

一旦场景被渲染到了后置缓冲中,GPU就会交换后置缓冲区和前置缓冲区(Front Buffer)中的内容,而前置缓冲区是之前显示在屏幕上的图像,保证了我们看到的图像总是连续的

 

你可能感兴趣的:(UnityShader)