Unity入门精要04(0)-更复杂的光照

本书知识点是真的多(

1.渲染路径

Unity中的渲染路径有如下选项 

   

对某一Pass指定了渲染路径后,Unity会在过程中自动填充系统的变量,便于使用。

1.1 前向渲染路径

   前向渲染的伪代码如下

一句话概括前向渲染就是:对于每个光源在其范围能影响物体的都计算一次所有的Pass(除了Base Pass以外),计算完光照后再把计算结果叠加起来。

但这如果在多光源的情况下会造成极大的性能消耗。因此Unity通常会自动进行光照质量的限制,

当然也可以自己设置

 

 在Render Mode处如果设置为Important,那么该光源就是逐像素光源。

详细参考可见  ↓↓↓

Forward rendering path - Unity 手册 (unity3d.com)

1.2 前向渲染的两种Pass

  

关于编译指令可参考

Declaring and using shader keywords in HLSL - Unity 手册 (unity3d.com)

使用这些编译指令可以产生Shader变种

 Base Pass 最基础的Pass,一个Base Pass只会进行一次平行光和其它所有的逐顶点和SH光

使用方法:在第一个Pass中加入

Tags { "LightMode"="ForwardBase" }
#pragma multi_compile_fwdbase//加入此编译命令,可以使之后光照衰减所使用的变量可以被正确赋值
//即能在BasePass中访问到正确的光照变量

 在第二个Pass中写下

Tags { "LightMode"="ForwardAdd" }
Blend One One//多光源叠加要开启混合,具体的混合操作可以看透明效果实现的那篇文章
#pragma multi_compile_fwdadd//此编译命令表示该Pass是用来进行多光源叠加的
//效果同上

Additional Pass,会计算影响该物体的所有逐像素光源,每个光源都进行一次Pass.

为了使Additional Pass 能和Base Pass进行混合,我们开启了混合命令,具体的混合命令请看《Unity入门精要》中的透明效果部分。

效果如下,我们设置绿色的为平行光,红色的为点光源

在帧调试器的时候,渲染顺序是调用Base先渲染平行光,然后调用Additional点光源远近依次渲染 

1.3 逐像素,SH光源和顶点照明(已经被抛弃)

  逐像素比较简单,略过,着重记录一下SH光源(魔方工作室的面试考到过)

球谐函数就是一组分布在球上的函数,这恰好可以描述光照在一个物体上的光照。 

详细参考:球谐光照——球谐函数 - 知乎 (zhihu.com)

2.延迟渲染。

如果游戏中有大量的实时光照,那么用前向渲染消耗会非常大,因为每个光照都会计算所有的Additional Pass,出大问题(

延迟渲染似乎只能在URP和SRP中使用?(不太清楚)

那么什么是延迟渲染?

延迟渲染分为两个Pass,第一个Pass通道用来将信息存储在G缓冲区(实际上就是输出到一张渲染纹理上)中

然后再第二个Pass进行计算光照和进行合成

可以把延迟渲染想象成一个拼图游戏。首先,你需要把所有的拼图碎片(物体)按照一定的规则(投影)放到一个大的画板(缓冲区)上,每个碎片都有自己的颜色、形状、质感等信息。这个过程叫做几何渲染(geometry rendering)。

然后,你需要把一些灯泡(光源)放到画板上,每个灯泡都有自己的颜色、亮度、方向等信息。这个过程叫做光照渲染(light rendering)。

最后,你需要把画板上的所有碎片和灯泡的信息混合起来,形成一个完整的拼图(图像)。这个过程叫做合成渲染(composition rendering)。

因此在实时光照和大量光照中延迟渲染性能更优。但延迟渲染也是有缺点的,例如

·不能支持真正的抗锯齿(因为延迟渲染使用了非常多的缓冲区来储存信息,而缓冲区的分辨率一般是和屏幕空间不同的,如果相同的话,缓冲区会占用非常多的带宽)

·不能处理半透明(因为延迟渲染相比于前向渲染是将光照计算放在三维物体投影到二维纹理之后进行光照计算。而我们在进行延迟渲染过程中是需要G-buffer,而G-buffer只能处理最前面的混合后的像素,不能记录半透明物体后面的像素)

·对显卡有一定要求,显卡必须支持MRT

延迟渲染的具体操作方法:使用两个Pass,第一个Pass用于渲染G_Buffer,用来储存漫反射颜色,高光反射颜色,自发光,平滑度,法线,深度,金属度等信息渲染到基于屏幕空间下的G缓冲区

 如图所示,更详细的可见

LearnOpenGL - Deferred Shading

 第二个Pass来计算光照,默认情况下只能用Standard 光照模型,如果要用其它的,就要替换掉原有的Internal-DeferredShaing.shader文件

延迟渲染可以访问的内置变量和函数

 3.光源类型

平行光,只有方向没有位置

点光源

聚光灯

面光源

4.光照衰减

  4.1光照衰减纹理

Unity内部使用了一张名为_LightTexture0的纹理来计算光源衰减。如果对光源使用了Cookie,那么衰减查找纹理是_LightTextureB0。

通俗来讲,这个纹理就是将光照衰减映射到纹理上,然后从(0,0)(离光源最近)依次查询到(1,1)(离光源最远)。为了知道物体上的某一点受到光照后的颜色究竟是什么?我们把物体上的所有顶点转换到光源空间下(这里就体现了Shader中的编程核心思想,要知道物体的样子,就是知道物体的颜色,要知道物体的颜色,就要根据光照模型计算,要获取光照模型中的相应参数,就应转换到相应的空间下去计算),在光源空间下查询光照衰减纹理

完整的光照衰减计算代码

#ifdef USING_DIRECTIONAL_LIGHT
    fixed atten = 1.0;
#else
	#if defined (POINT)//点光源
		float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;
		fixed atten = tex2D(_LightTexture0, dot(lightCoord,lightCoord).rr).UNITY_ATTEN_CHANNEL;
				//利用顶点在光源空间的中的距离的平方进行采样(避免开方)
				//利用宏UNITY_ATTEN_CHANNEL获取衰减纹理中(_LightTexture0)衰减值所在的分量
	#elif defined (SPOT)//聚光灯
		float4 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1));
		fixed atten = (lightCoord.z > 0) * tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w * tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;
	#else
		fixed atten = 1.0;
	#endif
