【Unity Shader】(七) ------ 复杂的光照(下)

笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题。

【Unity Shader】(三) ------ 漫反射和高光反射的实现
【Unity Shader】(四) ------ 纹理之法线纹理、单张纹理及遮罩纹理的实现
【Unity Shader】(五) ------ 透明效果之半透明效果的实现及原理
【Unity Shader】(五) ------ 透明效果之半透明效果的实现及原理
【Unity Shader】(七) ------ 复杂的光照(下)
【Unity Shader】(八) ------ 高级纹理之立方体纹理及光线反射、折射的实现
 

目录

前言

一. 光照衰减

1.1 使用 LUT

1.2 关于光照衰减纹理

1.3 关于光照衰减的总结

二. 阴影

2.1 阴影是如何实现出来的

Algorithm overview

2.2  普通非透明物体阴影的实现

2.2.1 准备工作

2.2.2 接收阴影

2.2.3 完善的的光照衰减和阴影管理

2.3 普通透明物体的阴影

三. 完整的光照 shader

四. 总结

 


 

前言

本文承接上文【Unity Shader】(六) ------ 复杂的光照(上),介绍剩下的光照衰减和阴影部分,最后实现包含了对不同光照类型进行光照计算,光照衰减,阴影产生等部分的真正意义上的标准光照 shader 。因为本文会上文有所联系,所以个人建议读者阅读上文,以免在本文某些地方出现思路上的突兀。

一. 光照衰减

 

1.1 使用 LUT

前面说过,我们使用 LUT 来计算衰减,这种做法的优劣点如下:

  • 优点:因为直接计算光照衰减会涉及大量且复杂的数学运算,使用 LUT 可以不依赖数学表达式的复杂性,只需一个参数去采样即可。
  • 缺点 : ① 需要预处理得到纹理,纹理大小影响衰减的精度。② 不直观,且使用 LUT 后就无法使用其它数学公式来计算。

当然,Unity 默认这种方法也是因为其在一定程度上提升了性能且大部分情况下,得到的效果是良好的。

 

1.2 关于光照衰减纹理

Unity 内使用 _LightTexture0 的纹理来计算光照衰减,在之前的代码中,我们已经使用过了。通常情况下,我们只关心 _LightTexture0 对角线上的纹理颜色值,其代表了在光源空间下不同位置的点的衰减值。(0,0)表示与光源重合的点的衰减值,(1,1)表示距离最远的点的光照衰减值。

上面说过,需要用一个点对纹理采样,那么就要先知道该点在光源空间下位置信息。同样是空间转换,我们在这里需要用到的转换矩阵为 _LightMatrix0 。在 Unity 5.4 之后,这个矩阵更换为 unity_WorldToLight 了。

 

所以这里转换语句应该为

float3 lightCoord = mul(unity_WorldToLight, float4(i.worldPos, 1)).xyz;

然后使用这个坐标的摸的平方进行采样。当然,如果用距离值来计算就需要开方操作了,为了,避免这个繁琐的步骤,我们使用顶点距离的平方来采样

fixed atten = tex2D(_LightTexture0, dot(lightCoord, lightCoord).rr).UNITY_ATTEN_CHANNEL;

 其中宏 UNITY_ATTEN_CHANNEL 可以得到衰减值所在的分量。

 

1.3 关于光照衰减的总结

上述所说的知识足够读者应付大部分的光照计算中的光照衰减部分,如果读者着实不希望采用 LUT 的方法来计算衰减,也可以使用数学公式,只是这样需要对公式有更深入的理解。很遗憾的是,笔者并没有找到关于计算衰减的公式的资料,对于衰减方面的资料,着实所寻不多,日后如果我能找到相关知识,我会补充到这篇文章中。

 

二. 阴影

在许多游戏制作中,为了追求真实,光影效果是必不可少的。光我们之前介绍了,现在来介绍阴影。

 

2.1 阴影是如何实现出来的

想象一下,一条光线从远方射过来,当它遇到了第一个不透明的物体时,那么理所当然的是,这条射线就无法再去照亮别的物体了。同时,挡住这条光线的物体会向附近的物体投射阴影。也就是说,阴影是光线无法到达的区域

在 Unity 的实时渲染中,我们采用的是 Shadow Map 技术。关于 Shadow Map,我们可以 WIKI 上看到它的解释。

大意为:Shadow Map 是 Lance Williams 先生在 1978 年提出的技术,从此之后,它常用于预渲染和实时场景中。在 Unity 中,就是先把摄像机的位置与光源重合,然后摄像机看不到的区域就是阴影。这样理解是不是很简单?

 

我们先来看看 Shadow Map 是如何定义其工作原理的:

 

Algorithm overview

Rendering a shadowed scene involves two major drawing steps. The first produces the shadow map itself, and the second applies it to the scene. Depending on the implementation (and number of lights), this may require two or more drawing passes.

 

简单地说就是 : ① 生成阴影纹理  ② 在场景中使用阴影纹理

比如:在前向渲染中,如果平行光开启了阴影(要注意需要手动开启,创建了一个新光源,默认是没有阴影的)

Unity 就会为这个平行光计算阴影映射纹理。这张阴影映射纹理实质就是一张深度纹理,记录着从光源出发,距离光源最近的表面信息

 

通常情况下,是通过调用 Base Pass 和 Additional Pass 来更新深度信息,但我们之前也说过,这两个 Pass 中包含了各种光照计算。为了避免多余的光照计算所造成的性能损耗,Unity 选择使用另外一个特别的 Pass 来管理光源的映射纹理。这个 Pass 就是 LightMode 标签中设置为 Shadow Caster 的那个 Pass。这个 Pass 的渲染目标是深度纹理。所以当一个光源开启了阴影效果之后,引擎就会在当前渲染物体的 shader 中寻找这个 Pass ,如果找不到,就去 Fallback 里面找;还找不到,就去 Fallback 的 Fallback 里面找。如果这样都找不到,那么该物体就无法向其它物体投射阴影,但是可以接收来自其它物体的阴影。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值