Files
Fishing2/Packages/com.waveharmonic.crest/Runtime/Scripts/Mask/MaskRenderer.cs
2026-01-31 00:32:49 +08:00

318 lines
9.0 KiB
C#

// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest
{
abstract partial class MaskRenderer
{
protected const string k_MaskColor = "_Crest_MaskColor";
protected const string k_MaskDepth = "_Crest_MaskDepth";
public static class ShaderIDs
{
public static readonly int s_WaterMaskTexture = Shader.PropertyToID("_Crest_WaterMaskTexture");
public static readonly int s_WaterMaskDepthTexture = Shader.PropertyToID("_Crest_WaterMaskDepthTexture");
}
public static MaskRenderer Instantiate(WaterRenderer water)
{
#pragma warning disable format
#if d_UnityHDRP
if (RenderPipelineHelper.IsHighDefinition)
{
return new MaskRendererHDRP(water);
}
else
#endif
#if d_UnityURP
if (RenderPipelineHelper.IsUniversal)
{
return new MaskRendererURP(water);
}
else
#endif
{
return new MaskRendererBIRP(water);
}
#pragma warning restore format
}
// For PortalRenderer.
public static System.Action s_OnAllocate;
public static System.Action s_OnRelease;
public static System.Action<RenderTextureDescriptor> s_OnReAllocate;
public MaskRenderer(WaterRenderer water)
{
_Water = water;
}
public bool Enabled => true; //_Water.Underwater.Enabled;
internal RenderTargetIdentifier _ColorRTI;
internal RenderTargetIdentifier _DepthRTI;
public RenderTextureDescriptor ColorDescriptor => ColorRT.descriptor;
public RenderTextureDescriptor DepthDescriptor => DepthRT.descriptor;
public abstract void OnBeginCameraRendering(Camera camera);
public abstract void OnEndCameraRendering(Camera camera);
public virtual void Enable()
{
}
public virtual void Disable()
{
}
public virtual void Destroy()
{
Release();
}
protected void UpdateColor(Texture color)
{
_ColorRTI = new(color, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
Shader.SetGlobalTexture(ShaderIDs.s_WaterMaskTexture, color);
}
protected void UpdateDepth(Texture depth)
{
_DepthRTI = new(depth, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
Shader.SetGlobalTexture(ShaderIDs.s_WaterMaskDepthTexture, depth);
}
//
// Pub/Sub
//
[System.Flags]
public enum MaskInput
{
None,
Zero = 1 << 0,
Color = 1 << 1,
Depth = 1 << 2,
Both = Color | Depth,
}
protected MaskInput _Inputs;
protected readonly WaterRenderer _Water;
internal readonly Utility.SortedList<int, IMaskProvider> _Providers = new(Helpers.DuplicateComparison);
internal readonly List<IMaskReceiver> _Receivers = new();
public interface IMaskProvider
{
MaskInput Allocate();
MaskInput Write(Camera camera);
void OnMaskPass(CommandBuffer commands, Camera camera, MaskRenderer mask);
}
public interface IMaskReceiver
{
MaskInput Allocate();
}
void Initialize()
{
_Inputs = MaskInput.None;
foreach (var receiver in _Receivers)
{
_Inputs |= receiver.Allocate();
}
}
internal void Add(IMaskReceiver receiver)
{
if (_Receivers.Contains(receiver))
{
return;
}
_Receivers.Add(receiver);
Initialize();
}
internal void Remove(IMaskReceiver receiver)
{
if (!_Receivers.Remove(receiver))
{
return;
}
Initialize();
}
internal void Add(int queue, IMaskProvider provider)
{
if (_Providers.Contains(provider))
{
return;
}
_Providers.Add(queue, provider);
Initialize();
}
internal void Remove(IMaskProvider provider)
{
if (!_Providers.Remove(provider))
{
return;
}
Initialize();
}
public void Execute(Camera camera, CommandBuffer commands)
{
foreach (var provider in _Providers)
{
if (provider.Value.Write(camera) == MaskInput.None)
{
continue;
}
provider.Value.OnMaskPass(commands, camera, this);
}
}
internal bool ShouldExecute(Camera camera)
{
var input = MaskInput.None;
foreach (var providers in _Providers)
{
input |= providers.Value.Write(camera);
}
return input != MaskInput.None;
}
}
// Holds common stuff for SRPs
abstract partial class MaskRenderer
{
internal RTHandle _ColorRTH;
internal RTHandle _DepthRTH;
// Null check due to U6 not being able to cast if null (Unity bug?).
public Texture ColorT => _ColorRTH?.rt;
public Texture DepthT => _DepthRTH?.rt;
public RTHandle ColorRTH => _ColorRTH;
public RTHandle DepthRTH => _DepthRTH;
public RenderTexture ColorRT => _ColorRTH;
public RenderTexture DepthRT => _DepthRTH;
public void ResetRenderTarget(CommandBuffer commands)
{
CoreUtils.SetRenderTarget(commands, ColorRTH, DepthRTH);
}
public void Allocate()
{
if (_Inputs.HasFlag(MaskInput.Color) && _ColorRTH == null)
{
_ColorRTH = RTHandles.Alloc
(
scaleFactor: Vector2.one,
slices: TextureXR.slices,
dimension: TextureXR.dimension,
depthBufferBits: DepthBits.None,
colorFormat: Helpers.GetCompatibleTextureFormat(GraphicsFormat.R16_SFloat, randomWrite: true),
enableRandomWrite: true,
useDynamicScale: true,
name: k_MaskColor
);
UpdateColor(_ColorRTH);
}
if (_Inputs.HasFlag(MaskInput.Depth) && _DepthRTH == null)
{
_DepthRTH = RTHandles.Alloc
(
scaleFactor: Vector2.one,
slices: TextureXR.slices,
dimension: TextureXR.dimension,
depthBufferBits: Rendering.GetDefaultDepthBufferBits(),
colorFormat: GraphicsFormat.None,
enableRandomWrite: false,
useDynamicScale: true,
name: k_MaskDepth
);
UpdateDepth(_DepthRTH);
}
s_OnAllocate?.Invoke();
}
public void ReAllocate(RenderTextureDescriptor descriptor)
{
// Shared settings. Enabling MSAA might be a good idea except cannot enable random
// writes. Having a raster shader to remove artifacts is a workaround.
// This looks safe to do as Unity's CopyDepthPass does the same.
descriptor.bindMS = false;
descriptor.msaaSamples = 1;
s_OnReAllocate?.Invoke(descriptor);
if (_Inputs.HasFlag(MaskInput.Depth))
{
descriptor.graphicsFormat = GraphicsFormat.None;
descriptor.depthBufferBits = (int)Rendering.GetDefaultDepthBufferBits();
if (RenderPipelineCompatibilityHelper.ReAllocateIfNeeded(ref _DepthRTH, descriptor, name: k_MaskDepth))
{
UpdateDepth(_DepthRTH);
}
}
if (_Inputs.HasFlag(MaskInput.Color))
{
// NOTE: Intel iGPU for Metal and DirectX both had issues with R16 (2021.11.18).
descriptor.graphicsFormat = Helpers.GetCompatibleTextureFormat(GraphicsFormat.R16_SFloat, randomWrite: true);
descriptor.depthStencilFormat = GraphicsFormat.None;
descriptor.depthBufferBits = 0;
descriptor.enableRandomWrite = true;
if (RenderPipelineCompatibilityHelper.ReAllocateIfNeeded(ref _ColorRTH, descriptor, name: k_MaskColor))
{
UpdateColor(_ColorRTH);
}
}
}
public void Release()
{
_ColorRTH?.Release();
_DepthRTH?.Release();
// Set to null possibly due to Initialize/Destroy overlap.
_ColorRTH = null;
_DepthRTH = null;
s_OnRelease?.Invoke();
}
}
}