using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
///
/// Builds the environment reflection from the current Skybox in memory (renders the sky into a cubemap
/// assigned 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 —
/// also works for runtime-loaded / procedural scenes.
///
/// Why a rendered cubemap and not : in this project
/// UpdateEnvironment does NOT produce a valid skybox reflection probe (a mirror stays black), so
/// 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: one synchronous
/// full-screen sky render "warms" the sky right before UpdateEnvironment — deterministic, no waiting on
/// renders. (RenderToCubemap does NOT warm the ambient path; a normal Camera.Render does.)
///
[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();
cam.enabled = false;
cam.cullingMask = 0;
cam.clearFlags = CameraClearFlags.Skybox;
cam.cameraType = CameraType.Reflection;
var camData = camGo.AddComponent();
camData.renderPostProcessing = false;
camData.requiresDepthTexture = false;
camData.requiresColorTexture = false;
camData.renderShadows = false;
if (reflectionSkyOverride)
camGo.AddComponent().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)"
};
}
}
}