Unity复杂光照

Unity的渲染路径决定了光照如何应用到Shader中,包括前向渲染和延迟渲染。前向渲染是最常用的,分为Base Pass和Additional Pass,适用于简单场景。延迟渲染用于处理大量光源,先计算G缓冲,然后进行光照计算,适用于复杂场景。每种渲染路径都有其适用的光照处理方式和内置变量。在Shader中,需要正确设置Pass的LightMode标签以确保光照计算的正确性。
摘要由CSDN通过智能技术生成

Unity渲染路径
Unity里,渲染路径决定了光照是如何应用到Unity Shader中的,因此,如果跟光源打交道,需要为每个Pass指定使用的渲染路径,只有这样,Unity才会把光源和处理后的光照信息放到数据里,也就是说只有为Shader正确选择和设置了需要的渲染路径,Shader的光照计算才能正确执行。
Unity支持多种类型的渲染路径,前向渲染,延迟渲染。
大多情况下,一个项目只使用一种渲染路径,因此可以为整个项目设置渲染路径,需要在Project Setting设置,默认选择是前向渲染路径,有时候希望使用多个渲染路径,可以在每个摄像机的渲染路径设置中设置相机使用的渲染路径。如果当前显卡不支持所选的渲染路径,会自动使用更低一级的渲染路径。

完成上面设置之后,就可以在每个Pass中使用标签来指定该Pass使用的渲染路径,这是通过设置Pass的LightMode标签实现的,不同类型的渲染路径可能会包含下面标签设置。

Tags {
   "LightMode" = "ForwardBase"}

上面的代码告诉Unity,Pass使用前线渲染中的ForwardBase路径,而前向渲染还有一种路径叫做ForwardAdd。表中给出了Pass的LightMode标签支持的渲染路径设置选项:
在这里插入图片描述
在这里插入图片描述

如果一个Pass没有指定任何渲染路径,一些光照变量很可能不会被正常赋值,计算出的效果很可能是错误的。

前向渲染路径
传统的渲染方式,最常用的一种渲染方式。
原理:
每进行一次完整的前向渲染,我们需要渲染该对象的渲染图元,并计算两个缓冲区的信息:一个是颜色缓冲,一个是深度缓冲。利用深度缓冲来决定一个片元是否可见,如果可见就更新颜色缓冲区的颜色值。伪代码描述前向渲染的大致过程:
在这里插入图片描述

对于每个逐像素光源,都需要进行上面一次完整的渲染流程。如果一个物体在多个逐像素光源的影响区域内,那么物体就需要执行多个Pass,每个Pass计算一个逐像素光源的光照结果,然后在帧缓冲中把这些光照结果混合起来得到最终颜色。假设场景中有N个物体,每个物体受M个光源影响,那么要渲染整个场景一共需要N*M个Pass,可以看出,如果有大量的逐像素关照,那么需要执行的Pass数目会更大,因此,渲染引擎通常会限制每个物体的逐像素光照数目。

Unity中的前向渲染
一个Pass不仅仅可以用来计算逐像素光照,也可以计算逐顶点等其他光照,取决计算光照计算所处的流水线阶段以及计算时候使用的数学模型,当我们渲染一个物体时候,Unity会计算哪些光源照亮了他,以及这些光源照亮该物体的方式。
Unity中,前向渲染路径有三种处理关照的方式,逐顶点,逐像素,球谐函数处理,而决定一个光源使用哪种处理模式取决他的类型和渲染方式。光源类型是指该光源是平行光还是其他类型的光源,光源的渲染模式指的是该光源是否是重要的,如果一个光照模式设置为Important,意味光源很重要,逐像素光源来处理,可以在光源的Light组件上设置这些属性

前向渲染中,渲染一个物体时,Unity会根据场景把各个光源的设置以及这些光源对物体的影响程度,对这些光源进行一个重要度排序。其中,一定数目的光源会按照逐像素处理,然后最多有4个光源按逐顶点的方式处理,剩下的光源可以按照SH方式处理。规则如下:

1.场景中最亮的平行光总是按逐像素处理
2.渲染模式设置成不重要的光源,按照逐顶点或者SH处理
3.渲染模式设置为重要,使用逐像素
4.根据以上规则得到的逐像素光源小于Quality Setting中的逐像素光源数量,会有更多光源以逐像素方式渲染。

前向渲染有两种Pass Base Pass和Additional Pass,这两种Pass进行的标签和渲染设置以及常规光照计算如下
在这里插入图片描述

