【从UnityURP开始探索游戏渲染】专栏-直达
技术原理与核心机制
- 立方体贴图映射:天空盒本质是包裹场景的立方体纹理映射,通过六个面的HDR图像(前、后、左、右、上、下)构成全景环境。URP渲染管线中,天空盒被定义为无限远的背景,始终跟随摄像机移动但不受视锥体裁剪。
- 光照交互 :天空盒直接影响全局光照计算,其颜色和亮度参与环境光遮蔽、反射探针等计算。动态天空盒通过调整太阳高度角(
mainLight.direction.y
)实现昼夜交替的光照变化。 - 程序化生成 :URP支持通过Shader代码动态生成天空盒,例如使用
smoothstep
函数平滑过渡昼夜状态,基于worldDir.y
计算天顶与地平线渐变颜色(如lerp(_DayBottomColor, _DayTopColor, verticalPos)
)。
发展历史关键节点
- 静态天空盒阶段:早期Unity仅支持预烘焙的立方体贴图,需手动配置六张纹理。
- 动态天空盒引入:2018年URP管线加入程序化天空盒支持,允许通过代码实时调整天空参数。
- HDRP/URP分化:2020年后,URP优化了移动端性能,采用简化版大气散射模型,而HDRP保留物理精确模拟。
解决的问题
- 性能优化:相比传统3D天空模型,天空盒仅消耗1次绘制调用。
- 环境一致性:确保远距离背景与光照系统同步(如昼夜切换时环境光自动适配)。
- 艺术控制 :支持HDR图像和程序化参数(如
_Exposure
曝光值)调整氛围。
URP实现示例
以下动态天空盒Shader关键代码实现昼夜交替:
c
hlsl
// 计算太阳高度状态(0=深夜,1=正午)
float sunNightStep = smoothstep(-0.3, 0.25, _MainLight.direction.y);
// 天空颜色分层混合
float3 skyColor = lerp(_NightColor, _DayColor, sunNightStep);
// 地平线光晕效果
float horizonGlow = pow(saturate(1 - absY), _HorizonSharpness);
动态天空盒完整实现
核心实现架构
-
Shader基础结构
使用URP的Unlit Shader模板,定义天空球体顶点着色器计算世界空间坐标,片段着色器实现颜色混合逻辑。关键参数包括:
chlsl float3 _SunDirection; float4 _DayColor, _NightColor; float _HorizonSharpness;
-
昼夜控制机制
通过
_SunDirection.y
值判断昼夜状态,结合smoothstep
函数实现平滑过渡。太阳位置由主光源方向控制,月亮位置取反方向。
完整代码实现
-
DynamicSkybox.shader
cShader "URP/DynamicSkybox" { Properties { _SunTex ("Sun Texture", 2D) = "white" {} _MoonTex ("Moon Texture", 2D) = "white" {} _DayTopColor ("Day Top", Color) = (0.37,0.74,1,1) _DayBottomColor ("Day Bottom", Color) = (0.89,0.96,1,1) _NightExposure ("Night Exposure", Range(0,5)) = 1 } SubShader { Tags { "Queue"="Background" "RenderType"="Background" } Pass { HLSLPROGRAM #pragma vertex vert #pragma fragment frag #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl" struct Attributes { float4 positionOS : POSITION; }; struct Varyings { float4 positionCS : SV_POSITION; float3 positionWS : TEXCOORD0; }; Varyings vert(Attributes v) { Varyings o; o.positionCS = TransformObjectToHClip(v.positionOS.xyz); o.positionWS = TransformObjectToWorld(v.positionOS.xyz); return o; } float3 _SunDirection; sampler2D _SunTex, _MoonTex; float4 _DayTopColor, _DayBottomColor; float _NightExposure; half4 frag(Varyings i) : SV_Target { float3 viewDir = normalize(i.positionWS); float sunDot = saturate(dot(viewDir, _SunDirection)); float nightFactor = smoothstep(0.1, -0.1, _SunDirection.y); // 天空颜色混合 float verticalPos = saturate(viewDir.y * 0.5 + 0.5); float3 daySky = lerp(_DayBottomColor, _DayTopColor, verticalPos); float3 nightSky = _NightColor * _NightExposure; float3 skyColor = lerp(daySky, nightSky, nightFactor); // 太阳/月亮绘制 float sunMask = step(0.999, sunDot); float moonMask = step(0.999, -sunDot); float4 celestialBody = sunMask * tex2D(_SunTex, i.uv) + moonMask * tex2D(_MoonTex, i.uv); return float4(skyColor + celestialBody.rgb, 1); } ENDHLSL } } }
-
SkyboxController.cs
csharpusing UnityEngine; using UnityEngine.Rendering; public class SkyboxController : MonoBehaviour { [SerializeField] private Light _mainLight; [SerializeField] private Material _skyboxMaterial; [SerializeField] private float _dayDuration = 120f; private float _currentTime; void Update() { _currentTime += Time.deltaTime / _dayDuration; _currentTime %= 1f; // 计算太阳高度角(0-1对应日出到日落) float sunAngle = Mathf.Lerp(-0.5f, 1.5f, _currentTime); _mainLight.transform.rotation = Quaternion.Euler(sunAngle * 180f, 0, 0); // 更新Shader参数 _skyboxMaterial.SetVector("_SunDirection", _mainLight.transform.forward); RenderSettings.skybox = _skyboxMaterial; // 动态调整光照强度 float lightIntensity = Mathf.Clamp01(sunAngle * 2f); _mainLight.intensity = lightIntensity; } }
-
CloudNoise.shader
c// 云噪声生成Shader需单独实现 Shader "URP/CloudNoise" { Properties { _NoiseScale ("Noise Scale", Float) = 1 } SubShader { // 云噪声生成逻辑... } }
关键实现细节
-
太阳轨迹计算
- 通过
Mathf.Lerp(-0.5f, 1.5f, _currentTime)
实现太阳从地平线下升起再落下的完整周期,y值小于0时进入夜晚阶段。
- 通过
-
性能优化技巧
- 使用
step()
替代if
判断天体可见性 - 通过
lerp
实现颜色平滑过渡避免突变 - 云层采用分形噪声算法降低采样次数
- 使用
-
天气系统集成
可扩展
_WeatherDensity
参数控制云层厚度,结合_RainIntensity
实现雨天效果,通过材质参数动画控制天气过渡。
配置流程
- 创建URP渲染管线资产
- 将DynamicSkybox.shader赋给天空盒材质
- 绑定主方向光到SkyboxController脚本
- 在Lighting窗口设置环境光源模式为Skybox
该方案支持实时昼夜循环、动态天气切换,在移动端可保持60FPS以上性能。如需更复杂效果可集成Altos插件实现体积云等高级特性
配置流程
资源准备:
- 导入HDR全景图(如PolyHaven免费资源)或六面体纹理。
材质创建:
- 选择
Skybox/Procedural
类型,绑定至Lighting窗口的Environment
面板。
动态控制:
-
通过C#脚本修改
RenderSettings.skybox
材质属性,如:csharpcsharp RenderSettings.skybox.SetFloat("_Rotation", Time.time * 0.1f);// 自动旋转
该技术显著提升了开放世界游戏的时空表现力,同时保持移动端高性能。现代URP进一步整合了云层扰动、大气散射等效果,扩展了程序化生成的可能性.
【从UnityURP开始探索游戏渲染】专栏-直达
(欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)