升级水插件
This commit is contained in:
@@ -0,0 +1,243 @@
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user