#endif

重点说明一下聚光灯(比较复杂) 

在聚光灯下_LightTextureB0是阴影纹理,_LightTexture0是储存光照强度和聚光灯颜色

1.

(lightCoord.z > 0) 这里的代码其实有歧义,因为在计算聚光灯光照的时候,我们通常采用的是锥形视锥,而不是平截头视锥,因为锥形视锥更适合表示聚光灯的光照区域。在锥形视锥中,视锥的顶点在 z 轴的负半轴上,因此 z 坐标始终是负数。而这里可能重新定义了朝向也说不定。所以这里开头是判断是否在视锥中(关于这个知识,可以去看Games101中的透视投影,每次投影的时候都需要将物体平移到原点,然后旋转相机为正(或者说是光源视锥)然后投影,然后再平移旋转返回原来的样子)

2. 

tex2D(_LightTexture0, lightCoord.xy / lightCoord.w + 0.5).w

第一个tex2D函数采样聚光灯的光强和颜色信息,lightCoord.xy / lightCoord.w 这里的含义是为了将其变为非齐次坐标变为齐次坐标,我们将物体的顶点从世界空间转换到光源的视锥空间的时候,因为矩阵乘法会给w赋上一定的权重,离视点越远,x,y,z越小,投影出来就会越小(这里需要理解齐次坐标的含义,我们将(x,y,z,w)叫做齐次坐标,它实际上表示的点应该为(x/w, y/w, z/w) 就是非齐次坐标,w中储存的是顶点的逆深度(1/z)),我们使用了转换矩阵转换到光源的视锥空间,然后又使用投影矩阵投影到二维上(因为纹理是二维的,可以形象的理解压缩成二维,贴在纹理上一个一个找),但是此时的x,y仍是齐次坐标,因此要除以w,消除w所带来的透视效果,变成原本的非齐次坐标。得到真正的二维坐标,而加上0.5则是保证映射到[0,1]上(纹理的范围)

那么为什么只取w分量呢?因为tex2D函数返回RGBA四个通道,我们取第4个通道,即透明度来表示光照衰减系数。

3. 

tex2D(_LightTextureB0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL

这个阴影贴图是根据光源空间中的深度信息计算得到的,因此需要将光源空间中的坐标进行 dot 运算得到深度值,然后取两个 R 分量构成 UV 坐标,用于采样阴影贴图纹理。这样做是因为阴影贴图可能采用不同的投影方式,如果不用 dot 运算来获取深度值并计算 UV 坐标,可能会导致阴影贴图采样出现错误。

最后使用宏UNITY_ATTEN_CHANNEL获取我们生产的衰减纹理中衰减值所在的分量

4.2 使用数学公式计算衰减

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值