找回密码
 立即注册
查看: 302|回复: 0

基础且直白的Unity渲染-支持多个光源的shader。

[复制链接]
发表于 2022-4-28 17:18 | 显示全部楼层 |阅读模式
多光照的shader是区别于基础shader的一种复合型,其shader中会包含多个PASS处理。基本上一个每一个pointlight或spotlight都会调用一次专门用来处理多光源的pass。因此计算量是成倍计算。但是相对的。可以对这个pass进行编译,让它区分于默认的shader光照做出更多有趣的变化,unity默认的standradshader便是官方提供的完善的多光源的shader。制作中可以多参照官方提供的效果再加以组合和拆解。
Unity渲染路径:

unity中,渲染路径决定光照如何应用到shader中。
unity默认支持4种渲染路径,其中有两种是旧版本使用,常规使用的是“向前渲染”ForwardBase,“延迟渲染”Deferred.默认会使用Forwardbase.
PASS的标签中可以指定的渲染路径:

Always:不区分渲染路径,该PASS持续被渲染,且不计算任何光照。
ForwardBase: 向前渲染,该Pass会计算环境光,主要平行光,逐顶点或者SH光源和Lightmaps。
ForwardAdd: 向前渲染,该Pass会计算额外的逐像素光源,每一个Pass对应一个光源。
Deferred:延迟渲染,该Pass会计算G缓冲(G-buffer)
ShadowCaster: 将物体的深度信息渲染到阴影映射纹理(Shadowmap)或一张深度纹理中。
PrepassBase: 用于遗留的延迟渲染。该Pass会渲染法线和高光反射部分的指数部分。
PrepassFinal: 用于遗留的延迟渲染。该Pass通过合并纹理,光照和自发光来渲染得到最后的颜色。
Verter,VertexMRGBM,VertexLM: 用于遗留的顶点照明渲染。

Unity中的向前渲染:
在Unity中,前向渲染有3种处理光照的方式:逐顶点处理、逐像素处理、球谐函数处理(SH)。决定一个光源使用哪种处理模式取决于它的类型和渲染模式。光源类型指的是该光源是平行光还是其他类型的光源,而光源的渲染模式指的是该光源是否是重要的。

在前向渲染种,当我们渲染一个物体时,Unity会根据场景中各个光源的设置以及这些光源对物体的影响程度(例如,距离该物体的远近、光源强度等)对这些光源进行一个重要度排序。其中,一定数目的光源会按照逐像素的方式处理,最多有4个光源按逐顶点的方式处理,剩下的光源可以按SH方式处理。

  • 场景中最亮的平行光总是按逐像素处理的。
  • 渲染模式被设置为Not Important的光源,会按照逐顶点或SH处理。
  • 渲染模式被设置为Important的光源,会按照逐像素处理。
  • 如果根据以上规则得到的逐像素光源数量小于Quality Setting中的逐像素光源数量,会有更多的光源以逐像素的方式进行渲染(默认是4)。
  • 首先,在渲染设置中,除了设置标签外,还使用了#pragma multi_compile_fwdbase的编译指令。这些编译指令会保证Unity可以为相应类型的Pass生成所有需要的Shader变种,这些变种会处理不同条件下的渲染逻辑,同时也会在背后声明相关的内置变量并传递到Shader中。通常情况下,只有分别为Base Pass和Additional Pass使用这两个编译指令,我们才可以在相关的Pass中得到一些正确的光照变量。
  • Base Pass中渲染的平行光默认是支持阴影的,而Additional Pass中渲染的光源在默认情况下是没有阴影的。我们可以在Addditional Pass中使用#pragma multi_compile_fwdadd_fullshadows指令来为点光源和聚光灯开启阴影效果。
  • 环境光和自发光在Base Pass中计算。因为我们希望这两种效果只计算一次。
  • 在Additional Pass的渲染设置中,我们还开启和设置了混合模式。我们希望每个Additional Pass可以与上一次的光照结果在帧缓冲中进行叠加,从而得到最终有多个光照的渲染效果。如果没有开启和设置混合模式,那么Additional Pass的渲染结果会覆盖掉之前的渲染结果。通常情况下我们选择的混合模式是Blend One One。
  • 对与前向渲染,一个Unity Shader通常会定义一个Base Pass(也可以定义多次,比如双面渲染)以及一个Additional Pass。一个Base Pass仅会执行一次,其它每个逐像素光源会执行一次Additional Pass。
