
·本专栏文章记录笔者阅读学习《Unity Shader入门精要》的感想与笔记,方便日后复习与查找
以下为渲染流水线简化后的版本与知识
一、什么是渲染流水线
渲染流水线:简单来说就是从一个三维场景出发,生成(渲染)成屏幕前的一张2D图片的过程
渲染流水线的好处:可以提高渲染的效率,使得渲染复杂场景变得可能
二、渲染流水线的三个概念阶段
1.应用阶段 (Application Stage)
目的:
准备输出好渲染需要的几何信息,也就是渲染图元(点、线、面)等
任务:
①准备场景数据
②剔除不可见的物体
③设置每一个集合体的渲染设置
输出:
渲染图元
2.几何阶段(Geometr Stage)
目的:
决定要绘制的图元是什么,在哪里绘制,以及怎么样绘制。
任务:
①处理输入的渲染图元
②将坐标变换到屏幕空间中
输出:
屏幕空间的二维坐标、每个顶点对应的深度值、着色等信息
3.光栅化阶段(Rasterizer Stage)
目的:
在屏幕上对像素上色,渲染出最终图像
任务:
①对上一阶段得到的注定点数据进行插值,然后再进行逐像素处理
①决定每个渲染图元中那些像素应该被绘制在屏幕上
注意:以上流水线为概念流水线,是为了给一个渲染流程进行基础的功能划分而提出的
三、渲染流水线的起点
渲染流水线的起点是CPU,可以分为三个阶段
1.把数据载入显存
数据转移流:硬盘(HDD)→系统内存(RAM)→显存(VRAM)
之所以这样存,是因为GPU对RAM大多没有访问权限,但是对VRAM的访问速度更快
通常载入的数据:顶点的位置信息、法线信息、顶点颜色、纹理坐标
2.设置渲染状态
什么是渲染状态:即定义场景中的网格是怎么样被渲染的。
可以设置的状态:使用哪个片元/顶点着色器、光源属性、材质等
3.调用Draw Call
DrawCall是什么:是一个由CPU对GPU发起的指向一个需要被渲染的图元列表的命令(其中并不包含任何材质信息,因为在上一步已经设置过了)
DrawCall的作用:让GPU根据渲染状态(材质、纹理、着色器)和所有输入的顶点数据进行计算,最终输出成屏幕上的像素。这个过程就是【GPU流水线】
四、GPU流水线
·GPU流水线就是DrawCall命令的具体执行操作过程了,它包含了概念阶段的【几何阶段】和【光栅化阶段】,其输入的顶点数据是由【应用阶段】载入显存中并由DrawCall指定的

绿色:可以完全控制变成;黄色:可配置但不可编程;蓝色:GPU固定完成,开发者无权限
1.几何阶段
1.1顶点着色器
目的:将顶点进行一定的计算处理并输出这些数据给下一个阶段
①坐标变换:
将顶点坐标从模型空间转换到齐次裁剪空间中(这一步可以改变顶点的位置,可以做顶点动画用于),之后再由硬件透视除法,之后归一化为【设备坐标 NDC】
注:设备坐标的Z分类在OpenGL和Unity中都是【-1,1】之间,但是DirectX中则是【0,1】之间
②逐顶点光照
③输出后续阶段所需的数据
常见是光栅化后输出给片元着色器,但是现代的Shader Model中还可以输出给曲面细分着色器或几何着色器
1.2.裁剪
目的:为了只处理会出现在摄像机中的物体
图元与摄像机视野的关系:
①图元完全在视野内
继续传递给下一个流水线阶段
②图元完全不在视野内
不传递给下一个流水线阶段
③图元部分在视野内
用图像和视野边界的交界处,生成一个新的顶点来代替在视野外的顶点

