《Unity Shader入门精要》学习笔记(一)渲染流水线

前言

最近在看《Unity Shader入门精要》这本shader的必修之书,想要分享一下自己的笔记,于是就有了这个博客。
此篇博客涉及到的是原书的第二章渲染流水线的内容。要想了解Shader到底是个啥玩意,是需要了解这方面的内容的。

渲染流水线

应用阶段→几何阶段→光栅化阶段

应用阶段

应用阶段由开发者主导,由CPU负责实现。

应用阶段的任务

1.准备好场景数据(摄像机、视锥体、三维模型、光照……)
2.剔除(culling)不可见物体
3.设置每个模型的渲染状态(材质、纹理、shader……)

应用阶段输出渲染图元,是渲染所需的几何信息,包括点、线、三角面……

几何阶段

几何阶段处理和要绘制的几何图形相关的事务,在GPU运行。
几何阶段将顶点坐标变换到屏幕空间,交由光栅器处理。
几何阶段输出屏幕空间的顶点坐标,和对应的深度、颜色等信息

光栅化阶段

光栅化阶段在屏幕上绘制所有像素,在GPU上进行。
光栅化阶段对几何阶段提供的信息进行逐个顶点插值等处理。

上述的渲染流水线是抽象的概念流水线,GPU流水线才是实现渲染流水线的实际工序。

CPU在应用阶段的工作

分为将数据存入显存设置渲染状态调用Draw Call三步。

将数据存入显存

渲染所需的数据需要从硬盘到内存,从内存到显存传递。
原则上加载到GPU之后这些数据就可以删除了。但若是CPU还需要这些数据(例如做碰撞检测),内存里的这些数据就不能删。因为数据从硬盘到内存比较耗时,删除了之后再写入内存会需要不少时间。

设置渲染状态

渲染状态决定了Mesh被如何渲染。
包括使用的顶点着色器(Vertex Shader)片元着色器(Fragment Shader)、光源属性、材质等等。

调用Draw Call

Draw Call是CPU向GPU发送的命令。这个命令指向一个需要被渲染的图元列表。由于这些东西已经事先存入显存了,对于GPU来讲,CPU告诉GPU要渲染哪个图元,GPU直接从显存里拿出来按照渲染状态渲染就行了。
而GPU在接收到指令之后就会开始渲染了。这里的渲染用的就是GPU流水线。

Draw Call的性能问题

Draw Call过程中造成性能问题的是CPU。
CPU和GPU之间也需要流水线,否则这二位根本无法同时工作。
为了让二位同时工作,需要使用命令缓冲区
命令缓冲区是一个命令的队列,CPU往里面加命令,GPU从里面读命令。这两个过程互相独立。
Draw Call是诸多命令的一种。
Draw Call需要CPU进行大量的准备工作,发送大量数据给GPU之后才会发出指令。
这样就容易出现CPU忙碌而GPU空闲的情况。
所以要尽量减少Draw Call

减少Draw Call的方法:批处理

将小的若干个Draw Call合成一个大的。
批处理适合处理静态的模型,合并一次即可。
动态物体也可以批处理,但会很慢。
批处理相当于把多个网格混成一个网格。它们的渲染状态是同一个
为了减少Draw Call的开销,要避免使用大量很小的网格(一定要用的话要考虑合并),尽量让不同的网格共用一个材质。

GPU流水线

渲染流水线中的应用阶段已经由CPU做完了,剩下的几何阶段和光栅化阶段都是GPU流水线的工作。

几何阶段细分

顶点着色器→曲面细分着色器→几何着色器→裁剪→屏幕映射→进入光栅化阶段

顶点着色器

完全可编程,用于实现顶点的空间变换和顶点着色等等。
数据来自CPU。处理单位为顶点。
顶点着色器不能增删顶点,也不能知道顶点之间是否有联系(共线、共三角面之类的)。
这样顶点之间彼此独立,可以并行处理,速度很快。
主要任务是坐标变换和逐顶点光照。
还会输出后续阶段需要的数据。
顶点着色器是可以进行顶点的坐标变换的,用来实现水面、布料等等的效果。
顶点着色器需要将顶点的坐标从三维空间转换成齐次裁剪空间。
随后硬件进行透视除法,得到归一化的坐标。

曲面细分着色器

用于细分图元

几何着色器

可以进行逐个图元的着色,也可以产生更多图元。

裁剪

