写在前面
在Unity中,天空盒(Skybox)不仅承担视觉上的背景作用,更是场景环境光照与氛围塑造的重要组成部分。不同时间、天气、场景转换等,都需要灵活调整天空的亮度。而**曝光度(Exposure)**就是其中最关键的调控参数之一。
本篇将带你从实战角度出发,构建一个Skybox曝光控制组件(SkyboxExposureController),支持:
- 内置渲染管线(Built-in)
- 通用渲染管线(URP)
- 高级渲染管线(HDRP)
最终实现一个跨平台、易拓展、实时可控的天空盒曝光调整方案。
一、Skybox曝光控制的实际意义
在游戏开发、VR仿真、影视预览等Unity项目中,我们往往需要模拟以下视觉效果:
场景 | 曝光度变化的意义 |
---|---|
日夜交替 | 白天亮、晚上暗 |
天气系统 | 晴天曝光高,阴天低曝光 |
剧情过渡 | 切换场景时通过渐变曝光实现氛围过渡 |
剧情特写 | 聚焦主角时,压暗背景提高聚焦感 |
这些都需要我们对天空盒的曝光参数进行精细控制,而由于Unity的不同渲染管线(Built-in / URP / HDRP)控制方式不同,往往给开发带来了困扰。因此,我们要做的第一件事就是——统一兼容性。
示例效果
- 曝光度:0.05
- 曝光度:0.36
- 曝光度:1.0
二、Unity三大渲染管线曝光控制机制对比
渲染管线 | 曝光控制方式 | 备注 |
---|---|---|
内置管线 | Skybox材质的 _Exposure 属性 | 默认Skybox Shader支持 |
URP | Volume中的Exposure组件 | 需设置为Fixed模式 |
HDRP | Volume中的HDRISky组件 | 通过 exposure.value 设置 |
如你所见,三者在控制机制和接口上都不相同,因此要通过代码自动适配。
三、组件设计目标与亮点
我们要实现的 SkyboxExposureController
组件将具备以下特性:
- 自动识别当前渲染管线,无需手动配置;
- Inspector面板可调节曝光值,并支持运行时动态生效;
- 打包构建可用,不依赖
UNITY_EDITOR
宏; - 扩展性强,支持绑定曲线/Timeline进行动画控制;
- Volume自动探测功能,降低用户配置门槛。
四、实现思路与核心技术
判断当前渲染管线
Unity 提供了以下宏定义用于区分当前渲染管线:
UNITY_RENDER_PIPELINE_UNIVERSAL
UNITY_RENDER_PIPELINE_HDRP
若都未定义,则说明使用的是内置渲染管线。
曝光控制逻辑实现
- 内置管线:通过
RenderSettings.skybox.SetFloat("_Exposure", value)
修改曝光; - URP:通过 Volume 的
Exposure
组件,设置其fixedExposure.value
; - HDRP:通过 Volume 的
HDRISky
组件,设置其exposure.value
。
注意 URP/HDRP 都需事先启用 Volume 中的相关模块。
自动寻找 Volume(新增功能)
为提升用户体验,我们设计逻辑:当用户未设置 Volume 引用时,自动查找场景中第一个可用 Volume。
五、完整组件代码
using UnityEngine;
using UnityEngine.Rendering;
#if UNITY_RENDER_PIPELINE_HDRP
using UnityEngine.Rendering.HighDefinition;
#elif UNITY_RENDER_PIPELINE_UNIVERSAL
using UnityEngine.Rendering.Universal;
#endif
[ExecuteAlways]
public class SkyboxExposureController : MonoBehaviour
{
[Header("统一曝光值")]
[Range(0f, 5f)]
public float exposure = 1.0f;
[Header("URP / HDRP 使用的 Volume")]
public Volume volume;
#if UNITY_RENDER_PIPELINE_HDRP
private HDRISky hdriSky;
#elif UNITY_RENDER_PIPELINE_UNIVERSAL
private Exposure urpExposure;
#endif
private Material skyboxMaterial;
void OnEnable()
{
if (volume == null)
volume = FindObjectOfType<Volume>();
skyboxMaterial = RenderSettings.skybox;
#if UNITY_RENDER_PIPELINE_HDRP
if (volume != null && volume.sharedProfile.TryGet(out hdriSky))
{
hdriSky.active = true;
}
#elif UNITY_RENDER_PIPELINE_UNIVERSAL
if (volume != null && volume.sharedProfile.TryGet(out urpExposure))
{
urpExposure.active = true;
}
#endif
}
void Update()
{
#if UNITY_RENDER_PIPELINE_HDRP
if (hdriSky != null)
{
hdriSky.exposure.value = exposure;
}
#elif UNITY_RENDER_PIPELINE_UNIVERSAL
if (urpExposure != null)
{
urpExposure.fixedExposure.value = exposure;
}
#else
if (skyboxMaterial != null && skyboxMaterial.HasProperty("_Exposure"))
{
skyboxMaterial.SetFloat("_Exposure", exposure);
}
#endif
}
}
六、使用方法详解
1. 内置渲染管线
- 确保项目未启用SRP;
- 将SkyboxExposureController挂载到任意场景对象;
- 拖入RenderSettings中正在使用的Skybox材质;
- 调整
exposure
值,即可实时控制天空亮度。
2. URP渲染管线
- 创建一个 Global Volume;
- 添加 Exposure 模块,并设置为 Fixed 模式;
- 将 SkyboxExposureController 脚本挂载至对象;
- 拖入 Volume 引用,或让脚本自动查找;
- 调整
exposure
数值观察效果。
URP 关键点:Exposure必须为 Fixed模式 才会响应参数设置。
3. HDRP渲染管线
- 创建一个 Global Volume;
- 添加 HDRISky 和 VisualEnvironment 模块;
- 将 Sky Type 设置为 HDRI;
- 设置 Exposure 值为可变;
- 同样添加脚本并拖入 Volume。
HDRP中
HDRISky.exposure.value
才是真正控制天空亮度的关键。
七、进阶用法:曲线控制曝光变化
为了实现如“日夜循环”般动态变化,我们可增加如下扩展:
[Header("曝光曲线控制")]
public bool useCurve;
public AnimationCurve exposureCurve;
public float time;
void Update()
{
if (useCurve)
{
exposure = exposureCurve.Evaluate(time);
time += Time.deltaTime;
}
// ...原有曝光设置逻辑
}
也可以使用 Timeline 控制 exposure
属性,实现导演级画面过渡。
八、常见问题Q&A
Q1:为什么打包后曝光设置无效?
答: 可能你误用了 #if UNITY_EDITOR
包裹了整段逻辑。应只用于识别逻辑判断,不影响运行时的代码执行。
Q2:URP下曝光值始终不变?
答: 请检查Exposure组件是否启用,并确保Mode为Fixed,否则不会响应fixedExposure.value
设置。
Q3:支持Timeline绑定吗?
答: 可将 exposure
变量声明为 public
,直接在Timeline中作为可动画属性进行关键帧绑定。
小结
通过本文的组件实现,我们可以在不同渲染管线下统一控制天空盒的曝光度,便于搭建日夜交替、天气系统、剧情过渡等视觉表现。