比如黄色是视野范围,这个线有一个点在视野外,那就只好在这个视野边界生成一个新的绿色顶点来作为替代的
总之,它只是决定传入的图元数据有没有必要继续往下传。
嗯,不过,为什么不先裁剪然后再进行顶点着色呢?可能是场景中有些信息在顶点着色计算的时候还是需要的,所以不能先裁剪的
1.3.屏幕映射
目的:将传入的三维坐标,转化为【屏幕坐标系】下的二维坐标
注意:只会处理X,Y坐标,Z坐标并不处理。【屏幕坐标系】和Z坐标合起来成为【窗口坐标系】
总之,在几何阶段我们输出的是经过一系列变换和处理后的顶点数据以及图元(Primitive)连接信息
2.光栅化阶段
2.1.三角形设置
目的:根据传入的屏幕坐标系下的顶点信息和额外信息来计算图元的覆盖顺序与颜色
任务:
①计算三角形每条边上的像素坐标(需要得到三角形的边界表示方式(即几何阶段输出的图元连接信息))
说人话:把点连成线

2.2.三角形遍历
目的:生成相应的片元数据,并输出片元序列
任务:
①用传入的数据判断三角网格覆盖了哪些像素
②其对整个覆盖区域的像素进行插值
注意:此时还只是片元,而并非像素,是包含了很多状态的集合(顶点信息、屏幕坐标、深度信息等)

说人话:把线框填充成面
2.3.片元着色器
目的:根据输入的片元序列,生成一个或者多个颜色值
任务:
①根据输入的信息计算相应的颜色值(可以看成是对片元进行着色)
注意:
①此时只是得到了颜色的信息数据,但是还没有实际真正地渲染到像素上
②片元着色器这一步可以进行纹理采样,因为它这一步已经包含了许多信息(这个片元上对应的纹理已经在上一步对顶点纹理坐标的插值运算得到)
说人话:算一下这个面上的像素的颜色值
2.4.逐片元操作
·总算到最后一步了,能不能在屏幕上出现就在此一举了啊
目的:将已经计算好的片元中数据合并进入颜色缓冲区中
任务:
①对片元进行测试工作:【深度测试】【模板测试】等
②把通过测试的片元的颜色值和意见存储在颜色缓冲中的颜色进行合并、混合
说人话:决定片元着色器中算出的颜色值,能不能进入颜色缓冲区中
五、逐片元操作中的测试与混合
1.测试
1.1.模板测试
作用:用于限制需要渲染的区域
应用:
①渲染物体的轮廓,通过规定只渲染这个物体的轮廓的像素而其他像素丢弃不渲染
②渲染阴影
③平面反射
④限制渲染区域
测试的过程:
①读取这个模板缓冲区中片元所在位置的模板值(S)
②用预设的模板参考值(Ref)
③决定片元命运与决定是否更新缓冲区:
如果测试结果为 false (失败),则立即丢弃该片元(不再进行深度测试和写入帧缓冲区颜色)。
如果测试结果为 true (通过),则该片元继续进行深度测试(如果启用)。
④(可选)更新模板缓冲区值:根据第3步的测试结果
(失败 / 通过但深度测试失败 / 通过且深度测试通过)
以及开发者通过为每种情况预设的操作,决定是否更新以及如何更新该片元位置对应的模板缓冲值 S。
即使在测试失败丢弃片元后,也可能更新模板值
注意:①模板值是在模板缓冲区中的,片元上并没有携带模板值信息,所以丢弃片元和更新模板缓冲区的模板值这个操作并不冲突
1.2.深度测试
作用:根据深度值(Z轴值),来决定是否舍弃该片元,保证正确的前后渲染关系
测试的过程:
①读取片元的深度值(Z_new)
②读取深度缓冲区中该片元位置所储存的的深度值(Z_stored)
③根据开发者设置的比较函数,比较二者的值,得到比较的结果(True/False)
④决定片元命运与决定是否更新缓冲区:
·测试失败 (false):丢弃该片元。 (不再进行后续测试和写入)深度缓冲区值 Z_stored 保持不变。并且默认情况下舍弃深度值 Z_new 比 Z_stored 更大(即离相机更远)的片元
·测试通过 (true):片元可以尝试【合并】进入颜色缓冲区了(可能深度测试之后还有其他测试,但是这里我们不考虑了)。
如果深度写入开启 :用
Z_new的值覆盖更新该位置深度缓冲区的值Z_stored。
说人话:总之就是用新的值和旧的值打擂台,新的值赢了才有资格被呈现在像素点上。不同的测试就是不同擂台规则而已
2.混合/合并功能
作用:使得已经在颜色缓冲区中的颜色与新渲染得到的颜色按一定方法进行覆盖或者混合得到新的像素颜色
关闭混合功能:新的颜色会直接覆盖掉旧的颜色(这就没办法渲染透明的效果)
开启混合功能:先得到片元颜色值,再得到缓冲区中该位置颜色值,然后进行混合操作,得到新的颜色之后,再更新到颜色缓冲区中(就像PS里两个图层按不同的混合模式混合一样)
说人话:打胜所有擂台的新来的颜色值决定怎么处理旧的颜色值,是直接完全推翻(覆盖),还是互相合作(混合)
3.提前测试
读到这里我们这样发现,有些片元压根就没办法通过测试,但是之前还那么幸苦跟着又顶点着色又片元着色的,这很浪费性能。所以我们要是先测试剔除本来就不需要着色的片元的话,那不就可以节约很多性能了嘛?
即:在执行片元着色器之前就执行部分测试。提前进行深度测试的技术就叫做【Early-Z技术】