将不在摄像机范围内的点裁剪掉,并剔除某些三角图元的面片。
图元和摄像机的关系:在视野内、部分在视野内、在视野外
视野内的就直接往后传,视野外的就不传
部分在视野内的需要裁剪。
用一个视野边缘的顶点代替在视野外的顶点。
裁剪不可编程,但可以自定义一个裁剪操作来进行配置。

屏幕映射

不可配置和编程,将图元坐标转换到屏幕坐标系。
屏幕坐标系和z坐标构成了窗口坐标系。
OpenGL原点在左下角,DirectX是左上角。

光栅化阶段细分

三角形设置→三角形遍历→片元着色器→逐片元操作→生成屏幕图像

三角形设置和三角形遍历是固定函数的阶段。

三角形设置

计算三角形每条边的像素坐标

三角形遍历

检查像素点是否被三角形网格覆盖。
使用顶点数据对整个网格中的所有像素插值,计算网格中每个像素的深度等数据。
起初输入的只有三个顶点的深度等数据,处理之后,所有网格中的像素的深度等数据都被计算出来了。
得到片元序列。
一个片元除了包含一个像素之外,还包含了很多信息。包括屏幕坐标、深度、法线方向、纹理坐标等等

片元着色器

可编程,实现逐片元的着色操作。
根据片元的各种信息,进行上色。
根据纹理坐标覆盖纹理。
片元着色器很强大,可以实现大多数的效果,但只能影响单个片元,不能将信息传递给其它片元。

逐片元操作

不可编程,但可以配置,执行很多重要操作。
1.决定每个片元的可见性。需要通过深度测试模板测试等等,确定每个片元是否可见。
2.对于通过测试的片元,将片元的颜色值和颜色缓冲区的颜色混合。
逐片元操作高度可配置。
常规工作流程:片元→模板测试→深度测试→混合→写入颜色缓冲区

模板测试

与之相关的是模板缓冲。
一旦模板测试开启,GPU会读取模板缓冲区中这个片元位置的模板值,将这个值和参考值比较。比较的函数可以由开发者指定。
无论模板测试是否通过,都可以根据测试结果修改模板缓冲区。修改操作也由开发者指定。
模板测试用于限制渲染区域。
也有一些诸如渲染阴影、渲染轮廓的高级用法。

深度测试

对通过模板测试之后的片元进行的测试。
将片元深度值和深度缓冲区的对应深度值比较。
虽然比较函数也可以由开发者指定,不过一般都是小于等于。毕竟正常的摄像机是不能看到被遮挡的东西的。
只有通过了深度测试,它的深度值才能写入深度缓冲区。至于这个深度写入是否进行,由开发者决定。
透明效果和深度测试和深度写入关系密切。

混合

对于不透明物体,可以直接关闭,直接覆盖颜色缓冲区即可。
对于半透明物体,需要将片元颜色和颜色缓冲区的颜色进行混合,混合出看上去半透明的颜色。
片元颜色和颜色缓冲区的颜色的混合需要通过一个混合函数进行。
与PS的图层混合极其相似,有相加相减相乘等等一系列方法。

关于提前测试

大部分GPU会在片元着色器之前就进行这些测试。
毕竟算了半天的片元,要是到头来根本不渲染,岂不是白费力气?
Unity的渲染流水线中,深度测试就在片元着色器之前。
但提前测试的话,测试结果可能和片元着色器的部分操作冲突。
现代的GPU一旦发现片元着色器中的内容和提前测试冲突,就会关闭对应的提前测试。这样会造成性能下降。

关于双重缓冲

GPU为了不让用户看见光栅化还没结束的图元,会使用双重缓冲。
场景渲染在后置缓冲区中,而前置缓冲区中的是当前屏幕显示的内容。

OpenGL和DirectX

应用程序将指令通过OpenGL或者DirectX发送给显卡驱动,由显卡驱动告知显卡如何操作。

Shader语言

常见shader语言:DirectX的HLSL、OpenGL的GLSL、Nvidia的CG
这些语言没有C#那么高级,但也比汇编高级,也就是所谓的“中间语言”。
GLSL跨平台。但这个跨平台是通过把编译器写在显卡驱动里实现的。
HLSL只能在微软的平台使用。
CG是真正的跨平台。和HLSL很像,甚至可以无缝移植。但无法发挥OpenGL的最新特性。
注:Nvidia已经停止更新CG了。现在Unity Shaderlab使用的是HLSL语言。

所以Shader到底是个啥

在GPU的可高度编程的阶段,Shader编译出来的代码可以在GPU上运行
包含顶点着色器、片元着色器等等
利用shader可以控制渲染流水线中的渲染细节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值