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 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 : /// 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: 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.) /// [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)" }; } } }