Unity中的延迟渲染:

向前渲染的弊端:当场景中存在大量的实施光源时,向前渲染的性能会急速下降。
延迟渲染是一种比较旧的渲染方法,延迟渲染除了会使用颜色缓冲和深度缓冲外,还会利用额外的缓冲区,G缓冲。G缓冲会储存模型的表面信息,例如该表面的法线,位置,用于光照计算的材质属性等。
延迟渲染主要包含两个Pass,在第一个Pass中,不进行任何光照计算,而是仅仅计算哪些片元是可见的,利用深度缓冲。当一个片元可见时,便将相关信息存储在G缓冲中。第二个Pass中,利用G缓冲中的片元信息来进行光照计算。
延迟渲染使用的Pass数量就是通常是2个,与场景中的光源数无关。
Unity中的延迟渲染
延迟渲染路径中的每个光源都可以按逐像素的方式处理,但是,延迟渲染也有一些缺点:

  • 不支持MSAA。
  • 不能处理半透明物体。
  • 对显卡有要求,显卡必须支持MRT等。
使用延迟渲染时,Unity要求提供两个Pass。
(1)第一个Pass用于渲染G缓冲。在这个Pass中,会把物体的漫反射颜色、高光反射颜色、平滑度、法线、自发光和深度等信息渲染到屏幕空间的G缓冲中。对于每个物体,这个Pass仅会执行一次。
(2)第二个Pass用于计算真正的光照模型。这个Pass会使用上一个Pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲中。