缺点:①有时候提前测试的结果可能回合片元着色器中一些操作相冲突(后续会具体解释这个)(但是现代GPU一般会先判断是否会产生冲突,产生就会禁用掉提前测试)
六、其他
1.OpenGL与DirectX
OpenGL和DirceX都是图像应用编程接口,作为上层应用和底层GPU沟通的桥梁

它和CPU,GPU的作用大概是这样子的
2.HLSL、GLSL、Cg是什么
HLSL:即High Level Shader Language,是DIrectX的着色器语言,是微软控制着色器编译,不同硬件编译得到的都是一样的,支持平台只是微软自家产品
GLSL:即OpenGL Shader Language,是OpenGL的着色器语言,由显卡来完成编译,这意味着它很容易跨平台
Cg:即C for Graphic,是NVIDIA的着色器语言,它会根据平台不同编译成相应的中间语言,也可以跨平台,切可以移植成HLSL语言。但是无法发挥OpenGL的最新特性
3.关于Draw Call
1.CPU与GPU的并行工作
我们指定CPU是负责对GPU发送DrawCall命令的,但并不需要等待GPU完成再发送吓一跳DrawCall命令,这是因为CPU的DrawCall命令都是发送给一个缓冲区中的,GPU也从这个缓冲区中取出DrawCall命令

大概是这么个意思
2DrawCall太多意味着什么
意味着CPU需要花费大量的时间在创建新的DrawCall命令上,如果需要创建的DrawCall过多就会导致CPU过载
3.减少DrawCall(批处理)
①CPU内存中合并网格(一般合并静态的就好了,因为它们只用合并一次)
②避免使用大量小网格,考虑合并很小的网格
③避免使用过多的材质
4.固定函数流水线(Fixed Function Pipeline)
就是只提供给开发者一些配置方法的渲染流水线,完全不可以编程,一般是比较旧的渲染管线。
现在慢慢淘汰了,看得见的也基本上都是用可编程渲染管线模仿的固定函数渲染管线
七、总结
①Shader就是GPU流水线上高度可编程的阶段上的可运行代码,不同的Shader可以帮助我们实现不同的效果,同时有一些固定的Shader比如【顶点着色器】【片元着色器】,而要如何编写这些着色器就要运用到着色器语言(Shader Language)
②GPU流水线是渲染管线中的几何阶段与光栅化阶段,几何阶段的目的是处理好顶点的数据,光栅化阶段是利用几何阶段的顶点数据生成相应的片元数据并决定是否加入颜色缓冲区中以在平面上显示出来
③CPU是渲染流水线的起点,负责应用阶段,负责把需要渲染的数据(顶点数据、状态设置(管线配置)和参数),渲染一个图元批次(Primitive Batch)

6万+

被折叠的 条评论
为什么被折叠?



