Files

133 lines
4.3 KiB
C#

using System.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.RenderGraphModule.Util;
using UnityEngine.Rendering.Universal;
/// <summary>
/// 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).
/// </summary>
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<UniversalResourceData>();
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;
}
}
}