Unity的光照衰减(以下的文字转载于 链接:https://www.jianshu.com/p/467c98c811a9,详细推导可以转进链接观看):

Unity在内部使用一张名为_LightTexture0的纹理来计算光照衰减。通常只关注对角线上的纹理颜色值,这些值表明了在光源空间中不同位置的点的衰减值。
为了对_LightTexture0纹理采样得到给定点到光源的衰减值,我们首先需要得到该点在光源空间中的位置,这里是通过_LightMatrix0变换矩阵得到的。_LightMatrix0可以将顶点从世界空间变换到光源空间。因此我们像这样获取光源空间中顶点的位置:
float3 lightCoord = mul(_LightMatrix0, float4(i.worldPosition, 1)).xyz;然后使用坐标模的平方对衰减纹理进行采样,得到衰减值:
fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;使用坐标模的平方进行采样是为了避免进行开方操作。然后使用UNITY_ATTEN_CHANNEL来得到衰减纹理中衰减值所在的分量,以得到最终的衰减值。
Unity的阴影:

在实时渲染中,尝试用阴影贴图技术(Shadew Map)来实现阴影。简单来说就是先把摄像机的位置放到与光源重合的位置,摄像机看不到的地方就是阴影区域。
在前向渲染路径中,如果场景中最重要的平行光开启了阴影,Unity就会为该光源计算它的阴影贴图。阴影贴图本质上时深度图,记录了从光源位置观察的能看到的最近的表面的深度信息。
Unity使用一个额外的Pass来专门更新光源的阴影贴图,这个Pass就是LightMode标签被设置为ShadowCaster的Pass,这个Pass的渲染目标时阴影贴图。Unit首先把摄像机放置到光源的位置上,然后调用该Pass,通过对顶点变换后得到光源空间的位置,并据此来输出深度信息到阴影贴图中。当开启了光源的阴影效果后,底层渲染引擎会在当前渲染物体的Unity Shader中找到LightMode为ShadwoCaster的Pass,如果没有,就在Fallback中寻找,若还是没有就不产生阴影。
在传统的阴影贴图的实现中,我们会在正常渲染的Pass中把顶点位置变换到光源空间下,以得到它在光源空间中的三维位置信息。然后我们使用xy分量对阴影贴图进行采样,得到阴影贴图中该位置的深度信息。如果该深度值小于该顶点的深度值,说明该顶点在阴影中。但对于支持MRT的显卡,Unity使用屏幕空间的阴影映射技术。
当使用屏幕空间的阴影映射技术时,Unity会通过调用LightMode为ShadwoCaster的Pass来得到可投射阴影的光源的阴影贴图以及摄像机的深度纹理。然后根据光源的阴影贴图和摄像机的深度纹理来得到屏幕空间的阴影图。如果摄像机的深度图中记录的表面深度大于转换到阴影贴图中的深度值,就说明该表面位于阴影中。由于阴影图在屏幕空间下,因此,我们首先需要将表面坐标从模型空间变换到屏幕空间,然后使用这个坐标对阴影图进行采样。


shader实例范例:

多光照的Diffuseshader:


单独一个方向光的性能效果,可以看到RenderForward.RenderLoopJob里只渲染了3次DrawMesh。


此处可以明显的看到增加到了9次。因为开启了两站pointlight。因为ForwardAdd的pass每添加一个灯光便会多渲染一次该PASS计算量自然也是成倍增加。但阴影的渲染数量未增加。因为ADD中并未写入shadows的计算。多光照的shader一般只会应用在角色身上。但也会做限制。光照数量越多根据增加的forward里的计算量的多寡会增加渲染的压力。但好处是可以很自由的控制想要的光照效果。
多光照的BumpSpecularShader:




此处的截图可以非常明显的发现每一个灯光都被计算了specualer的光照。但是ForwardAdd中的pass里并没有计算specular遮罩,法线以及贴图的信息。因此细节比起ForwardBase中要少。接下继续将法线计算和specular的光照计算也加入ForwardAdd中。




这两张图中可以非常明显的看到法线信息参与到了ForwardBass里的specular和光照的计算。增了很多的局部细节。但相对的对计算量要求更高。接下来加入SpecularMask参与计算。


和上面的图片对比可以很明显的发现specular的高光被specualrmask遮挡住了。由此可以证明ForwardAdd中的PASS是可以进行自由编辑,且根据美术需求进行自由的控制。接下来继续加入给ForwardBase和ForwardAdd中加入自阴影和投射阴影。



主光源自身阴影的绘制



关闭主方向光照仅用点光源进行照明。



主方向光照。阴影和多光源融合的最终效果。

ForwardBase和ForwardAdd或者多PASS的应用对shader编写人员的要求会相比较高。因未牵扯到的后期优化性能上的工作会成批量增加。将其计算嵌入G缓存中也是一种做法。但需要根据平台的支持与否来进行把控。但好处是可以根据自身需要自定义各种光源下的光照。例如在制作风格化的角色时。一般仅接受一盏方向光获得的风格化光照的效果很好把控。但是如果遇到该角色需要受到多种光源影响下时如何能保证光照风格的统一,同时还能获得更优的性能。
因此ForwardAdd和ForwardBase的配合运用则显得非常的必要。但相对的。过于需要自行定义需求的风格化光照效果的同时。维护和性能优化的成本也会相对的提高。因此在unity中进行灯光的限制。单屏内限定实时灯光的数量就显得非常重要。
重点注意:受多光源的影响的物件一般只会运用到角色或者某些重要的物件身上。绝不可大批量运用。无论是PC还是手机游戏。都需要考虑性能上的限制。且现有的ASE或者SG以及之前的SF之类的shader节点编辑器暂时都未能提供可以很方便的编辑多PASS的功能。如果需要此类自定义的控制是避免不了需要进行手动代码编译的。
笔者在这里提供一下自己编译的shader,因为修改了ASE内部的部分默认Shader,可能会对ASE插件产生修改。还请学习时使用。
<hr/>ASEunit修改.unitypackage
4.4K
· 百度网盘


char.unitypackage
58.6M
· 百度网盘

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-9-22 13:38 , Processed in 0.149248 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表