在本文,笔者讲简单的讲一讲怎么在 Unity 中实现黑夜白天的一键切换。
演示:
场景中的灯效,除了直射光其他均是烘焙出来的。
色彩太丰富,实在不敢录太长时间
原理:
如果想要实现上面的这个黑夜白天切换效果,控制好以下几点就行了:
- 保证烘焙黑夜和白天光照贴图使用的是同一个场景(Ctrl+D挺好用)。
- 动态切换 黑夜和白天的 光照贴图设置(LightmapSettings)。
- 动态修改天空盒白天和黑夜对应的天空盒,或者像上面演示的那样:仅仅修改天空盒曝光值。
- 修改直射光的强度(如果有必要,可以修改直射光的角度)。
- 如果像演示中的镜面建筑,加了光照探针(ReflectionProbe)的需要指定对应的烘焙OK的贴图。
- 关于黑夜白天差异性的游戏对象:
- 不参与烘焙的,比如上面的效果晚上多加的 Lens 辉光,直接白天显示隐藏晚上显示即可。
- 参与烘焙的,可以直接使用显示 / 隐藏操作。
- 参与烘焙的,也可以存为预制体动态加载,但该方案要写点代码保证游戏对象上烘焙的光照信息能被加载出来。
示例:
using System.Linq;
using UnityEngine;
using DG.Tweening;
public class TestSwitchBakedLightMap : MonoBehaviour
{
[Header("Common Configuration and Component ")]
public ReflectionProbe reflectionProbe;
public Light directLight;
public GameObject effectLights; //晚上要打灯,白天不打灯
[Header("Configuration and Component For Day"), Space(10)]
public LightDataConfiguration dayConfig;
public Cubemap dayReflect;
public float daySkyboxExposure = 1f;
public float directLightDayIntensity = 1;
public float dayLightIntensityMultiplier = 1;
[Header("Configuration and Component For Night"), Space(10)]
public LightDataConfiguration nightConfig;
public Cubemap nightReflect;
public float nightSkyboxExposure = 0.2f;
public float directLightNightIntensity = 0.2f;
public float nightLightIntensityMultiplier = 0.3f;
// private fields
LightmapData[] day;
LightmapData[] night;
bool isDay = true;
private void Start()
{
night = nightConfig.Lightmaps
.Select(x => new LightmapData() { lightmapColor = x.lightmapColor, shadowMask = x.shadowMask })
.ToArray();
day = dayConfig.Lightmaps
.Select(x => new LightmapData() { lightmapColor = x.lightmapColor, shadowMask = x.shadowMask })
.ToArray();
RenderSettings.skybox.SetFloat("_Exposure", daySkyboxExposure);
reflectionProbe.customBakedTexture = dayReflect;
}
void OnGUI()
{
if (GUILayout.Button(isDay ? "To Night" : "To Day"))
{
SwithDayAndNight();
}
}
public void SwithDayAndNight()
{
if (!isDay)
{
// 切换为白天的配置
LightmapSettings.lightmaps = day;
directLight.DOIntensity(directLightDayIntensity, 0.5f);
DOTween.To(()=> RenderSettings.ambientIntensity, y=> { RenderSettings.ambientIntensity = y; },dayLightIntensityMultiplier,0.5f);
RenderSettings.skybox.DOFloat(daySkyboxExposure, "_Exposure", 0.5f);
effectLights.SetActive(false);
reflectionProbe.mode = UnityEngine.Rendering.ReflectionProbeMode.Custom;
reflectionProbe.customBakedTexture = dayReflect;
}
else
{
// 切换为晚上的配置
LightmapSettings.lightmaps = night;
directLight.DOIntensity(directLightNightIntensity, 0.5f);
DOTween.To(()=> RenderSettings.ambientIntensity, y=> { RenderSettings.ambientIntensity = y; },nightLightIntensityMultiplier,0.5f);
RenderSettings.skybox.DOFloat(nightSkyboxExposure, "_Exposure", 0.5f);
effectLights.SetActive(true);
reflectionProbe.customBakedTexture = nightReflect;
}
isDay = !isDay;
}
}
Tips:
- 仅仅是将原理中提到的几点找到对应的 API 通过代码动态修改罢了,仅供熟悉API.
- 使用了 DoTween 的非扩展方法形式的缓动,可以熟悉下。
- 使用了 DoTween 的扩展方法形式对天空盒(材质球)的曝光值进行了缓动。
- 使用了 ScriptableObject 分别保存的黑夜/白天的光照贴图设置信息,只需要 lightmapColor 和 shadowMask 两组信息,使用 ScriptableObject 在本示例中的好处可要好好体会体会哈~
- 演示中没有出现原理中第六条第三点提到的问题,所以示例代码没做演示。
扩展阅读:
-
Unity5.x场景优化之动态设置光照贴图lightmap - yuyingwin的专栏 - CSDN博客
↑怎么让动态加载的游戏对象实例化后自动加载光照贴图效果:↑ -
[Unity3D]Lightmapping使用及动态加载lightmap方案 - bread's code - CSDN博客
↑更多关于光照贴图+光照探针的参考资料↑
网友评论