上图可以看到,在渲染设置中,除了设置Pass的标签外,还使用了#pragma multi_compile_fwdbase这样的编译指令,实验表明,只有使用了这些编译指令,Pass中才可以得到一些正确的光照变量,例如光照衰减。
Base Pass旁边的注释给出了支持的一些光照特性,可以访问光照纹理。
BasePass中渲染的平行光默认是支持阴影的,而Additional Pass中渲染的光源默认情况下是没有阴影的,即使我们在Light组件中设置了有阴影的Shadow Type。但我们可以在Additional Pass中使用#pragma multi_compile_fwdadd_fullshadows代替#pragma multi_compile_fwdadd编译指令,为点光源聚光灯开启阴影效果,这需要Unity在内部使用更多的Shader变种。
环境光和自发光也是在Base Pass中计算,因为,对于一个物体来说,环境光和自发光我们只希望计算一次即可,如果在Additional Pass中计算,就会叠加多次环境光和自发光。
在Additional Pass的渲染设置中,还开启和设置了混合模式,因为希望每个Additional Pass可以与上一次的光照结果在帧缓存中叠加,从而得到最终的有多个光照的渲染效果,如果没有开启和设置混合模式,那么Additional Pass的渲染模式会覆盖掉之前的渲染结果,看起来就好像只受该光源的影响。通常选择的混合模式是Blend One One。
对于前向渲染,一个Unity Shader通常会定义一个BasePass(也可以定义多次,双面渲染)以及一个Additional pass。一个 Base Pass只会执行一次,一个Additional Pass会根据影响该物体的其他逐像素光源的数目被多次调用,每个逐像素光源会执行一次Additional Pass。

实际上,渲染路径的设置用于告诉Unity该Pass在前向渲染路径中的位置,然后底层的渲染引擎会进行相关计算来填充一些内置变量,比如_LightColor0,如何使用这些内置变量进行计算完全取决于开发者的选择。可以利用Unity提供的内置变量在Base Pass中只进行逐顶点光照,也完全可以在Additional Pass中按逐顶点的方式进行光照计算,不进行任何像素光照计算。

内置光照变量和函数
根据使用的渲染路径,Pass中的LightMode的值,Unity会把不同的光照变量传递给Shader
Unity5中,对于前向渲染(LightMode为ForwardBase或ForwardAdd)来说,下表给出了可以在Shader中访问的光照变量
在这里插入图片描述

下面列出前向渲染中可以使用的内置光照函数
在这里插入图片描述

顶点照明渲染路径
对硬件配置要求最少,运算性能最高同时也是效果最差的一种类型,不支持逐像素才能得到的效果,例如阴影,法线,高光反射等。实际上他只是前向渲染的一个子集,顶点照明渲染路径只是使用了逐顶点的方式来计算光照,上面的前向渲染路径中也可以计算一些逐顶点的光源,但如果选择使用顶点照明渲染路径,那么Unity会只填充那些逐顶点相关的光源变量,意味着不可以使用一些逐像素光照变量。

Unity中的顶点照明渲染
顶点照明渲染路径通常在一个Pass中就可以完成对物体的渲染。在这个Pass中,会计算所有光源对该物体的照明,这个计算是按逐顶点处理的,Unity中最快的渲染路径。

可访问的内置变量和函数
Unity中,可以在一个顶点照明的Pass中最多访问到8个逐顶点光源。如果只需要渲染其中两个光源对物体的照明,可以只使用下表中内置光照数据的前两个,如果影响该物体的光源数目小于8,那么数组中剩下的光源颜色会设置成黑色。
在这里插入图片描述
在这里插入图片描述

延迟渲染路径
前向渲染的问题:场景中包含大量实时光源时,前向渲染的性能急速下降。由于前向渲染可能造成的瓶颈问题,延迟渲染流行起来,除了前向渲染中使用的颜色缓冲和深度缓冲外,延迟渲染还会利用额外的缓冲区,统称为G缓冲,G缓冲区存储了我们关心的表面的其他信息,例如法线,位置,用于光照计算的材质属性等。

延迟渲染的原理
主要包含两个Pass。一个Pass,不进行任何光照计算,仅仅计算哪些片元是可见,通过深度缓冲技术实现,当一个片元是可见的,就把相关信息存储在G缓冲。然后,第二个Pass中,利用G缓冲的各个片元信息,例如表面法线,视角方向,漫反射系数,进行真正的光照计算。过程大致可以用下面伪代码来描述:
在这里插入图片描述
在这里插入图片描述

可以看出,延迟渲染使用的Pass数目通常是2个,跟场景中包含的光源数目没有关系,也就是说延迟渲染效率不依赖于场景的复杂度,而是和我们使用的屏幕空间大小有关。因为,需要的信息都在缓冲区中,这些缓冲区可以理解成是一张2D图像,计算实际上就是在这些图像空间进行。

Unity中的延迟渲染
两种延迟渲染路径。一种是遗留的延迟渲染路径,Unity5之前使用的延迟渲染路径,另一种是Unity5.x中使用的延迟渲染路径,如果游戏中使用了大量的实时光照,最好选择延迟渲染路径,这种路径需要一定的硬件支持。

缺点:
不支持真正的抗锯齿共嗯
不能处理半透明
对显卡有一定要求,显卡必须支持MRT ShaderModel3.0以及以上 深度渲染纹理以及双面模板缓冲

使用延迟渲染,Unity要求我们提供两个Pass:
第一个Pass用于渲染G缓冲,在这个Pass,会把物体的漫反射颜色,高光反射颜色,平滑度,法线,自发光和深度信息渲染到屏幕空间的G缓冲区,对于每个物体,Pass只会执行一次。
第二个Pass用于真正的光照模型,这个Pass会使用上一个Pass中渲染的数据来计算最终的光照颜色,再存储到帧缓冲。

