news 2026/2/25 2:52:04

【光照】UnityURP[天空盒]原理与[动态天空盒]实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【光照】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关键代码实现昼夜交替:

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模板,定义天空球体顶点着色器计算世界空间坐标,片段着色器实现颜色混合逻辑。关键参数包括:

hlsl

float3 _SunDirection;

float4 _DayColor, _NightColor;

float _HorizonSharpness;

‌昼夜控制机制‌

通过_SunDirection.y值判断昼夜状态,结合smoothstep函数实现平滑过渡。太阳位置由主光源方向控制,月亮位置取反方向。

完整代码实现

DynamicSkybox.shader

Shader "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

using 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

// 云噪声生成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材质属性,如:

csharp

RenderSettings.skybox.SetFloat("_Rotation", Time.time * 0.1f);// 自动旋转

该技术显著提升了开放世界游戏的时空表现力,同时保持移动端高性能。现代URP进一步整合了云层扰动、大气散射等效果,扩展了程序化生成的可能性.

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/23 21:38:34

浅谈InheritableThreadLocal---线程可继承的小书包

前文中我们讲过ThreadLocal,相当于是每个线程有一个小书包,线程之间的小书包是隔离的,只存放了属于当前线程自己的变量,因此不会发生数据安全的问题。(前文博客浅谈ThreadLocal----每个线程一个小书包 https://www.cnb…

作者头像 李华
网站建设 2026/2/23 9:05:37

Jellyfin Android TV客户端音频播放异常问题深度解析

作为一款优秀的开源媒体服务器客户端,Jellyfin Android TV在音频播放方面偶尔会出现一些令人困扰的问题。很多用户反馈在播放音频内容时,会遇到突然中断、音质异常或无法正常播放等情况。今天咱们就来深入分析这个问题的来龙去脉。 【免费下载链接】jell…

作者头像 李华
网站建设 2026/2/24 21:40:13

CTR预测系统构建实战:从FM到DeepFM的推荐算法演进之路

CTR预测系统构建实战:从FM到DeepFM的推荐算法演进之路 【免费下载链接】d2l-en d2l-ai/d2l-en: 是一个基于 Python 的深度学习教程,它使用了 SQLite 数据库存储数据。适合用于学习深度学习,特别是对于需要使用 Python 和 SQLite 数据库的场景…

作者头像 李华
网站建设 2026/2/22 22:50:20

从零玩转RT-Thread(22):定时器底层机制揭秘

由于定时器模拟实现的一些机制,将导致我们在使用时容现某些低级错误。为了更好地理解这些错误,本小节介绍RT-Thread定时器的基本设计原理。 注:本小节只对其设计做比较简单的介绍,目的是更好地掌握API的使用。 定时器是如何被触…

作者头像 李华
网站建设 2026/2/24 2:30:31

B站缓存视频转换完整教程:m4s-converter高效管理本地视频

B站缓存视频转换完整教程:m4s-converter高效管理本地视频 【免费下载链接】m4s-converter 将bilibili缓存的m4s转成mp4(读PC端缓存目录) 项目地址: https://gitcode.com/gh_mirrors/m4/m4s-converter 你是否遇到过这样的情况:精心缓存的B站视频突…

作者头像 李华