还原水插件
This commit is contained in:
@@ -5,19 +5,10 @@ using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using WaveHarmonic.Crest.Internal;
|
||||
using WaveHarmonic.Crest.Utility;
|
||||
|
||||
namespace WaveHarmonic.Crest
|
||||
{
|
||||
enum WaterMeshType
|
||||
{
|
||||
[Tooltip("Chunks implemented as a clip-map.")]
|
||||
Chunks,
|
||||
|
||||
[Tooltip("A single quad.\n\nOptimal for demanding platforms like mobile. Displacement will only contribute to normals.")]
|
||||
Quad,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Renders the water surface.
|
||||
/// </summary>
|
||||
@@ -36,12 +27,6 @@ namespace WaveHarmonic.Crest
|
||||
[@DecoratedField, SerializeField]
|
||||
internal bool _Enabled = true;
|
||||
|
||||
[@Label("Mesh")]
|
||||
[Tooltip("The meshing solution for the water surface.")]
|
||||
[@DecoratedField]
|
||||
[@SerializeField]
|
||||
WaterMeshType _MeshType;
|
||||
|
||||
[Tooltip("The water chunk renderers will have this layer.")]
|
||||
[@Layer]
|
||||
[@GenerateAPI]
|
||||
@@ -89,12 +74,6 @@ namespace WaveHarmonic.Crest
|
||||
|
||||
[@Heading("Advanced")]
|
||||
|
||||
[Tooltip("Rules to exclude cameras from surface rendering.\n\nThese are exclusion rules, so for all cameras, select Nothing. These rules are applied on top of the Layer rules.")]
|
||||
[@DecoratedField]
|
||||
[@GenerateAPI]
|
||||
[SerializeField]
|
||||
internal WaterCameraExclusion _CameraExclusions = WaterCameraExclusion.Hidden | WaterCameraExclusion.Reflection;
|
||||
|
||||
[Tooltip("How to handle self-intersections of the water surface.\n\nThey can be caused by choppy waves which can cause a flipped underwater effect. When not using the portals/volumes, this fix is only applied when within 2 metres of the water surface. Automatic will disable the fix if portals/volumes are used which is the recommend setting.")]
|
||||
[@DecoratedField, SerializeField]
|
||||
internal SurfaceSelfIntersectionFixMode _SurfaceSelfIntersectionFixMode = SurfaceSelfIntersectionFixMode.Automatic;
|
||||
@@ -143,8 +122,8 @@ namespace WaveHarmonic.Crest
|
||||
// Level of Detail
|
||||
//
|
||||
|
||||
readonly MaterialPropertyBlock[] _PerCascadeMPB = new MaterialPropertyBlock[Lod.k_MaximumSlices];
|
||||
internal MaterialPropertyBlock[] PerCascadeMPB { get; private set; }
|
||||
// Extra frame is for motion vectors.
|
||||
internal BufferedData<MaterialPropertyBlock[]> _PerCascadeMPB = new(2, () => new MaterialPropertyBlock[Lod.k_MaximumSlices]);
|
||||
|
||||
// We are computing these values to be optimal based on the base mesh vertex density.
|
||||
float _LodAlphaBlackPointFade;
|
||||
@@ -176,7 +155,6 @@ namespace WaveHarmonic.Crest
|
||||
internal Material _MotionVectorMaterial;
|
||||
|
||||
internal Material AboveOrBelowSurfaceMaterial => _VolumeMaterial == null ? _Material : _VolumeMaterial;
|
||||
internal bool IsQuadMesh => _MeshType == WaterMeshType.Quad;
|
||||
|
||||
|
||||
//
|
||||
@@ -222,23 +200,8 @@ namespace WaveHarmonic.Crest
|
||||
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_NormalMapParameters = Shader.PropertyToID("_Crest_NormalMapParameters");
|
||||
}
|
||||
|
||||
bool _ForceRenderingOff;
|
||||
|
||||
internal bool ForceRenderingOff
|
||||
{
|
||||
get => _ForceRenderingOff;
|
||||
set
|
||||
{
|
||||
_ForceRenderingOff = value;
|
||||
|
||||
if (_Enabled)
|
||||
{
|
||||
Root.gameObject.SetActive(!_ForceRenderingOff && !IsQuadMesh);
|
||||
}
|
||||
}
|
||||
public static readonly int s_ChunkMeshScaleAlphaSource = Shader.PropertyToID("_Crest_ChunkMeshScaleAlphaSource");
|
||||
public static readonly int s_ChunkGeometryGridWidthSource = Shader.PropertyToID("_Crest_ChunkGeometryGridWidthSource");
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
@@ -248,10 +211,19 @@ namespace WaveHarmonic.Crest
|
||||
Root.position = _Water.Position;
|
||||
Root.localScale = new(_Water.Scale, 1f, _Water.Scale);
|
||||
|
||||
// Populate MPBs with defaults. Protects against null exceptions etc.
|
||||
PerCascadeMPB = _PerCascadeMPB;
|
||||
NormalMapParameters = _NormalMapParameters;
|
||||
InitializeProperties();
|
||||
// Populate MPBs with defaults.
|
||||
for (var index = 0; index < _Water.LodLevels; index++)
|
||||
{
|
||||
for (var frame = 0; frame < 2; frame++)
|
||||
{
|
||||
var mpb = new MaterialPropertyBlock();
|
||||
mpb.SetInteger(Lod.ShaderIDs.s_LodIndex, index);
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkFarNormalsWeight, 1f);
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkMeshScaleAlpha, 0f);
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkMeshScaleAlphaSource, 0f);
|
||||
_PerCascadeMPB.Previous(frame)[index] = mpb;
|
||||
}
|
||||
}
|
||||
|
||||
// Resolution is 4 tiles across.
|
||||
var baseMeshDensity = _Water.LodResolution * 0.25f / _Water._GeometryDownSampleFactor;
|
||||
@@ -325,11 +297,6 @@ namespace WaveHarmonic.Crest
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsQuadMesh)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
GeometryUtility.CalculateFrustumPlanes(camera, _CameraFrustumPlanes);
|
||||
|
||||
foreach (var chunk in Chunks)
|
||||
@@ -380,43 +347,20 @@ namespace WaveHarmonic.Crest
|
||||
_Rebuild = false;
|
||||
}
|
||||
|
||||
internal void DisableChunks()
|
||||
internal void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
|
||||
{
|
||||
foreach (var chunk in Chunks)
|
||||
if (!WaterRenderer.ShouldRender(camera, Layer))
|
||||
{
|
||||
if (chunk.Rend != null) chunk.Rend.enabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
internal bool ShouldRender(Camera camera)
|
||||
{
|
||||
if (!_Enabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WaterRenderer.ShouldRender(camera, Layer, _CameraExclusions))
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Our planar reflection camera must never render the surface.
|
||||
if (camera == _Water.Reflections.ReflectionCamera)
|
||||
if (camera == WaterReflections.CurrentCamera)
|
||||
{
|
||||
return false;
|
||||
return;
|
||||
}
|
||||
|
||||
if (Material == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
internal void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
|
||||
{
|
||||
if (!ShouldRender(camera))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -424,7 +368,7 @@ namespace WaveHarmonic.Crest
|
||||
WritePerCameraMaterialParameters(camera);
|
||||
|
||||
// Motion Vectors.
|
||||
if (ShouldRenderMotionVectors(camera) && QueueMotionVectors)
|
||||
if (ShouldRenderMotionVectors(camera) && _QueueMotionVectors)
|
||||
{
|
||||
UpdateChunkVisibility(camera);
|
||||
|
||||
@@ -434,7 +378,6 @@ namespace WaveHarmonic.Crest
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning disable format
|
||||
#if d_UnityURP
|
||||
if (RenderPipelineHelper.IsUniversal)
|
||||
{
|
||||
@@ -450,26 +393,19 @@ namespace WaveHarmonic.Crest
|
||||
{
|
||||
OnBeginCameraRenderingLegacy(camera);
|
||||
}
|
||||
#pragma warning restore format
|
||||
}
|
||||
|
||||
internal void OnEndCameraRendering(Camera camera)
|
||||
{
|
||||
_DoneChunkVisibility = false;
|
||||
|
||||
// Restore in case exclusion culling ran.
|
||||
foreach (var chunk in Chunks)
|
||||
{
|
||||
if (chunk.Rend != null && !chunk._Culled) chunk.Rend.enabled = true;
|
||||
}
|
||||
|
||||
if (!WaterRenderer.ShouldRender(camera, Layer))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Our planar reflection camera must never render the surface.
|
||||
if (camera == _Water.Reflections.ReflectionCamera)
|
||||
if (camera == WaterReflections.CurrentCamera)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -480,20 +416,6 @@ namespace WaveHarmonic.Crest
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeProperties()
|
||||
{
|
||||
System.Array.Fill(NormalMapParameters, new Vector4(0, 0, 1, 0));
|
||||
|
||||
// Populate MPBs with defaults.
|
||||
for (var index = 0; index < PerCascadeMPB.Length; index++)
|
||||
{
|
||||
var block = new MaterialPropertyBlock();
|
||||
block.SetInteger(Lod.ShaderIDs.s_LodIndex, index);
|
||||
block.SetFloat(ShaderIDs.s_ChunkFarNormalsWeight, 1f);
|
||||
PerCascadeMPB[index] = block;
|
||||
}
|
||||
}
|
||||
|
||||
void WritePerCameraMaterialParameters(Camera camera)
|
||||
{
|
||||
if (Material == null)
|
||||
@@ -520,16 +442,14 @@ namespace WaveHarmonic.Crest
|
||||
var value = _SurfaceSelfIntersectionFixMode switch
|
||||
{
|
||||
SurfaceSelfIntersectionFixMode.On =>
|
||||
!_Water._PerCameraHeightReady
|
||||
? ForceFacing.None
|
||||
: height < -2f
|
||||
height < -2f
|
||||
? ForceFacing.BelowWater
|
||||
: height > 2f
|
||||
? ForceFacing.AboveWater
|
||||
: ForceFacing.None,
|
||||
// Skip for portals as it is possible to see both sides of the surface at any position.
|
||||
SurfaceSelfIntersectionFixMode.Automatic =>
|
||||
_Water.Portaled || !_Water._PerCameraHeightReady
|
||||
_Water.Portaled
|
||||
? ForceFacing.None
|
||||
: height < -2f
|
||||
? ForceFacing.BelowWater
|
||||
@@ -551,27 +471,12 @@ namespace WaveHarmonic.Crest
|
||||
Rebuild();
|
||||
}
|
||||
|
||||
if (_ForceRenderingOff)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LoadCameraData(_Water.CurrentCamera);
|
||||
|
||||
Root.position = _Water.Position;
|
||||
Root.localScale = new(_Water.Scale, 1f, _Water.Scale);
|
||||
|
||||
Root.gameObject.SetActive(!IsQuadMesh);
|
||||
Material.SetKeyword(new(Material.shader, "_CREST_CUSTOM_MESH"), IsQuadMesh);
|
||||
|
||||
_PerCascadeMPB.Flip();
|
||||
WritePerCascadeInstanceData();
|
||||
|
||||
if (IsQuadMesh)
|
||||
{
|
||||
LateUpdateQuadMesh();
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var chunk in Chunks)
|
||||
{
|
||||
chunk.UpdateMeshBounds(_Water, this);
|
||||
@@ -601,48 +506,60 @@ namespace WaveHarmonic.Crest
|
||||
{
|
||||
var levels = _Water.LodLevels;
|
||||
var texel = _Water.LodResolution * 0.25f / _Water._GeometryDownSampleFactor;
|
||||
var mpbsCurrent = _PerCascadeMPB.Current;
|
||||
var mpbsPrevious = _PerCascadeMPB.Previous(1);
|
||||
|
||||
// LOD 0
|
||||
{
|
||||
var mpb = mpbsCurrent[0];
|
||||
|
||||
if (_Water.WriteMotionVectors)
|
||||
{
|
||||
// NOTE: it may be more optimal to store in an array than fetching from MPB.
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkMeshScaleAlphaSource, mpbsPrevious[0].GetFloat(ShaderIDs.s_ChunkMeshScaleAlpha));
|
||||
}
|
||||
|
||||
// Blend LOD 0 shape in/out to avoid pop, if scale could increase.
|
||||
PerCascadeMPB[0].SetFloat(ShaderIDs.s_ChunkMeshScaleAlpha, _Water.ScaleCouldIncrease ? _Water.ViewerAltitudeLevelAlpha : 0f);
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkMeshScaleAlpha, _Water.ScaleCouldIncrease ? _Water.ViewerAltitudeLevelAlpha : 0f);
|
||||
}
|
||||
|
||||
// LOD N
|
||||
{
|
||||
var mpb = mpbsCurrent[levels - 1];
|
||||
|
||||
// Blend furthest normals scale in/out to avoid pop, if scale could reduce.
|
||||
var weight = _Water.ScaleCouldDecrease ? _Water.ViewerAltitudeLevelAlpha : 1f;
|
||||
PerCascadeMPB[levels - 1].SetFloat(ShaderIDs.s_ChunkFarNormalsWeight, weight);
|
||||
NormalMapParameters[levels - 1] = new(0, 0, weight, 0);
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkFarNormalsWeight, _Water.ScaleCouldDecrease ? _Water.ViewerAltitudeLevelAlpha : 1f);
|
||||
}
|
||||
|
||||
for (var index = 0; index < levels; index++)
|
||||
{
|
||||
var mpb = PerCascadeMPB[index];
|
||||
var mpbCurrent = mpbsCurrent[index];
|
||||
var mpbPrevious = mpbsPrevious[index];
|
||||
|
||||
// geometry data
|
||||
// compute grid size of geometry. take the long way to get there - make sure we land exactly on a power of two
|
||||
// and not inherit any of the lossy-ness from lossyScale.
|
||||
var scale = _Water.CascadeData.Current[index].x;
|
||||
var scale = _Water._CascadeData.Current[index].x;
|
||||
var width = scale / texel;
|
||||
|
||||
mpb.SetFloat(ShaderIDs.s_ChunkGeometryGridWidth, width);
|
||||
if (_Water.WriteMotionVectors)
|
||||
{
|
||||
// NOTE: it may be more optimal to store in an array than fetching from MPB.
|
||||
mpbPrevious.SetFloat(ShaderIDs.s_ChunkGeometryGridWidthSource, mpbCurrent.GetFloat(ShaderIDs.s_ChunkGeometryGridWidth));
|
||||
}
|
||||
|
||||
mpbCurrent.SetFloat(ShaderIDs.s_ChunkGeometryGridWidth, width);
|
||||
|
||||
var mul = 1.875f; // fudge 1
|
||||
var pow = 1.4f; // fudge 2
|
||||
var texelWidth = width / _Water._GeometryDownSampleFactor;
|
||||
var speed = new Vector2
|
||||
mpbCurrent.SetVector(ShaderIDs.s_ChunkNormalScrollSpeed, new
|
||||
(
|
||||
Mathf.Pow(Mathf.Log(1f + 2f * texelWidth) * mul, pow),
|
||||
Mathf.Pow(Mathf.Log(1f + 4f * texelWidth) * mul, pow)
|
||||
);
|
||||
|
||||
mpb.SetVector(ShaderIDs.s_ChunkNormalScrollSpeed, speed);
|
||||
|
||||
var normals = NormalMapParameters[index];
|
||||
normals.x = speed.x;
|
||||
normals.y = speed.y;
|
||||
NormalMapParameters[index] = normals;
|
||||
Mathf.Pow(Mathf.Log(1f + 4f * texelWidth) * mul, pow),
|
||||
0,
|
||||
0
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -732,7 +649,7 @@ namespace WaveHarmonic.Crest
|
||||
_CanSkipCulling = WaterBody.WaterBodies.Count == 0;
|
||||
}
|
||||
|
||||
internal void Render(Camera camera, CommandBuffer buffer, Material material = null, int pass = 0, bool culled = false, MaterialPropertyBlock mpb = null)
|
||||
internal void Render(Camera camera, CommandBuffer buffer, Material material = null, int pass = 0, bool culled = false)
|
||||
{
|
||||
var noMaterial = material == null;
|
||||
|
||||
@@ -741,12 +658,6 @@ namespace WaveHarmonic.Crest
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsQuadMesh)
|
||||
{
|
||||
buffer.DrawMesh(Helpers.QuadMesh, Matrix4x4.TRS(Root.position, Quaternion.Euler(90f, 0, 0), new(10000, 10000, 1)), noMaterial ? Material : material, 0, shaderPass: pass, mpb);
|
||||
return;
|
||||
}
|
||||
|
||||
UpdateChunkVisibility(camera);
|
||||
|
||||
// Spends approx 0.2-0.3ms here on 2018 Dell XPS 15.
|
||||
@@ -841,7 +752,6 @@ namespace WaveHarmonic.Crest
|
||||
partial class SurfaceRenderer
|
||||
{
|
||||
bool _QueueMotionVectors;
|
||||
bool QueueMotionVectors => _QueueMotionVectors && !IsQuadMesh;
|
||||
|
||||
bool ShouldRenderMotionVectors(Camera camera)
|
||||
{
|
||||
@@ -907,7 +817,7 @@ namespace WaveHarmonic.Crest
|
||||
|
||||
void UpdateMotionVectorsMaterial(Material surface, ref Material motion)
|
||||
{
|
||||
if (!QueueMotionVectors)
|
||||
if (!_QueueMotionVectors)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -945,80 +855,4 @@ namespace WaveHarmonic.Crest
|
||||
motion.SetFloat(ShaderIDs.s_BuiltShadowCasterZTest, 1); // ZTest Never
|
||||
}
|
||||
}
|
||||
|
||||
partial class SurfaceRenderer
|
||||
{
|
||||
internal Dictionary<Camera, MaterialPropertyBlock[]> _PerCameraPerCascadeMPB = new();
|
||||
internal Dictionary<Camera, Vector4[]> _PerCameraNormalMapParameters = new();
|
||||
|
||||
void LoadCameraData(Camera camera)
|
||||
{
|
||||
if (!_Water.SeparateViewpoint)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!_PerCameraPerCascadeMPB.ContainsKey(camera))
|
||||
{
|
||||
PerCascadeMPB = new MaterialPropertyBlock[Lod.k_MaximumSlices];
|
||||
_PerCameraPerCascadeMPB.Add(camera, PerCascadeMPB);
|
||||
NormalMapParameters = new Vector4[Lod.k_MaximumSlices];
|
||||
_PerCameraNormalMapParameters.Add(camera, NormalMapParameters);
|
||||
InitializeProperties();
|
||||
}
|
||||
else
|
||||
{
|
||||
PerCascadeMPB = _PerCameraPerCascadeMPB[camera];
|
||||
NormalMapParameters = _PerCameraNormalMapParameters[camera];
|
||||
}
|
||||
}
|
||||
|
||||
internal void RemoveCameraData(Camera camera)
|
||||
{
|
||||
if (_PerCameraPerCascadeMPB.ContainsKey(camera))
|
||||
{
|
||||
_PerCameraPerCascadeMPB.Remove(camera);
|
||||
_PerCameraNormalMapParameters.Remove(camera);
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
RemoveCameraDataLDT(camera);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
// Quad
|
||||
partial class SurfaceRenderer
|
||||
{
|
||||
readonly Vector4[] _NormalMapParameters = new Vector4[Lod.k_MaximumSlices];
|
||||
Vector4[] NormalMapParameters { get; set; }
|
||||
|
||||
void LateUpdateQuadMesh()
|
||||
{
|
||||
Shader.SetGlobalVectorArray(ShaderIDs.s_NormalMapParameters, NormalMapParameters);
|
||||
|
||||
var scale = new Vector3(10000 * _Water.Scale, 10000 * _Water.Scale, 1);
|
||||
var bounds = Helpers.QuadMesh.bounds;
|
||||
bounds.Expand(scale);
|
||||
Graphics.RenderMesh
|
||||
(
|
||||
new()
|
||||
{
|
||||
motionVectorMode = MotionVectorGenerationMode.Camera,
|
||||
material = Material,
|
||||
worldBounds = Root.TransformBounds(bounds),
|
||||
layer = Layer,
|
||||
shadowCastingMode = CastShadows ? ShadowCastingMode.On : ShadowCastingMode.Off,
|
||||
lightProbeUsage = LightProbeUsage.Off,
|
||||
reflectionProbeUsage = ReflectionProbeUsage.BlendProbesAndSkybox,
|
||||
renderingLayerMask = (uint)Layer,
|
||||
},
|
||||
Helpers.QuadMesh,
|
||||
submeshIndex: 0,
|
||||
Matrix4x4.TRS(Root.position, Quaternion.Euler(90f, 0, 0), scale)
|
||||
);
|
||||
|
||||
UpdateMaterial(_Material, ref _MotionVectorMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user