默认的G缓冲包含了以下几个渲染纹理
RT0: 格式ARGB32 RGB通道用于存储漫反射颜色,A通道没被使用。
RT1:格式ARGB32,RGB用于存储高光反射,A用于存储高光反射的指数部分。
RT2:格式ARGB2101010,RGB通道用于存储法线,A通道没被使用
RT3:格式是ARGB32(非HDR) 或ARGBHalf(HDR) 用于存储自发光+lightmap+反射探针
深度缓冲和模板缓冲

在第二个Pass中计算光照时候,默认情况下仅可以使用Unity内置的Standard光照模型,如果想要使用其他光照模型,需要替换掉原来的Internal-DeferredShading.Shader文件。

可访问的内置变量和函数
下表给出处理延迟渲染路径可以使用的光照变量,都可以在UnityDeferredLibrary.cginc文件找到他们的声明:
在这里插入图片描述

总体来说,需要更艰巨游戏发布的目标平台来选择渲染路径,当前显卡不支持所选路径,那么Unity会自动使用低一级的渲染路径。
Unity光源类型

Unity一共支持4种光源,平行光,点光源,聚光灯,面光源。面光源仅在烘焙才可发挥作用,这里不讨论。最常用的光源属性有位置,方向,颜色,强度以及衰减五个属性。
平行光
没有位置,照亮范围没有限制,几何属性只有方向,可以调整平行光的Transform组件中的Rotation属性来改变光源方向,平行光到场景所有点的方向都是一样的,平行光没有具体的位置,所以也不存在衰减的概念。
点光源
照亮空间有限,由空间一个球体定义。点光源可以表示由一个点出发,向所有方向延伸的光。下图给出Unity中点光源在Scene视图中的表示以及Light组件的面板。球体半径可以由面板的Range属性调整,也可以在Scene视图中直接拖拉点光源的线框来修改属性。点光源是有位置属性的,由Position属性定义,方向属性需要用点光源的位置减去某点的位置得到该点的方向。而点光源的颜色和强度可以在Light组件面板中调整。同时,点光源也是会衰减的,随着物体逐渐远离点光源,接收到的光照强度也会逐渐减少。点光源球心处光照强度最强,球体边界处最弱,值为0。
聚光灯
照亮空间有限,空间中的一块锥形区域定义的。聚光灯可以用于表示一个特定位置出发,向特定方向延伸的光。锥形区域的半径由面板中的Range属性决定,锥体的
张开角度Spot Angle属性决定。聚光灯的位置同样是Position属性定义,方向属性用聚光灯的位置减去某点的位置来得到他到该点的方向。聚光灯的衰减也是随着物体逐渐远离点光源而逐渐减少,锥形顶点处光照强度最强,锥形边界强度为0。

前向渲染中处理不同的光源类型

Shader "Unity Shaders Book/Chapter 9/Forward Rendering" {
   
	Properties {
   
		_Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
		_Specular ("Specular", Color) = (1, 1, 1, 1)
		_Gloss ("Gloss", Range(8.0, 256)) = 20
	}
	SubShader {
   
		Tags {
    "RenderType"="Opaque" }
		
		Pass {
   
			// Pass for ambient light & first pixel light (directional light)
			Tags {
    "LightMode"="ForwardBase" }
		
			CGPROGRAM
			
			// Apparently need to add this declaration 
			#pragma multi_compile_fwdbase	
			
			#pragma vertex vert
			#pragma fragment frag
			
			#include "Lighting.cginc"
			
			fixed4 _Diffuse;
			fixed4 _Specular;
			float _Gloss;
			
			struct a2v {
   
				float4 vertex : POSITION;
				float3 normal : NORMAL;
			};
			
			struct v2f {
   
				float4 pos : SV_POSITION;
				float3 worldNormal : TEXCOORD0;
				float3 worldPos : TEXCOORD1;
			};
			
			v2f vert(a2v v) {
   
				v2f o;
				o.pos = UnityObjectToClipPos(v.vertex);
				
				o.worldNormal = UnityObjectToWorldNormal(v.normal);
				
				o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
				
				return o;
			}
			
			fixed4 frag(v2f i) : SV_Target {
   
				fixed3 worldNormal = normalize(i.worldNormal);
				fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
				
				fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
				
			 	fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * max(0, dot(worldNormal, worldLightDir));

			 	fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
			 	fixed3 halfDir = normalize(worldLightDir + viewDir);
			 	fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(max(0, dot(worldNormal, halfDir)), _Gloss);

				fixed atten = 1.0;
				
				return fixed4(ambient + (diffuse + specular) * atten, 1.0);
			}
			
			ENDCG
		}
	
		Pass {
   
			// Pass for other pixel lights
			Tags {
    "LightMode"="ForwardAdd" }
			
			Blend One One
		
			CGPROGRAM
			
			
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值