Add SkyboxEnvironmentUpdater: runtime sky reflection + ambient
New [ExecuteAlways] component that renders the skybox (or an optional override material) into a custom reflection cubemap and refreshes DynamicGI so smooth/metallic materials reflect the sky without a baked LightingData asset. Uses a deferred apply plus a synchronous warm-render so the ambient probe resolves after domain reload; runs the reflection capture as a stripped CameraType.Reflection to avoid the URP MIP-bind error. Exposes reflection intensity (0..4) and optional ambient override. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
1 parent
b74123188b
commit
deee9d6ca1
2 files changed
+166
No files matched your search
@@ -0,0 +1,164 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
/// <summary>
|
||||
/// Builds the environment reflection from the current Skybox in memory (renders
|
||||
/// the sky into a cubemap and assigns it as the Custom reflection), so
|
||||
/// smooth/metallic materials reflect the sky WITHOUT a baked LightingData.asset.
|
||||
/// Runs in Edit and Play mode ([ExecuteAlways]); drop it on one object per scene
|
||||
/// (or a prefab that lives in every scene) and you never have to assign a foreign
|
||||
/// LightingData again — also works for runtime-loaded / procedural scenes.
|
||||
///
|
||||
/// Why a rendered cubemap and not <see cref="DynamicGI.UpdateEnvironment"/>:
|
||||
/// in this project UpdateEnvironment does NOT produce a valid skybox reflection
|
||||
/// probe (a mirror stays black), so <see cref="RenderSettings.reflectionIntensity"/>
|
||||
/// had nothing to scale. Rendering the sky into a Custom reflection cubemap gives a
|
||||
/// real reflection that intensity can actually drive.
|
||||
///
|
||||
/// The skybox AMBIENT probe still comes from UpdateEnvironment — but it only convolves a
|
||||
/// non-zero probe once the sky has actually been rendered this session. Straight after a
|
||||
/// domain reload / scene load / play-start the sky hasn't rendered yet, so the probe is
|
||||
/// ZERO and the Environment intensity multiplier has nothing to scale ("doesn't take
|
||||
/// effect" until a manual re-enable). Fix: do one synchronous full-screen sky render to
|
||||
/// "warm" the sky right before UpdateEnvironment — deterministic, no waiting on renders.
|
||||
/// (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
|
||||
/// </summary>
|
||||
[ExecuteAlways]
|
||||
[DisallowMultipleComponent]
|
||||
public class SkyboxEnvironmentUpdater : MonoBehaviour
|
||||
{
|
||||
[Range(0f, 4f)]
|
||||
public float environmentLightingIntensity = 1;
|
||||
|
||||
[Header("Reflections")]
|
||||
|
||||
[Tooltip("Resolution of the generated sky reflection cubemap (power of two).")]
|
||||
public int reflectionResolution = 128;
|
||||
|
||||
[Tooltip("How strongly the sky reflection tints reflective/metallic surfaces. 1 = neutral; the Lighting window slider caps at 1, but higher values (via this field) over-drive the reflection for a glossier metal look. Only applied while Force Skybox Reflections is on.")]
|
||||
[Range(0f, 4f)]
|
||||
public float reflectionIntensity = 1f;
|
||||
|
||||
[Tooltip("Optional: render THIS sky material into the reflection cubemap instead of the scene's global Skybox. Leave empty to use RenderSettings.skybox. Only affects the reflection — the visible scene skybox is unchanged.")]
|
||||
public Material reflectionSkyOverride;
|
||||
|
||||
[System.NonSerialized] Cubemap _reflectionCube;
|
||||
|
||||
void OnDisable()
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
UnityEditor.EditorApplication.delayCall -= Apply;
|
||||
#endif
|
||||
if (!_reflectionCube)
|
||||
return;
|
||||
|
||||
if (RenderSettings.customReflectionTexture == _reflectionCube)
|
||||
RenderSettings.customReflectionTexture = null;
|
||||
|
||||
DestroyImmediate(_reflectionCube);
|
||||
|
||||
_reflectionCube = null;
|
||||
}
|
||||
|
||||
void OnValidate()
|
||||
{
|
||||
if (this && isActiveAndEnabled)
|
||||
OnEnable();
|
||||
else
|
||||
OnDisable();
|
||||
}
|
||||
|
||||
void OnEnable()
|
||||
{
|
||||
if (!RenderSettings.skybox && !reflectionSkyOverride)
|
||||
return;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
if (!Application.isPlaying)
|
||||
{
|
||||
UnityEditor.EditorApplication.delayCall -= Apply;
|
||||
UnityEditor.EditorApplication.delayCall += Apply;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
StartCoroutine(ApplyNextFrame());
|
||||
}
|
||||
|
||||
IEnumerator ApplyNextFrame()
|
||||
{
|
||||
yield return null;
|
||||
Apply();
|
||||
}
|
||||
|
||||
void Apply()
|
||||
{
|
||||
if (!this || !isActiveAndEnabled)
|
||||
return;
|
||||
|
||||
if (!RenderSettings.skybox && !reflectionSkyOverride)
|
||||
return;
|
||||
|
||||
RenderSettings.ambientIntensity = environmentLightingIntensity;
|
||||
|
||||
var camGo = new GameObject("__SkyboxEnvCam") { hideFlags = HideFlags.HideAndDontSave };
|
||||
try
|
||||
{
|
||||
var cam = camGo.AddComponent<Camera>();
|
||||
cam.enabled = false;
|
||||
cam.cullingMask = 0;
|
||||
cam.clearFlags = CameraClearFlags.Skybox;
|
||||
cam.cameraType = CameraType.Reflection;
|
||||
|
||||
var camData = camGo.AddComponent<UniversalAdditionalCameraData>();
|
||||
camData.renderPostProcessing = false;
|
||||
camData.requiresDepthTexture = false;
|
||||
camData.requiresColorTexture = false;
|
||||
camData.renderShadows = false;
|
||||
|
||||
if (reflectionSkyOverride)
|
||||
camGo.AddComponent<Skybox>().material = reflectionSkyOverride;
|
||||
|
||||
camGo.transform.position = transform.position;
|
||||
|
||||
var rt = RenderTexture.GetTemporary(64, 64, 16);
|
||||
cam.targetTexture = rt;
|
||||
cam.Render();
|
||||
cam.targetTexture = null;
|
||||
RenderTexture.ReleaseTemporary(rt);
|
||||
|
||||
EnsureReflectionCubemap();
|
||||
cam.RenderToCubemap(_reflectionCube);
|
||||
}
|
||||
finally
|
||||
{
|
||||
DestroyImmediate(camGo);
|
||||
}
|
||||
|
||||
RenderSettings.defaultReflectionMode = DefaultReflectionMode.Custom;
|
||||
RenderSettings.customReflectionTexture = _reflectionCube;
|
||||
RenderSettings.reflectionIntensity = reflectionIntensity;
|
||||
|
||||
RenderSettings.ambientMode = AmbientMode.Skybox;
|
||||
DynamicGI.UpdateEnvironment();
|
||||
}
|
||||
|
||||
void EnsureReflectionCubemap()
|
||||
{
|
||||
int size = Mathf.Max(16, reflectionResolution);
|
||||
|
||||
if (!_reflectionCube || _reflectionCube.width != size)
|
||||
{
|
||||
if (_reflectionCube)
|
||||
{
|
||||
DestroyImmediate(_reflectionCube);
|
||||
}
|
||||
|
||||
_reflectionCube = new Cubemap(size, TextureFormat.RGBA32, true)
|
||||
{
|
||||
name = "SkyboxReflection (generated)"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8ad6962cd13b2914a82ab60384f37a3b
|
||||
Reference in new issue
Block a user