升级水插件

This commit is contained in:
2026-01-08 22:30:55 +08:00
parent febff82d24
commit ca68084264
415 changed files with 18138 additions and 7134 deletions

View File

@@ -1,9 +1,8 @@
// Crest Water System
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
@@ -27,29 +26,22 @@ namespace WaveHarmonic.Crest
[@ExecuteDuringEditMode]
sealed class WaterChunkRenderer : ManagedBehaviour<WaterRenderer>
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 0;
#pragma warning restore 414
[SerializeField]
internal bool _DrawRenderBounds = false;
static class ShaderIDs
{
public static readonly int s_ChunkMeshScaleAlpha = Shader.PropertyToID("_Crest_ChunkMeshScaleAlpha");
public static readonly int s_ChunkGeometryGridWidth = Shader.PropertyToID("_Crest_ChunkGeometryGridWidth");
public static readonly int s_ChunkFarNormalsWeight = Shader.PropertyToID("_Crest_ChunkFarNormalsWeight");
public static readonly int s_ChunkNormalScrollSpeed = Shader.PropertyToID("_Crest_ChunkNormalScrollSpeed");
public static readonly int s_ChunkMeshScaleAlphaSource = Shader.PropertyToID("_Crest_ChunkMeshScaleAlphaSource");
public static readonly int s_ChunkGeometryGridWidthSource = Shader.PropertyToID("_Crest_ChunkGeometryGridWidthSource");
}
internal const string k_UpdateMeshBoundsMarker = "Crest.WaterChunkRenderer.UpdateMeshBounds";
internal Bounds _BoundsLocal;
Mesh _Mesh;
static readonly Unity.Profiling.ProfilerMarker s_UpdateMeshBoundsMarker = new(k_UpdateMeshBoundsMarker);
internal Transform _Transform;
internal Mesh _Mesh;
public Renderer Rend { get; private set; }
internal MaterialPropertyBlock _MaterialPropertyBlock;
Matrix4x4 _CurrentObjectToWorld;
Matrix4x4 _PreviousObjectToWorld;
internal Material _MotionVectorMaterial;
internal int _SortingOrder;
internal int _SiblingIndex;
internal Rect _UnexpandedBoundsXZ = new();
public Rect UnexpandedBoundsXZ => _UnexpandedBoundsXZ;
@@ -72,24 +64,17 @@ namespace WaveHarmonic.Crest
public static List<IReportsHeight> HeightReporters { get; } = new();
public static List<IReportsDisplacement> DisplacementReporters { get; } = new();
private protected override System.Action<WaterRenderer> OnLateUpdateMethod => OnLateUpdate;
private protected override void Initialize()
// There is a 1-frame delay with Initialized in edit mode due to setting
// enableInEditMode in EditorApplication.update. This only really affect this
// component as it is instantiate via script, and is partial driven externally.
// So instead, call this after instantiation.
internal void Initialize(int index, Renderer renderer, Mesh mesh)
{
base.Initialize();
_MaterialPropertyBlock ??= new();
if (Rend == null)
{
Rend = GetComponent<Renderer>();
}
if (_Mesh == null)
{
// Meshes are cloned so it is safe to use sharedMesh in play mode. We need clones to modify the render bounds.
_Mesh = GetComponent<MeshFilter>().sharedMesh;
}
_LodIndex = index;
Rend = renderer;
_Mesh = mesh;
_PreviousObjectToWorld = _CurrentObjectToWorld = transform.localToWorldMatrix;
_Transform = transform;
}
private protected override void OnStart()
@@ -99,97 +84,95 @@ namespace WaveHarmonic.Crest
UpdateMeshBounds();
}
void OnLateUpdate(WaterRenderer water)
internal void UpdateMeshBounds(WaterRenderer water, SurfaceRenderer surface)
{
_WaterDataHasBeenBound = false;
var count = surface.TimeSliceBoundsUpdateFrameCount;
// Time slice update to distribute the load.
if (!(transform.GetSiblingIndex() % water.TimeSliceBoundsUpdateFrameCount != Time.frameCount % water.Chunks.Count % water.TimeSliceBoundsUpdateFrameCount))
if (count <= 1 || !(_SiblingIndex % count != Time.frameCount % surface.Chunks.Count % count))
{
// This needs to be called on Update because the bounds depend on transform scale which can change. Also OnWillRenderObject depends on
// the bounds being correct. This could however be called on scale change events, but would add slightly more complexity.
UpdateMeshBounds();
}
}
// Update chunk shader data.
_MaterialPropertyBlock ??= new();
// FIXME: Sometimes thrown.
// NullReferenceException: Object reference not set to an instance of an object
// WaveHarmonic.Crest.WaterChunkRenderer.OnLateUpdate(WaveHarmonic.Crest.WaterRenderer water)(at Packages/com.waveharmonic.crest/Runtime/Scripts/Surface/WaterChunkRenderer.cs:119)
// WaveHarmonic.Crest.WaterRenderer.LateUpdate()(at Packages/com.waveharmonic.crest/Runtime/Scripts/WaterRenderer.cs:733)
if (Rend == null)
bool ShouldRender(bool culled)
{
// Is visible to camera.
if (!_Visible)
{
Rend = GetComponent<Renderer>();
return false;
}
Rend.GetPropertyBlock(_MaterialPropertyBlock);
_MaterialPropertyBlock.SetInteger(Lod.ShaderIDs.s_LodIndex, _LodIndex);
var data = water._PerCascadeInstanceData.Current[_LodIndex];
_MaterialPropertyBlock.SetFloat(ShaderIDs.s_ChunkMeshScaleAlpha, data._MeshScaleLerp);
_MaterialPropertyBlock.SetFloat(ShaderIDs.s_ChunkGeometryGridWidth, data._GeometryGridWidth);
_MaterialPropertyBlock.SetFloat(ShaderIDs.s_ChunkFarNormalsWeight, data._FarNormalsWeight);
_MaterialPropertyBlock.SetVector(ShaderIDs.s_ChunkNormalScrollSpeed, data._NormalScrollSpeeds);
data = water._PerCascadeInstanceData.Previous(1)[_LodIndex];
_MaterialPropertyBlock.SetFloat(ShaderIDs.s_ChunkMeshScaleAlphaSource, data._MeshScaleLerp);
_MaterialPropertyBlock.SetFloat(ShaderIDs.s_ChunkGeometryGridWidthSource, data._GeometryGridWidth);
Rend.SetPropertyBlock(_MaterialPropertyBlock);
#if UNITY_6000_0_OR_NEWER
if (Application.isPlaying && RenderPipelineHelper.IsUniversal && water.WriteMotionVectors)
// If including culling, is it culled.
if (culled && _Culled)
{
var material = water._MotionVectorsMaterial;
var parameters = new RenderParams(material)
{
motionVectorMode = MotionVectorGenerationMode.Object,
material = material,
matProps = _MaterialPropertyBlock,
worldBounds = Rend.bounds,
layer = water.Layer,
receiveShadows = false,
shadowCastingMode = ShadowCastingMode.Off,
lightProbeUsage = LightProbeUsage.Off,
reflectionProbeUsage = ReflectionProbeUsage.Off,
};
if (_Mesh == null)
{
_Mesh = GetComponent<MeshFilter>().sharedMesh;
}
Graphics.RenderMesh(parameters, _Mesh, 0, transform.localToWorldMatrix, _PreviousObjectToWorld);
_PreviousObjectToWorld = transform.localToWorldMatrix;
return false;
}
#endif
return true;
}
internal void OnLateUpdate()
{
_PreviousObjectToWorld = _CurrentObjectToWorld;
_CurrentObjectToWorld = _Transform.localToWorldMatrix;
}
internal void RenderMotionVectors(SurfaceRenderer surface, Camera camera)
{
if (!ShouldRender(culled: true))
{
return;
}
// RenderMesh will copy properties immediately, thus we need them bound.
if (!_WaterDataHasBeenBound)
{
Bind();
}
var material = MaterialOverridden ? _MotionVectorMaterial : surface._MotionVectorMaterial;
var parameters = new RenderParams(material)
{
motionVectorMode = MotionVectorGenerationMode.Object,
material = material,
matProps = _MaterialPropertyBlock,
worldBounds = Rend.bounds,
layer = surface.Layer,
receiveShadows = false,
shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.Off,
lightProbeUsage = UnityEngine.Rendering.LightProbeUsage.Off,
reflectionProbeUsage = UnityEngine.Rendering.ReflectionProbeUsage.Off,
camera = camera,
};
Graphics.RenderMesh(parameters, _Mesh, 0, _CurrentObjectToWorld, _PreviousObjectToWorld);
}
void UpdateMeshBounds()
{
UnityEngine.Profiling.Profiler.BeginSample("Crest.WaterChunkRenderer.UpdateMeshBounds");
s_UpdateMeshBoundsMarker.Begin(this);
var bounds = _Mesh.bounds;
if (WaterBody.WaterBodies.Count > 0)
{
_UnexpandedBoundsXZ = ComputeBoundsXZ(transform, ref _BoundsLocal);
_UnexpandedBoundsXZ = ComputeBoundsXZ(_Transform, bounds);
}
var newBounds = _BoundsLocal;
ExpandBoundsForDisplacements(transform, ref newBounds);
bounds = ExpandBoundsForDisplacements(_Transform, bounds);
// FIXME: Fixes a crash which was reported twice. Could not reproduce.
// NullReferenceException: Object reference not set to an instance of an object.
// at WaveHarmonic.Crest.WaterChunkRenderer.UpdateMeshBounds()[0x00000] in < 00000000000000000000000000000000 >:0
// at WaveHarmonic.Crest.WaterChunkRenderer.OnUpdate(WaveHarmonic.Crest.WaterRenderer water)[0x00000] in < 00000000000000000000000000000000 >:0
// at WaveHarmonic.Crest.WaterRenderer.Update()[0x00000] in < 00000000000000000000000000000000 >:0
if (_Mesh == null)
{
_Mesh = GetComponent<MeshFilter>().sharedMesh;
}
Rend.localBounds = bounds;
_Mesh.bounds = newBounds;
UnityEngine.Profiling.Profiler.EndSample();
s_UpdateMeshBoundsMarker.End();
}
public static Rect ComputeBoundsXZ(Transform transform, ref Bounds bounds)
public static Rect ComputeBoundsXZ(Transform transform, Bounds bounds)
{
// Since chunks are axis-aligned it is safe to rotate the bounds.
var center = transform.rotation * bounds.center * transform.lossyScale.x + transform.position;
@@ -201,31 +184,14 @@ namespace WaveHarmonic.Crest
};
}
static Camera s_CurrentCamera = null;
static void BeginCameraRendering(ScriptableRenderContext context, Camera camera)
{
// Camera.current is only supported in the built-in pipeline. This provides the current camera for
// OnWillRenderObject for SRPs. BeginCameraRendering is called for each active camera in every frame.
// OnWillRenderObject is called after BeginCameraRendering for the current camera so this works.
s_CurrentCamera = camera;
}
// Used by the water mask system if we need to render the water mask in situations
// where the water itself doesn't need to be rendered or has otherwise been disabled
internal void Bind(Camera camera)
internal void Bind()
{
_MaterialPropertyBlock = _Water.Surface._PerCascadeMPB.Current[_LodIndex];
Rend.SetPropertyBlock(_MaterialPropertyBlock);
_WaterDataHasBeenBound = true;
if (Rend == null)
{
return;
}
if (!MaterialOverridden && Rend.sharedMaterial != _Water.Material)
{
Rend.sharedMaterial = _Water.Material;
}
}
void OnDestroy()
@@ -236,22 +202,21 @@ namespace WaveHarmonic.Crest
// Called when visible to a camera
void OnWillRenderObject()
{
// Camera.current is only supported in built-in pipeline.
if (RenderPipelineHelper.IsLegacy && Camera.current != null)
{
s_CurrentCamera = Camera.current;
}
// If only the game view is visible, this reference will be dropped for SRP on recompile.
if (s_CurrentCamera == null)
if (Rend == null)
{
return;
}
// Depth texture is used by water shader for transparency/depth fog, and for fading out foam at shoreline.
s_CurrentCamera.depthTextureMode |= DepthTextureMode.Depth;
if (!MaterialOverridden && Rend.sharedMaterial != _Water.Surface.Material)
{
Rend.sharedMaterial = _Water.Surface.Material;
_MotionVectorMaterial = _Water.Surface._MotionVectorMaterial;
}
Bind(s_CurrentCamera);
if (!_WaterDataHasBeenBound)
{
Bind();
}
if (_DrawRenderBounds)
{
@@ -261,8 +226,11 @@ namespace WaveHarmonic.Crest
// this is called every frame because the bounds are given in world space and depend on the transform scale, which
// can change depending on view altitude
public void ExpandBoundsForDisplacements(Transform transform, ref Bounds bounds)
public Bounds ExpandBoundsForDisplacements(Transform transform, Bounds bounds)
{
var extents = bounds.extents;
var center = bounds.center;
var scale = transform.lossyScale;
var rotation = transform.rotation;
@@ -276,14 +244,10 @@ namespace WaveHarmonic.Crest
boundsY += 5f;
}
var extents = bounds.extents;
// Extend bounds by global waves.
bounds.extents = new(extents.x + expandXZ, boundsY, extents.z + expandXZ);
extents = bounds.extents;
var center = bounds.center;
var size = bounds.size;
extents.x += expandXZ;
extents.y += boundsY;
extents.z += expandXZ;
// Get XZ bounds. Doing this manually bypasses updating render bounds call.
Rect rect;
@@ -291,7 +255,7 @@ namespace WaveHarmonic.Crest
var p1 = transform.position;
var p2 = rotation * new Vector3(center.x, 0f, center.z);
var s1 = scale;
var s2 = rotation * new Vector3(size.x, 0f, size.z);
var s2 = rotation * (extents.XNZ(0f) * 2f);
rect = new(0, 0, Mathf.Abs(s1.x * s2.x), Mathf.Abs(s1.z * s2.z))
{
@@ -319,7 +283,9 @@ namespace WaveHarmonic.Crest
expandXZ = boundsPadding / scale.x;
boundsY = totalVertical;
bounds.extents = new(extents.x + expandXZ, extents.y + boundsY, extents.z + expandXZ);
extents.x += expandXZ;
extents.y += boundsY;
extents.z += expandXZ;
}
// Expand and offset bounds by height.
@@ -342,35 +308,23 @@ namespace WaveHarmonic.Crest
maximumWaterLevelBounds *= 0.5f;
boundsY = minimumWaterLevelBounds + maximumWaterLevelBounds;
extents = bounds.extents;
bounds.extents = new(extents.x, extents.y + boundsY, extents.z);
extents.y += boundsY;
bounds.extents = extents;
var offset = maximumWaterLevelBounds - minimumWaterLevelBounds;
center = bounds.center;
bounds.center = new(center.x, center.y + offset, center.z);
center.y += offset;
bounds.center = center;
}
}
public void SetInstanceData(int lodIndex)
{
_LodIndex = lodIndex;
return bounds;
}
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
static void InitStatics()
{
// Init here from 2019.3 onwards
s_CurrentCamera = null;
HeightReporters.Clear();
DisplacementReporters.Clear();
}
[RuntimeInitializeOnLoadMethod]
static void RunOnStart()
{
RenderPipelineManager.beginCameraRendering -= BeginCameraRendering;
RenderPipelineManager.beginCameraRendering += BeginCameraRendering;
}
}
static class BoundsHelper