using System.Collections; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.Rendering.RenderGraphModule; using UnityEngine.Rendering.RenderGraphModule.Util; using UnityEngine.Rendering.Universal; /// /// Scene-local "altered perception" fullscreen effect (Custom/LiminalPerception) for the main camera. /// The pass is injected only while this component is enabled, so the shared renderer asset and the /// other scenes are untouched. Fade it in/out from UnityEvents; while active, its intensity slowly /// swells and recedes in irregular waves. /// Photosensitivity: the shader and the swell only use slow, smooth variations (no flashes). /// public class LiminalPerception : MonoBehaviour { [SerializeField] private Material _material; [SerializeField, Range(0f, 1f)] private float _initialWeight = 0f; [SerializeField, Min(0f)] private float _fadeDuration = 4f; [Tooltip("Speed of the irregular intensity waves (lower = slower)")] [SerializeField, Min(0f)] private float _swellSpeed = 0.05f; private static readonly int WeightId = Shader.PropertyToID("_Weight"); private static readonly int SwellId = Shader.PropertyToID("_Swell"); private LiminalPerceptionPass _pass; private Material _instance; private float _weight; private Coroutine _fade; private float _seed; private void OnEnable() { if (!_material) return; _instance = new Material(_material) { hideFlags = HideFlags.HideAndDontSave }; _pass = new LiminalPerceptionPass(_instance); _weight = _initialWeight; _seed = Random.Range(0f, 100f); RenderPipelineManager.beginCameraRendering += OnBeginCamera; } private void OnDisable() { RenderPipelineManager.beginCameraRendering -= OnBeginCamera; if (_instance) Destroy(_instance); _instance = null; _pass = null; } public void FadeIn() => FadeTo(1f); public void FadeOut() => FadeTo(0f); public void FadeTo(float weight) { if (_fade != null) StopCoroutine(_fade); _fade = StartCoroutine(Fade(Mathf.Clamp01(weight))); } private IEnumerator Fade(float target) { float start = _weight; for (float t = 0f; t < _fadeDuration; t += Time.deltaTime) { _weight = Mathf.Lerp(start, target, Mathf.SmoothStep(0f, 1f, t / _fadeDuration)); yield return null; } _weight = target; _fade = null; } private void Update() { if (!_instance) return; // Irregular waves: mostly calm, sometimes a slow swell lasting several seconds float noise = Mathf.PerlinNoise(Time.time * _swellSpeed, _seed); float swell = Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(0.35f, 0.75f, noise)); _instance.SetFloat(WeightId, _weight); _instance.SetFloat(SwellId, swell); } private void OnBeginCamera(ScriptableRenderContext context, Camera camera) { if (_weight <= 0.001f || camera.cameraType != CameraType.Game || !camera.CompareTag("MainCamera")) return; camera.GetUniversalAdditionalCameraData().scriptableRenderer.EnqueuePass(_pass); } private class LiminalPerceptionPass : ScriptableRenderPass { private readonly Material _material; public LiminalPerceptionPass(Material material) { _material = material; renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing; requiresIntermediateTexture = true; } public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData) { UniversalResourceData resources = frameData.Get(); if (resources.isActiveTargetBackBuffer) return; TextureHandle source = resources.activeColorTexture; TextureDesc desc = renderGraph.GetTextureDesc(source); desc.name = "_LiminalPerception"; desc.clearBuffer = false; TextureHandle destination = renderGraph.CreateTexture(desc); renderGraph.AddBlitPass(new RenderGraphUtils.BlitMaterialParameters(source, destination, _material, 0), "Liminal Perception"); resources.cameraColor = destination; } } }