unity shader入门精要_Unity Shader入门——渲染流水线

本文详细介绍了 Unity 中渲染流水线的基本概念,包括 CPU 和 GPU 在渲染过程中的分工,渲染流水线的不同阶段及其具体任务,如应用阶段的数据加载与剔除、几何阶段的顶点处理与裁剪、光栅化阶段的三角形设置与片元着色等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言:最近一直在忙着搞Houdini与Unity的衔接工作,发现Unity Shader术语整理的文章,天天有人赞,看来还是Shader比较主流,且入门者居多,所以打算抽出点时间开个新坑,从最简单的入门讲起,方便跟我一样学习Shader遇到困难的人一起学习,也方便后续查找相关知识点。个人能力有限,如有错误,还请指正。

本人也是女神一书《Unity Shader入门精要》的受益者之一,所以也请大家多支持乐乐女神。

知乎用户​www.zhihu.com

后续的整体思路结构也基本顺从书中的顺序,以自己的理解方式加以整理,涉及到的知识点不是本人自创,都是前人的成果,只是自己顺手整理了一波,也不存在任何商用问题,所以若有雷同,还请勿喷。


渲染流水线,顾名思义地理解就是渲染的一整套流程,从虚拟数据的三维空间以摄像机的角度渲染当前帧的可见内容到屏幕2D的一张图片,游戏运行过程中,这个流水线一帧一帧的运行,不断地刷新屏幕,如果遇到卡顿,那就是CPU或者GPU哪里出了问题或者计算量太大导致卡顿,都很正常,所以开发游戏有30%的时间在做游戏优化。(30%这是数值只是约数,浮动很大的,毕竟游戏涉及面很广,不能以偏概全)

《Real-Time Rendering,Third Edition》一书中将渲染过程分成三个阶段:应用阶段(Application Stage),几何阶段(Geometry Stage),光栅化阶段(Rasterizer Stage)。这也是主流的说法,每个阶段内部又可以继续细分,详见下图

c2765dc805c3285a53fe10cfc796831f.png

通俗一点的整体来讲渲染流水线就是:

1.CPU负责整理什么需要绘制(准备数据+剔除工作),该怎么绘制(设置渲染状态)

2.CPU发送指令给GPU (DrawCall)

3.GPU乖乖地听CPU的话,加班干活(根据指令进行渲染)


应用阶段

游戏运行之后(本人游戏开发,所以多以Unity游戏开发为案例),将需要的数据从电脑硬盘(HDD)加载到内存(RAM)中,这是普通程序都会有的操作,但游戏需要渲染,这就需要GPU的工作,所以为了访问速度的提升,需要把网格纹理等数据加载到显存中(VRAM)。三者之间的互传速率是大相径庭的,以前看到有图例说明的,一时半会找不到了(论自己整理的重要性)。当前的一帧,以60FPS为例,即一秒渲染60次,一整套渲染流程花费的时间约在1/60 ≈ 0.01667秒≈16.7ms

c70384cc9c154838b78ced39146153cf.png
99.3FPS 10.1ms

此阶段的主要由CPU负责实现,主要任务有:

1.准备好场景所需的数据,如该场景用到了哪些模型,需要什么材质,贴图,以及各个对象的各个属性参数等等。

2.剔除工作,为了提升性能,Unity底层会做一个剔除工作,把那些摄像机理论上看不到的被遮挡的物体,不需要做渲染,减少后续的工作量。

3.渲染状态的设置,这里就包含一些对模型的渲染状态的设置,如该模型使用什么类型的光照模型,光源属性,材质贴图等等


Batches,DrawCall,SetPass Call

CPU 收集每个即将被渲染的物体信息并把数据按命令分类,这就是DrawCall,drawcall内部为单个模型的顶点数据,以及其需要用到的材质贴图等数据。在某些条件符合的情况下(如材质相同,渲染状态相同),多个drawcall会被合并为一个drawcall,这就是Batches,批处理。Unity现在Game窗口下的Statistics已经不再显示DrawCall的数量,相对应的改成了显示Batches的数量,更直观一些。批处理有时会包含DrawCall以外的数据,这里不多做考虑。

