Files
Fishing2/Packages/com.waveharmonic.crest/Runtime/Scripts/Surface/SurfaceRenderer.Legacy.cs
2026-03-05 00:14:42 +08:00

244 lines
9.6 KiB
C#

// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
partial class SurfaceRenderer
{
partial class ShaderIDs
{
public static readonly int s_DummyTarget = Shader.PropertyToID("_Crest_DummyTarget");
public static readonly int s_WorldToShadow = Shader.PropertyToID("_Crest_WorldToShadow");
public static class Unity
{
public static readonly int s_BuiltInSurface = Shader.PropertyToID("_BUILTIN_Surface");
public static readonly int s_BuiltInTransparentReceiveShadows = Shader.PropertyToID("_BUILTIN_TransparentReceiveShadows");
}
}
CommandBuffer _DrawWaterSurfaceBuffer;
void OnBeginCameraRenderingLegacy(Camera camera)
{
_Water.UpdateMatrices(camera);
#if UNITY_EDITOR
if (!Application.isPlaying)
{
OnPreRenderWaterLevelDepthTexture(camera);
}
#endif
// Everything from here depends on the material being transparent.
if (!IsTransparent(Material))
{
return;
}
camera.depthTextureMode |= DepthTextureMode.Depth;
_DrawWaterSurfaceBuffer ??= new() { name = WaterRenderer.k_DrawWater };
_DrawWaterSurfaceBuffer.Clear();
// Create or update RT.
_Water.OnBeginCameraOpaqueTexture(camera);
SetUpShadows(camera);
if (_Water.RenderBeforeTransparency)
{
Draw(_DrawWaterSurfaceBuffer, camera);
}
camera.AddCommandBuffer(CameraEvent.BeforeForwardAlpha, _DrawWaterSurfaceBuffer);
}
void OnEndCameraRenderingLegacy(Camera camera)
{
#if UNITY_EDITOR
if (!Application.isPlaying)
{
OnPostRenderWaterLevelDepthTexture(camera);
}
#endif
_Water.OnEndCameraOpaqueTexture(camera);
if (_DrawWaterSurfaceBuffer != null)
{
camera.RemoveCommandBuffer(CameraEvent.BeforeForwardAlpha, _DrawWaterSurfaceBuffer);
}
if (QualitySettings.shadows != ShadowQuality.Disable && _Water.PrimaryLight != null)
{
if (_ScreenSpaceShadowMapBuffer != null)
{
_Water.PrimaryLight.RemoveCommandBuffer(LightEvent.AfterScreenspaceMask, _ScreenSpaceShadowMapBuffer);
}
if (_DeferredShadowMapBuffer != null)
{
_Water.PrimaryLight.RemoveCommandBuffer(LightEvent.AfterShadowMap, _DeferredShadowMapBuffer);
}
}
Shader.SetGlobalTexture(Crest.ShaderIDs.Unity.s_ShadowMapTexture, Texture2D.whiteTexture);
}
// Draws the water surface including lighting.
internal void Draw(CommandBuffer commands, Camera camera)
{
commands.BeginSample(k_DrawWaterSurface);
CoreUtils.SetRenderTarget(commands, BuiltinRenderTextureType.CameraTarget);
var sun = RenderSettings.sun;
if (sun != null)
{
// Unity does not set up lighting for us so we will get the last value which could incorrect.
// SetGlobalColor is just an alias for SetGlobalVector (no color space conversion like Material.SetColor):
// https://docs.unity3d.com/2017.4/Documentation/ScriptReference/Shader.SetGlobalColor.html
commands.SetGlobalVector(Crest.ShaderIDs.Unity.s_LightColor0, sun.FinalColor());
commands.SetGlobalVector(Crest.ShaderIDs.Unity.s_WorldSpaceLightPos0, -sun.transform.forward);
}
// Always enabled.
commands.SetShaderKeyword("LIGHTPROBE_SH", true);
UpdateChunkVisibility(camera);
foreach (var chunk in Chunks)
{
var renderer = chunk.Rend;
if (chunk.Rend == null)
{
continue;
}
if (!chunk._Visible)
{
continue;
}
if (chunk._Culled)
{
continue;
}
if (!chunk._WaterDataHasBeenBound)
{
chunk.Bind();
}
var mpb = new PropertyWrapperMPB(chunk._MaterialPropertyBlock);
mpb.SetSHCoefficients(chunk.transform.position);
commands.DrawMesh(chunk._Mesh, chunk.transform.localToWorldMatrix, renderer.sharedMaterial, 0, 0, chunk._MaterialPropertyBlock);
}
commands.EndSample(k_DrawWaterSurface);
}
}
partial class SurfaceRenderer
{
Material _ForceShadowsMaterial;
ComputeBuffer _ShadowMatrixBuffer;
readonly Matrix4x4[] _ShadowMatrixDefaults = { Matrix4x4.zero, Matrix4x4.zero, Matrix4x4.zero, Matrix4x4.zero };
Material _CaptureShadowMatrices;
CommandBuffer _DeferredShadowMapBuffer;
CommandBuffer _ScreenSpaceShadowMapBuffer;
void LegacyOnEnable()
{
_ShadowMatrixBuffer ??= new(4, sizeof(float) * 16, ComputeBufferType.Structured);
_ShadowMatrixBuffer.SetData(_ShadowMatrixDefaults);
}
void LegacyOnDisable()
{
_ShadowMatrixBuffer?.Dispose();
_ShadowMatrixBuffer = null;
}
void SetUpShadows(Camera camera)
{
if (QualitySettings.shadows == ShadowQuality.Disable || _Water.PrimaryLight == null)
{
return;
}
var transform = camera.transform;
if (_ForceShadowsMaterial == null)
{
_ForceShadowsMaterial = new Material(WaterResources.Instance.Shaders._ForceShadows);
}
// Force shadows, as Unity ignores transparent shadow receivers, otherwise shadow
// passes will skip if caster or receiver out of view. ShadowLod also depends on this.
Graphics.RenderMesh
(
new(_ForceShadowsMaterial)
{
receiveShadows = true,
shadowCastingMode = ShadowCastingMode.Off,
},
mesh: Helpers.QuadMesh,
submeshIndex: 0,
objectToWorld: QualitySettings.shadowProjection == ShadowProjection.StableFit
? Matrix4x4.TRS(transform.position + transform.forward, Quaternion.LookRotation(transform.forward), Vector3.one * 0.01f)
// TODO: render water level inputs to support shadows for varying water level.
// Sort of works for close fit. But will decrease shadow quality.
: Matrix4x4.TRS(Vector3.up * _Water.SeaLevel, Quaternion.LookRotation(-Vector3.up), Vector3.one * 100f)
);
if (!Material.IsKeywordEnabled("_BUILTIN_TRANSPARENT_RECEIVES_SHADOWS"))
{
return;
}
if (_CaptureShadowMatrices == null)
{
_CaptureShadowMatrices = new Material(WaterResources.Instance.Shaders._CaptureShadowMatrices);
}
// Used ComputeBuffer must always be bound!
Shader.SetGlobalBuffer(ShaderIDs.s_WorldToShadow, _ShadowMatrixBuffer);
// Capture shadow matrices, as Unity clears all but the first cascade.
_ScreenSpaceShadowMapBuffer ??= new() { name = WaterRenderer.k_DrawWater };
_ScreenSpaceShadowMapBuffer.Clear();
// Cannot set target to None, as it will make some UI black (Unity bug?).
_ScreenSpaceShadowMapBuffer.GetTemporaryRT(ShaderIDs.s_DummyTarget, new RenderTextureDescriptor(4, 4));
CoreUtils.SetRenderTarget(_ScreenSpaceShadowMapBuffer, ShaderIDs.s_DummyTarget);
// Setting the buffer (SetGlobalBuffer) and writing to it only worked with Metal.
// For other graphics APIs, had to use SetRandomWriteTarget.
_ScreenSpaceShadowMapBuffer.ClearRandomWriteTargets();
_ScreenSpaceShadowMapBuffer.SetRandomWriteTarget(1, _ShadowMatrixBuffer);
_ScreenSpaceShadowMapBuffer.DrawProcedural(Matrix4x4.identity, _CaptureShadowMatrices, 0, MeshTopology.Triangles, 3);
_ScreenSpaceShadowMapBuffer.ClearRandomWriteTargets();
_ScreenSpaceShadowMapBuffer.ReleaseTemporaryRT(ShaderIDs.s_DummyTarget);
_Water.PrimaryLight.AddCommandBuffer(LightEvent.AfterScreenspaceMask, _ScreenSpaceShadowMapBuffer);
// Make shadow map available to transparents.
// Call this regardless of rendering path as it has no negative consequences for forward.
_DeferredShadowMapBuffer ??= new() { name = WaterRenderer.k_DrawWater };
_DeferredShadowMapBuffer.Clear();
_DeferredShadowMapBuffer.SetGlobalTexture(Crest.ShaderIDs.Unity.s_ShadowMapTexture, BuiltinRenderTextureType.CurrentActive);
_Water.PrimaryLight.AddCommandBuffer(LightEvent.AfterShadowMap, _DeferredShadowMapBuffer);
// Set up shadow keywords.
_DrawWaterSurfaceBuffer.SetKeyword(new("SHADOWS_SINGLE_CASCADE"), QualitySettings.shadowCascades == 1);
_DrawWaterSurfaceBuffer.SetKeyword(new("SHADOWS_SPLIT_SPHERES"), QualitySettings.shadowProjection == ShadowProjection.StableFit);
_DrawWaterSurfaceBuffer.SetKeyword(new("SHADOWS_SOFT"), QualitySettings.shadows == ShadowQuality.All);
}
}
}