对于每个包含DrawCall的批处理来讲,CPU可以向GPU发送一条命令来设置统称为渲染状态的变量,这个命令就是SetPass Call,这个命令告诉GPU下一个Mesh需要使用哪个渲染状态去执行渲染,只有当渲染状态需要改变的时候,即下一个与上一个渲染状态不一致时,才会发送SetPass Call。若无改变,GPU则使用最近的一次渲染状态进行下一个网格的渲染。

在某种情况下,批处理可能有多个 pass。pass 是 shader 代码的一部分,一个新的 pass 需要渲染状态的改变。对于批处理中的每个 pass, CPU 必须先发一个新的 SetPass call 然后再发 draw call。

渲染一帧,CPU 和 GPU 必须完成它们所有的任务。如果它们当中的任何一个任务完成时间太长,将会造成渲染延迟。Batches与DrawCall的值是比较接近的,SetPass Call相对少一点。是否将多个DrawCall合并为一个Batches是有Unity 的Dynamic Batch绝对的,当然还有Static Batch。


几何阶段

几何阶段就进入了GPU的专场了。后续的可以理解为GPU的流水线,又可以继续细分多个步骤。

1.顶点着色器

这个阶段主要处理顶点,将顶点坐标转换到屏幕坐标。如图所示,每个顶点都会调用一次顶点着色器,不得创建或销毁顶点,也无法获得顶点之间的相互关系。这个阶段的主要工作是将顶点的坐标从模型空间转化到齐次裁剪空间。逐顶点光照也在这里进行计算。

o.pos= UnityObjectToClipPos(v.vertex); 新的API

o.pos= mul(UNITY_MVP,v.vertex); 旧版本的写法

2.裁剪

摄像机本身有个视锥体,代表摄像机的可见范围,超出这部分区域的不做渲染, 大部分的不可见模型,CPU已经提前做了剔除处理,剩下网格的三角面,与视锥体的关系有三种情况:

  • 三角面完全包含在视锥体内,继续做后续的渲染处理
  • 三角面完全在视锥体外,无交集,则不进入后续的渲染处理
  • 三角面与视锥体有交叉穿模,则在交叉处产生新的顶点,裁剪掉多余的处于视锥体范围外的图元,进入后续渲染处理

3.屏幕映射

将裁剪空间的三维坐标转化为屏幕空间的二维坐标,Z值不做处理,输入的坐标,x,y的范围都在[-1,1],而屏幕是根据分辨率的属性大小不一,以屏幕1920*1080为例,需要将原本正负一范围内的坐标值重映射到0-1920和0-1080,屏幕中央,原本的裁剪空间的(0,0,0)匹配的应该是屏幕的(960,540,0)

d2d0aa73a8ec5f4de634a33364dd38f1.png

另外需要注意的就是OpenGL和DirectX的区别。OpenGL以左下角为原点,右上为最大;DirectX以左上角为原点,右下为最大。我们的阅读习惯也是从左到右,从上到下,图像文件的存储用大部分以这样的格式。


光栅化阶段

4.三角形设置(Triangle Setup)

由于上一个阶段输出的是一堆离散不相关的点的集合,此阶段就是将这部些顶点数据转化为一定的三角形边界的表达式,即将点连成三角面,方便后续的处理。

5.三角形遍历(Triangle Traversal)

此阶段会遍历屏幕的每一个像素,检测其是否被当前三角形所覆盖,所被覆盖,则产生一个片元。三角形内部的片元的深度值可以通过三个顶点的深度值lerp计算得到。

此阶段产生的一系列片元是以单个像素为单位,但又比像素多了一些额外数据,如深度值,法线,纹理坐标等等数据。

6.片元着色器(Fragment Shader)

这里和顶点着色器一样,是Shader可编程的重要部分。片元着色器的主要任务就是逐片元处理,计算出该片元的最终颜色,但片元计算出的颜色不等于像素的最终颜色,还要进入下一个阶段进一步处理。此阶段的纹理采样技术是与顶点着色器的坐标转换一样主要的任务。

7.逐片元操作

这一步就相对复杂一点了。

片元-->模板测试-->深度测试-->混合-->颜色缓冲区(明天再更新,累了)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值