移除水

This commit is contained in:
2025-06-21 21:58:06 +08:00
parent d61516a576
commit e9f76d0f11
1566 changed files with 9218 additions and 300913 deletions

View File

@@ -1,83 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest
{
sealed class UnderwaterEffectPass
{
readonly UnderwaterRenderer _Renderer;
RTHandle _ColorTexture;
RTHandle _ColorTarget;
RTHandle _DepthTarget;
bool _FirstRender = true;
readonly System.Action<CommandBuffer> _CopyColorTexture;
public UnderwaterEffectPass(UnderwaterRenderer renderer)
{
_Renderer = renderer;
_CopyColorTexture = new(CopyColorTexture);
}
void CopyColorTexture(CommandBuffer buffer)
{
Blitter.BlitCameraTexture(buffer, _ColorTarget, _ColorTexture);
CoreUtils.SetRenderTarget(buffer, _ColorTarget, _DepthTarget, ClearFlag.None);
}
public void Allocate(GraphicsFormat format)
{
// TODO: There may other settings we want to set or bring in. Not MSAA since this is a resolved texture.
_ColorTexture = RTHandles.Alloc
(
Vector2.one,
TextureXR.slices,
dimension: TextureXR.dimension,
colorFormat: format,
depthBufferBits: DepthBits.None,
useDynamicScale: true,
wrapMode: TextureWrapMode.Clamp,
name: "_Crest_UnderwaterCameraColorTexture"
);
}
public void ReAllocate(RenderTextureDescriptor descriptor)
{
// Descriptor will not have MSAA bound.
RenderPipelineCompatibilityHelper.ReAllocateIfNeeded(ref _ColorTexture, descriptor, name: "_Crest_UnderwaterCameraColorTexture");
}
public void Release()
{
_ColorTexture?.Release();
_ColorTexture = null;
}
public void Execute(Camera camera, CommandBuffer buffer, RTHandle color, RTHandle depth, MaterialPropertyBlock mpb = null)
{
_Renderer.UpdateEffectMaterial(camera, _FirstRender);
_ColorTarget = color;
_DepthTarget = depth;
CopyColorTexture(buffer);
buffer.SetGlobalTexture(UnderwaterRenderer.ShaderIDs.s_CameraColorTexture, _ColorTexture);
_Renderer.ExecuteEffect(camera, buffer, _CopyColorTexture, mpb);
// The last pass (uber post) does not resolve the texture.
// Although, this is wasteful if the pass after this does a resolve.
// Possibly a bug with Unity?
buffer.ResolveAntiAliasedSurface(color);
_FirstRender = false;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 6c581581e08ff40e689d952358cea7a0
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,186 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityHDRP
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
using UnityEngine.Rendering.RendererUtils;
namespace WaveHarmonic.Crest
{
sealed class UnderwaterEffectPassHDRP : CustomPass
{
const string k_Name = "Underwater Effect";
static UnderwaterRenderer s_Renderer;
static UnderwaterEffectPass s_UnderwaterEffectPass;
static UnderwaterEffectPassHDRP s_Instance;
static CopyDepthBufferPassHDRP s_CopyDepthBufferPassHDRP;
static ShaderTagId[] s_ForwardShaderTags;
GameObject _GameObject;
public static void Enable(UnderwaterRenderer renderer)
{
var gameObject = CustomPassHelpers.CreateOrUpdate
(
parent: renderer._Water.Container.transform,
k_Name,
hide: !renderer._Water._Debug._ShowHiddenObjects
);
CustomPassHelpers.CreateOrUpdate
(
gameObject,
ref s_CopyDepthBufferPassHDRP,
"Copy Depth Buffer",
CustomPassInjectionPoint.AfterOpaqueDepthAndNormal
);
CustomPassHelpers.CreateOrUpdate
(
gameObject,
ref s_Instance,
k_Name,
CustomPassInjectionPoint.BeforePostProcess
);
s_Instance._GameObject = gameObject;
s_Renderer = renderer;
s_UnderwaterEffectPass = new(renderer);
RenderPipelineManager.beginCameraRendering -= s_Instance.OnBeginCameraRendering;
RenderPipelineManager.beginCameraRendering += s_Instance.OnBeginCameraRendering;
}
public static void Disable()
{
// It should be safe to rely on this reference for this reference to fail.
if (s_Instance != null && s_Instance._GameObject != null)
{
// Will also trigger Cleanup below.
s_Instance._GameObject.SetActive(false);
}
}
void OnBeginCameraRendering(ScriptableRenderContext context, Camera camera)
{
s_CopyDepthBufferPassHDRP.enabled = s_Renderer.UseStencilBuffer;
}
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
{
var asset = GraphicsSettings.currentRenderPipeline as HDRenderPipelineAsset;
// Developers have a choice with the color buffer format. There is also a custom buffer buffer format but
// that is not relevant here. This will not cover the format change when scene filtering as Setup/Cleanup is
// not executed for this change.
s_UnderwaterEffectPass.Allocate((GraphicsFormat)asset.currentPlatformRenderPipelineSettings.colorBufferFormat);
// Taken from:
// https://github.com/Unity-Technologies/Graphics/blob/778ddac6207ade1689999b95380cd835b0669f2d/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/DrawRenderersCustomPass.cs#L136-L142
s_ForwardShaderTags ??= new[]
{
HDShaderPassNames.s_ForwardName, // HD Lit shader
HDShaderPassNames.s_ForwardOnlyName, // HD Unlit shader
HDShaderPassNames.s_SRPDefaultUnlitName, // Cross SRP Unlit shader
};
}
protected override void Cleanup()
{
RenderPipelineManager.beginCameraRendering -= s_Instance.OnBeginCameraRendering;
s_UnderwaterEffectPass?.Release();
}
protected override void Execute(CustomPassContext context)
{
var camera = context.hdCamera.camera;
if (!s_Renderer.ShouldRender(camera, UnderwaterRenderer.Pass.Effect))
{
return;
}
// Create a separate stencil buffer context by using a depth buffer copy if needed.
var depthBuffer = s_Renderer.UseStencilBuffer
? s_CopyDepthBufferPassHDRP._DepthBufferCopy
: context.cameraDepthBuffer;
s_UnderwaterEffectPass.Execute(camera, context.cmd, context.cameraColorBuffer, depthBuffer, context.propertyBlock);
// Renders transparent objects after the underwater effect. Using the correct
// shader, the above water portion of the object is rendered normally (in the
// transparent pass), and the below water portion is rendered here with underwater
// applied.
// See the following for reference:
// https://github.com/Unity-Technologies/Graphics/blob/master/Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/DrawRenderersCustomPass.cs
if (s_Renderer.EnableShaderAPI)
{
var renderConfig = HDUtils.GetRendererConfiguration
(
#if UNITY_6000_0_OR_NEWER
context.hdCamera.frameSettings.IsEnabled(FrameSettingsField.AdaptiveProbeVolume),
#else
context.hdCamera.frameSettings.IsEnabled(FrameSettingsField.ProbeVolume),
#endif
context.hdCamera.frameSettings.IsEnabled(FrameSettingsField.Shadowmask)
);
var result = new RendererListDesc(s_ForwardShaderTags, context.cullingResults, context.hdCamera.camera)
{
rendererConfiguration = renderConfig,
renderQueueRange = GetRenderQueueRange(RenderQueueType.AllTransparent),
sortingCriteria = SortingCriteria.CommonTransparent,
excludeObjectMotionVectors = false,
layerMask = s_Renderer._TransparentObjectLayers,
};
context.cmd.EnableShaderKeyword(UnderwaterRenderer.k_KeywordUnderwaterObjects);
CoreUtils.DrawRendererList(context.renderContext, context.cmd, context.renderContext.CreateRendererList(result));
context.cmd.DisableShaderKeyword(UnderwaterRenderer.k_KeywordUnderwaterObjects);
}
}
}
sealed class CopyDepthBufferPassHDRP : CustomPass
{
public RTHandle _DepthBufferCopy;
protected override void Execute(CustomPassContext context)
{
// Multiple cameras could have different settings.
RenderPipelineCompatibilityHelper.ReAllocateIfNeeded
(
ref _DepthBufferCopy,
context.cameraDepthBuffer.rt.descriptor,
FilterMode.Point,
name: "_Crest_UnderwaterCopiedDepthBuffer"
);
var buffer = context.cmd;
buffer.SetRenderTarget(BuiltinRenderTextureType.None, _DepthBufferCopy);
buffer.ClearRenderTarget(RTClearFlags.Depth, Color.black, 1, 0);
buffer.CopyTexture(context.cameraDepthBuffer.rt, _DepthBufferCopy.rt);
// Clear the stencil component just in case.
buffer.ClearRenderTarget(RTClearFlags.Stencil, Color.black, 1, 0);
}
protected override void Cleanup()
{
_DepthBufferCopy?.Release();
_DepthBufferCopy = null;
}
}
}
#endif // d_UnityHDRP

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 03d7c1db420e64f7c9894f9c2bdaae4c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,202 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityURP
#if UNITY_6000_0_OR_NEWER
using UnityEngine.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using UnityEngine.Rendering.Universal;
namespace WaveHarmonic.Crest
{
partial class UnderwaterEffectPassURP
{
class PassData
{
#pragma warning disable IDE1006 // Naming Styles
public UniversalCameraData cameraData;
public RenderGraphHelper.Handle colorTargetHandle;
public RenderGraphHelper.Handle depthTargetHandle;
#pragma warning restore IDE1006 // Naming Styles
public void Init(ContextContainer frameData, IUnsafeRenderGraphBuilder builder = null)
{
var resources = frameData.Get<UniversalResourceData>();
cameraData = frameData.Get<UniversalCameraData>();
if (builder == null)
{
#pragma warning disable CS0618 // Type or member is obsolete
colorTargetHandle = cameraData.renderer.cameraColorTargetHandle;
depthTargetHandle = cameraData.renderer.cameraDepthTargetHandle;
#pragma warning restore CS0618 // Type or member is obsolete
}
else
{
colorTargetHandle = resources.activeColorTexture;
depthTargetHandle = resources.activeDepthTexture;
builder.UseTexture(colorTargetHandle, AccessFlags.ReadWrite);
builder.UseTexture(depthTargetHandle, AccessFlags.ReadWrite);
}
}
}
readonly PassData _PassData = new();
public override void RecordRenderGraph(RenderGraph graph, ContextContainer frame)
{
using (var builder = graph.AddUnsafePass<PassData>(k_Name, out var data))
{
data.Init(frame, builder);
builder.AllowPassCulling(false);
builder.SetRenderFunc<PassData>((data, context) =>
{
var buffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
OnSetup(buffer, data);
Execute(context.GetRenderContext(), buffer, data);
});
}
}
[System.Obsolete]
public override void OnCameraSetup(CommandBuffer buffer, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
}
[System.Obsolete]
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
var buffer = CommandBufferPool.Get(k_Name);
OnSetup(buffer, _PassData);
Execute(context, buffer, _PassData);
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
}
partial class RenderObjectsWithoutFogPass
{
class PassData
{
#pragma warning disable IDE1006 // Naming Styles
public UniversalCameraData cameraData;
public UniversalLightData lightData;
public UniversalRenderingData renderingData;
public CullingResults cullResults;
#pragma warning restore IDE1006 // Naming Styles
public void Init(ContextContainer frameData, IUnsafeRenderGraphBuilder builder = null)
{
cameraData = frameData.Get<UniversalCameraData>();
lightData = frameData.Get<UniversalLightData>();
renderingData = frameData.Get<UniversalRenderingData>();
cullResults = renderingData.cullResults;
}
}
readonly PassData _PassData = new();
public override void RecordRenderGraph(RenderGraph graph, ContextContainer frame)
{
using (var builder = graph.AddUnsafePass<PassData>(k_Name, out var data))
{
data.Init(frame, builder);
builder.AllowPassCulling(false);
builder.SetRenderFunc<PassData>((data, context) =>
{
var buffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
Execute(context.GetRenderContext(), buffer, data);
});
}
}
[System.Obsolete]
public override void OnCameraSetup(CommandBuffer buffer, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
}
[System.Obsolete]
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
var buffer = CommandBufferPool.Get(k_Name);
Execute(context, buffer, _PassData);
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
}
}
}
partial class CopyDepthBufferPassURP
{
class PassData
{
#pragma warning disable IDE1006 // Naming Styles
public UniversalCameraData cameraData;
public RenderGraphHelper.Handle colorTargetHandle;
public RenderGraphHelper.Handle depthTargetHandle;
#pragma warning restore IDE1006 // Naming Styles
public void Init(ContextContainer frameData, IUnsafeRenderGraphBuilder builder = null)
{
var resources = frameData.Get<UniversalResourceData>();
cameraData = frameData.Get<UniversalCameraData>();
if (builder == null)
{
#pragma warning disable CS0618 // Type or member is obsolete
depthTargetHandle = cameraData.renderer.cameraDepthTargetHandle;
#pragma warning restore CS0618 // Type or member is obsolete
}
else
{
depthTargetHandle = resources.activeDepthTexture;
builder.UseTexture(depthTargetHandle, AccessFlags.ReadWrite);
}
}
}
readonly PassData _PassData = new();
public override void RecordRenderGraph(RenderGraph graph, ContextContainer frame)
{
using (var builder = graph.AddUnsafePass<PassData>(k_Name, out var data))
{
data.Init(frame, builder);
builder.AllowPassCulling(false);
builder.SetRenderFunc<PassData>((data, context) =>
{
var buffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
OnSetup(buffer, data);
Execute(context.GetRenderContext(), buffer, data);
});
}
}
[System.Obsolete]
public override void OnCameraSetup(CommandBuffer buffer, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
}
[System.Obsolete]
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
_PassData.Init(data.GetFrameData());
var buffer = CommandBufferPool.Get(k_Name);
OnSetup(buffer, _PassData);
Execute(context, buffer, _PassData);
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
}
}
}
#endif // UNITY_6000_0_OR_NEWER
#endif // d_UnityURP

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 7fba7ac72b29f4b12a08eb07d80a2703
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,308 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityURP
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace WaveHarmonic.Crest
{
sealed partial class UnderwaterEffectPassURP : ScriptableRenderPass
{
const string k_Name = "Crest Underwater Effect";
UnderwaterRenderer _Renderer;
static UnderwaterEffectPassURP s_Instance;
RenderObjectsWithoutFogPass _ApplyFogToTransparentObjects;
UnderwaterEffectPass _UnderwaterEffectPass;
CopyDepthBufferPassURP _CopyDepthBufferPass;
RTHandle _ColorBuffer;
RTHandle _DepthBuffer;
public UnderwaterEffectPassURP()
{
renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
ConfigureInput(ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth);
}
public static void Enable(UnderwaterRenderer renderer)
{
if (s_Instance == null)
{
s_Instance = new();
s_Instance._Renderer = renderer;
s_Instance._CopyDepthBufferPass = new(RenderPassEvent.AfterRenderingOpaques);
s_Instance._ApplyFogToTransparentObjects = new();
}
RenderPipelineManager.beginCameraRendering -= s_Instance.EnqueuePass;
RenderPipelineManager.beginCameraRendering += s_Instance.EnqueuePass;
RenderPipelineManager.activeRenderPipelineTypeChanged -= Disable;
RenderPipelineManager.activeRenderPipelineTypeChanged += Disable;
}
public static void Disable()
{
if (s_Instance != null) RenderPipelineManager.beginCameraRendering -= s_Instance.EnqueuePass;
RenderPipelineManager.activeRenderPipelineTypeChanged -= Disable;
s_Instance?._UnderwaterEffectPass?.Release();
s_Instance?._CopyDepthBufferPass?.Release();
s_Instance = null;
}
void EnqueuePass(ScriptableRenderContext context, Camera camera)
{
if (!_Renderer.ShouldRender(camera, UnderwaterRenderer.Pass.Effect))
{
return;
}
var renderer = camera.GetUniversalAdditionalCameraData().scriptableRenderer;
#if UNITY_EDITOR
if (renderer == null) return;
#endif
// Copy the depth buffer to create a new depth/stencil context.
if (_Renderer.UseStencilBuffer)
{
renderer.EnqueuePass(_CopyDepthBufferPass);
}
// Set up internal pass which houses shared code for SRPs.
_UnderwaterEffectPass ??= new(_Renderer);
renderer.EnqueuePass(s_Instance);
if (_Renderer.EnableShaderAPI)
{
renderer.EnqueuePass(_ApplyFogToTransparentObjects);
}
}
#if UNITY_6000_0_OR_NEWER
void OnSetup(CommandBuffer buffer, PassData data)
{
_ColorBuffer = data.colorTargetHandle.Texture;
_DepthBuffer = data.depthTargetHandle.Texture;
// TODO: renderingData.cameraData.cameraTargetDescriptor?
_UnderwaterEffectPass.ReAllocate(_ColorBuffer.rt.descriptor);
}
void Execute(ScriptableRenderContext context, CommandBuffer buffer, PassData data)
{
if (_Renderer.UseStencilBuffer)
{
_DepthBuffer = _CopyDepthBufferPass._DepthBufferCopy;
}
_UnderwaterEffectPass.Execute(data.cameraData.camera, buffer, _ColorBuffer, _DepthBuffer);
}
#else
public override void OnCameraSetup(CommandBuffer buffer, ref RenderingData data)
{
_ColorBuffer = data.cameraData.renderer.cameraColorTargetHandle;
_DepthBuffer = data.cameraData.renderer.cameraDepthTargetHandle;
// TODO: renderingData.cameraData.cameraTargetDescriptor?
_UnderwaterEffectPass.ReAllocate(_ColorBuffer.rt.descriptor);
}
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
var buffer = CommandBufferPool.Get(k_Name);
if (_Renderer.UseStencilBuffer)
{
_DepthBuffer = _CopyDepthBufferPass._DepthBufferCopy;
}
_UnderwaterEffectPass.Execute(data.cameraData.camera, buffer, _ColorBuffer, _DepthBuffer);
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
}
#endif
// Renders transparent objects after the underwater effect. Using the correct
// shader, the above water portion of the object is rendered normally (in the
// transparent pass), and the below water portion is rendered here with underwater
// applied.
sealed partial class RenderObjectsWithoutFogPass : ScriptableRenderPass
{
FilteringSettings _FilteringSettings;
static readonly List<ShaderTagId> s_ShaderTagIdList = new()
{
new("SRPDefaultUnlit"),
new("UniversalForward"),
new("UniversalForwardOnly"),
new("LightweightForward"),
};
public RenderObjectsWithoutFogPass()
{
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
_FilteringSettings = new(RenderQueueRange.transparent, 0);
}
#if UNITY_6000_0_OR_NEWER
void Execute(ScriptableRenderContext context, CommandBuffer buffer, PassData renderingData)
#else
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
#endif
{
_FilteringSettings.layerMask = s_Instance._Renderer._TransparentObjectLayers;
#if !UNITY_6000_0_OR_NEWER
var buffer = CommandBufferPool.Get("Crest Underwater Objects");
#endif
// Disable Unity's fog keywords as there is no option to ignore fog for the Shader Graph.
if (RenderSettings.fog)
{
switch (RenderSettings.fogMode)
{
case FogMode.Exponential:
buffer.DisableShaderKeyword("FOG_EXP");
break;
case FogMode.Linear:
buffer.DisableShaderKeyword("FOG_LINEAR");
break;
case FogMode.ExponentialSquared:
buffer.DisableShaderKeyword("FOG_EXP2");
break;
}
}
buffer.EnableShaderKeyword(UnderwaterRenderer.k_KeywordUnderwaterObjects);
// If we want anything to apply to DrawRenderers, it has to be executed before:
// https://docs.unity3d.com/ScriptReference/Rendering.ScriptableRenderContext.DrawRenderers.html
context.ExecuteCommandBuffer(buffer);
buffer.Clear();
#if UNITY_6000_0_OR_NEWER
var drawingSettings = RenderingUtils.CreateDrawingSettings
(
s_ShaderTagIdList,
renderingData.renderingData,
renderingData.cameraData,
renderingData.lightData,
SortingCriteria.CommonTransparent
);
var parameters = new RendererListParams(renderingData.cullResults, drawingSettings, _FilteringSettings);
var list = context.CreateRendererList(ref parameters);
buffer.DrawRendererList(list);
#else
var drawingSettings = CreateDrawingSettings
(
s_ShaderTagIdList,
ref renderingData,
SortingCriteria.CommonTransparent
);
context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref _FilteringSettings);
#endif
// Revert fog keywords.
if (RenderSettings.fog)
{
switch (RenderSettings.fogMode)
{
case FogMode.Exponential:
buffer.EnableShaderKeyword("FOG_EXP");
break;
case FogMode.Linear:
buffer.EnableShaderKeyword("FOG_LINEAR");
break;
case FogMode.ExponentialSquared:
buffer.EnableShaderKeyword("FOG_EXP2");
break;
}
}
buffer.DisableShaderKeyword(UnderwaterRenderer.k_KeywordUnderwaterObjects);
#if !UNITY_6000_0_OR_NEWER
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
#endif
}
}
}
// Copies the depth buffer to avoid conflicts when using the stencil buffer.
sealed partial class CopyDepthBufferPassURP : ScriptableRenderPass
{
const string k_Name = "Crest Copy Depth Buffer";
RTHandle _DepthBuffer;
public RTHandle _DepthBufferCopy;
public CopyDepthBufferPassURP(RenderPassEvent @event)
{
renderPassEvent = @event;
}
#if UNITY_6000_0_OR_NEWER
void OnSetup(CommandBuffer buffer, PassData data)
#else
public override void OnCameraSetup(CommandBuffer buffer, ref RenderingData data)
#endif
{
var descriptor = data.cameraData.cameraTargetDescriptor;
descriptor.graphicsFormat = GraphicsFormat.None;
descriptor.bindMS = descriptor.msaaSamples > 1;
#if UNITY_6000_0_OR_NEWER
RenderingUtils.ReAllocateHandleIfNeeded(ref _DepthBufferCopy, descriptor, FilterMode.Point, name: "Crest Copied Depth Buffer");
_DepthBuffer = data.depthTargetHandle;
#else
RenderingUtils.ReAllocateIfNeeded(ref _DepthBufferCopy, descriptor, FilterMode.Point, name: "Crest Copied Depth Buffer");
_DepthBuffer = data.cameraData.renderer.cameraDepthTargetHandle;
#endif
}
#if UNITY_6000_0_OR_NEWER
void Execute(ScriptableRenderContext context, CommandBuffer buffer, PassData data)
#else
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
#endif
{
#if !UNITY_6000_0_OR_NEWER
var buffer = CommandBufferPool.Get(k_Name);
#endif
// Must clear even though we are overwriting or there will be strange artifacts on new writes.
// This could be a Unity bug and may be worth reporting.
buffer.SetRenderTarget(BuiltinRenderTextureType.None, _DepthBufferCopy);
buffer.ClearRenderTarget(RTClearFlags.Depth, Color.black, 1, 0);
buffer.CopyTexture(_DepthBuffer.rt, _DepthBufferCopy.rt);
// Clear the stencil component just in case.
buffer.ClearRenderTarget(RTClearFlags.Stencil, Color.black, 1, 0);
#if !UNITY_6000_0_OR_NEWER
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
#endif
}
public void Release()
{
_DepthBuffer = null;
_DepthBufferCopy?.Release();
}
}
}
#endif // d_UnityURP

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5ab3e34da699e48eaa28a35fba152510
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,140 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Experimental.Rendering;
namespace WaveHarmonic.Crest
{
sealed class UnderwaterMaskPass
{
readonly UnderwaterRenderer _Renderer;
#if d_CrestPortals
readonly Portals.PortalRenderer _Portals;
#endif
RTHandle _MaskTexture;
RTHandle _DepthTexture;
RenderTargetIdentifier _MaskTarget;
RenderTargetIdentifier _DepthTarget;
public UnderwaterMaskPass(UnderwaterRenderer renderer)
{
_Renderer = renderer;
#if d_CrestPortals
_Portals = renderer._Portals;
#endif
}
public void Allocate()
{
_MaskTexture = RTHandles.Alloc
(
scaleFactor: Vector2.one,
slices: TextureXR.slices,
dimension: TextureXR.dimension,
depthBufferBits: DepthBits.None,
colorFormat: GraphicsFormat.R16_SFloat,
enableRandomWrite: true,
useDynamicScale: true,
name: "_Crest_WaterMask"
);
_MaskTarget = new(_MaskTexture, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
_DepthTexture = RTHandles.Alloc
(
scaleFactor: Vector2.one,
slices: TextureXR.slices,
dimension: TextureXR.dimension,
depthBufferBits: UnderwaterRenderer.k_DepthBits,
colorFormat: GraphicsFormat.None,
enableRandomWrite: false,
useDynamicScale: true,
name: "_Crest_WaterMaskDepth"
);
#if d_CrestPortals
// For HDRP we cannot allocate in OnEnable as RTHandle will complain.
if (_Portals.Active)
{
_Portals.Allocate();
}
#endif
_DepthTarget = new(_DepthTexture, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
_Renderer.SetUpArtifactsShader();
}
// We should not have to reallocate, but URP will raise errors when an option like HDR is changed if we do not.
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.
descriptor.bindMS = false;
descriptor.msaaSamples = 1;
descriptor.graphicsFormat = GraphicsFormat.None;
if (RenderPipelineCompatibilityHelper.ReAllocateIfNeeded(ref _DepthTexture, descriptor, name: "_Crest_WaterMaskDepth"))
{
_DepthTarget = new(_DepthTexture, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
}
#if d_CrestPortals
if (_Portals.Active)
{
_Portals.ReAllocate(descriptor);
}
#endif
descriptor.graphicsFormat = GraphicsFormat.R16_SFloat;
descriptor.enableRandomWrite = true;
descriptor.depthBufferBits = 0;
if (RenderPipelineCompatibilityHelper.ReAllocateIfNeeded(ref _MaskTexture, descriptor, name: "_Crest_WaterMask"))
{
_MaskTarget = new(_MaskTexture, mipLevel: 0, CubemapFace.Unknown, depthSlice: -1);
}
}
public void Release()
{
_MaskTexture?.Release();
_DepthTexture?.Release();
#if d_CrestPortals
if (_Portals.Active)
{
_Portals.Release();
}
#endif
}
public void Execute(Camera camera, CommandBuffer buffer)
{
#if d_CrestPortals
// Populate water volume before mask so we can use the stencil.
if (_Portals.Active)
{
_Portals.RenderMask(camera, buffer, _Renderer._MaskMaterial);
_Portals.RenderStencil(buffer, _DepthTexture);
}
#endif
// For HDRP software dynamic scaling to work.
CoreUtils.SetRenderTarget(buffer, _MaskTexture, _DepthTexture);
Helpers.ScaleViewport(camera, buffer, _MaskTexture);
_Renderer.SetUpMask(buffer, _MaskTarget, _DepthTarget);
_Renderer.PopulateMask(buffer, camera);
var size = _MaskTexture.GetScaledSize(_MaskTexture.rtHandleProperties.currentViewportSize);
var descriptor = _MaskTexture.rt.descriptor;
descriptor.width = size.x; descriptor.height = size.y;
_Renderer.FixMaskArtefacts(buffer, descriptor, _MaskTarget);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 034fbbb00c45d493294db385ff38a629
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,78 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityHDRP
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.HighDefinition;
namespace WaveHarmonic.Crest
{
sealed class UnderwaterMaskPassHDRP : CustomPass
{
const string k_Name = "Underwater Mask";
static UnderwaterRenderer s_Renderer;
static UnderwaterMaskPass s_UnderwaterMaskPass;
static UnderwaterMaskPassHDRP s_Instance;
GameObject _GameObject;
public static void Enable(UnderwaterRenderer renderer)
{
var gameObject = CustomPassHelpers.CreateOrUpdate
(
parent: renderer._Water.Container.transform,
k_Name,
hide: !renderer._Water._Debug._ShowHiddenObjects
);
CustomPassHelpers.CreateOrUpdate
(
gameObject,
ref s_Instance,
k_Name,
CustomPassInjectionPoint.BeforeRendering
);
s_Instance._GameObject = gameObject;
s_Renderer = renderer;
s_UnderwaterMaskPass = new(renderer);
}
public static void Disable()
{
// It should be safe to rely on this reference for this reference to fail.
if (s_Instance != null && s_Instance._GameObject != null)
{
// Will also trigger Cleanup below.
s_Instance._GameObject.SetActive(false);
}
}
protected override void Setup(ScriptableRenderContext renderContext, CommandBuffer cmd)
{
s_UnderwaterMaskPass.Allocate();
}
protected override void Cleanup()
{
s_UnderwaterMaskPass?.Release();
}
protected override void Execute(CustomPassContext context)
{
var camera = context.hdCamera.camera;
if (!s_Renderer.ShouldRender(camera, UnderwaterRenderer.Pass.Mask))
{
return;
}
s_UnderwaterMaskPass.Execute(camera, context.cmd);
}
}
}
#endif // d_UnityHDRP

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 3fa78b61faddf4493ae6381f85fb2572
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,106 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if d_UnityURP
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
namespace WaveHarmonic.Crest
{
sealed partial class UnderwaterMaskPassURP : ScriptableRenderPass
{
const string k_Name = "Crest Underwater Mask";
static UnderwaterMaskPassURP s_Instance;
UnderwaterRenderer _Renderer;
UnderwaterMaskPass _UnderwaterMaskPass;
public UnderwaterMaskPassURP()
{
// Will always execute and matrices will be ready.
renderPassEvent = RenderPassEvent.BeforeRenderingPrePasses;
}
public static void Enable(UnderwaterRenderer renderer)
{
s_Instance ??= new();
s_Instance._Renderer = renderer;
RenderPipelineManager.beginCameraRendering -= s_Instance.EnqueuePass;
RenderPipelineManager.beginCameraRendering += s_Instance.EnqueuePass;
RenderPipelineManager.activeRenderPipelineTypeChanged -= Disable;
RenderPipelineManager.activeRenderPipelineTypeChanged += Disable;
}
public static void Disable()
{
if (s_Instance != null) RenderPipelineManager.beginCameraRendering -= s_Instance.EnqueuePass;
RenderPipelineManager.activeRenderPipelineTypeChanged -= Disable;
s_Instance?._UnderwaterMaskPass?.Release();
s_Instance = null;
}
void EnqueuePass(ScriptableRenderContext context, Camera camera)
{
if (!_Renderer.ShouldRender(camera, UnderwaterRenderer.Pass.Mask))
{
return;
}
var renderer = camera.GetUniversalAdditionalCameraData().scriptableRenderer;
#if UNITY_EDITOR
if (renderer == null) return;
#endif
if (_UnderwaterMaskPass == null)
{
_UnderwaterMaskPass = new(_Renderer);
_UnderwaterMaskPass.Allocate();
}
// Enqueue the pass. This happens every frame.
renderer.EnqueuePass(this);
}
#if UNITY_6000_0_OR_NEWER
class PassData
{
public UniversalCameraData _CameraData;
public UnderwaterMaskPass _UnderwaterMaskPass;
}
public override void RecordRenderGraph(UnityEngine.Rendering.RenderGraphModule.RenderGraph graph, ContextContainer frame)
{
using (var builder = graph.AddUnsafePass<PassData>(k_Name, out var data))
{
builder.AllowPassCulling(false);
data._CameraData = frame.Get<UniversalCameraData>();
data._UnderwaterMaskPass = _UnderwaterMaskPass;
builder.SetRenderFunc<PassData>((data, context) =>
{
var buffer = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
data._UnderwaterMaskPass.ReAllocate(data._CameraData.cameraTargetDescriptor);
data._UnderwaterMaskPass.Execute(data._CameraData.camera, buffer);
});
}
}
[System.Obsolete]
#endif
public override void Execute(ScriptableRenderContext context, ref RenderingData data)
{
var buffer = CommandBufferPool.Get(k_Name);
_UnderwaterMaskPass.ReAllocate(data.cameraData.cameraTargetDescriptor);
_UnderwaterMaskPass.Execute(data.cameraData.camera, buffer);
context.ExecuteCommandBuffer(buffer);
CommandBufferPool.Release(buffer);
}
}
}
#endif // d_UnityURP

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1ce8d0e0aca6a47a9b0b5a8a7544a064
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,32 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
#if UNITY_EDITOR
using UnityEngine;
using WaveHarmonic.Crest.Editor;
namespace WaveHarmonic.Crest
{
// Edit Mode.
partial class UnderwaterRenderer
{
static bool IsFogEnabledForEditorCamera(Camera camera)
{
// Check if scene view has disabled fog rendering.
if (camera.cameraType == CameraType.SceneView)
{
var sceneView = EditorHelpers.GetSceneViewFromSceneCamera(camera);
// Skip rendering if fog is disabled or for some reason we could not find the scene view.
if (sceneView == null || !sceneView.sceneViewState.fogEnabled)
{
return false;
}
}
return true;
}
}
}
#endif

View File

@@ -1,17 +0,0 @@
fileFormatVersion: 2
guid: 9c58e49fb2a8646388cd64da7f35b182
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _volumeGeometry: {instanceID: 0}
- _EffectMaterial: {instanceID: 0}
- _MaskMaterial: {instanceID: 0}
- _VolumeMaterial: {instanceID: 0}
- _fixMaskComputeShader: {fileID: 7200000, guid: 08549c36146ad4899a07193754b21ea2,
type: 3}
executionOrder: 201
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,315 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
partial class UnderwaterRenderer
{
const string k_KeywordFullScreenEffect = "_FULL_SCREEN_EFFECT";
const string k_KeywordDebugVisualizeMask = "_DEBUG_VISUALIZE_MASK";
const string k_KeywordDebugVisualizeStencil = "_DEBUG_VISUALIZE_STENCIL";
internal const string k_KeywordUnderwaterObjects = "CREST_UNDERWATER_OBJECTS_PASS";
static partial class ShaderIDs
{
// Local
public static readonly int s_HorizonNormal = Shader.PropertyToID("_Crest_HorizonNormal");
// Global
public static readonly int s_CameraColorTexture = Shader.PropertyToID("_Crest_CameraColorTexture");
public static readonly int s_WaterVolumeStencil = Shader.PropertyToID("_Crest_WaterVolumeStencil");
public static readonly int s_AmbientLighting = Shader.PropertyToID("_Crest_AmbientLighting");
public static readonly int s_ExtinctionMultiplier = Shader.PropertyToID("_Crest_ExtinctionMultiplier");
public static readonly int s_UnderwaterEnvironmentalLightingWeight = Shader.PropertyToID("_Crest_UnderwaterEnvironmentalLightingWeight");
// Built-ins
public static readonly int s_WorldSpaceLightPos0 = Shader.PropertyToID("_WorldSpaceLightPos0");
public static readonly int s_LightColor0 = Shader.PropertyToID("_LightColor0");
}
// These map to passes in the underwater shader.
internal enum EffectPass
{
FullScreen,
Reflections,
}
CommandBuffer _EffectCommandBuffer;
Material _CurrentWaterMaterial;
readonly UnderwaterSphericalHarmonicsData _SphericalHarmonicsData = new();
System.Action<CommandBuffer> _CopyColor;
RenderTargetIdentifier _ColorTarget = new
(
BuiltinRenderTextureType.CameraTarget,
0,
CubemapFace.Unknown,
-1
);
RenderTargetIdentifier _DepthStencilTarget = new
(
ShaderIDs.s_WaterVolumeStencil,
0,
CubemapFace.Unknown,
-1
);
RenderTargetIdentifier _ColorCopyTarget = new
(
ShaderIDs.s_CameraColorTexture,
0,
CubemapFace.Unknown,
-1
);
sealed class UnderwaterSphericalHarmonicsData
{
internal Color[] _AmbientLighting = new Color[1];
internal Vector3[] _DirectionsSH = { new(0.0f, 0.0f, 0.0f) };
}
void CopyColorTexture(CommandBuffer buffer)
{
// Use blit instead of CopyTexture as it will smooth out issues with format
// differences which is very hard to get right for BIRP.
buffer.Blit(BuiltinRenderTextureType.CameraTarget, _ColorCopyTarget);
if (UseStencilBuffer)
{
_EffectCommandBuffer.SetRenderTarget(_ColorTarget, _DepthStencilTarget);
}
else
{
_EffectCommandBuffer.SetRenderTarget(_ColorTarget);
}
}
void SetupUnderwaterEffect()
{
_EffectCommandBuffer ??= new()
{
name = "Underwater Pass",
};
_CopyColor ??= new(CopyColorTexture);
}
void OnPreRenderUnderwaterEffect(Camera camera)
{
#if UNITY_EDITOR
// Do not use this to prevent the mask from rendering due to portals and volumes feature.
if (!IsFogEnabledForEditorCamera(camera))
{
_EffectCommandBuffer?.Clear();
return;
}
#endif
var descriptor = XRHelpers.GetRenderTextureDescriptor(camera);
descriptor.useDynamicScale = camera.allowDynamicResolution;
// Format must be correct for CopyTexture to work. Hopefully this is good enough.
if (camera.allowHDR && QualitySettings.activeColorSpace == ColorSpace.Linear)
{
descriptor.graphicsFormat = SystemInfo.GetGraphicsFormat(_Water.LikelyFrameBufferFormat);
}
UpdateEffectMaterial(camera, _FirstRender);
_EffectCommandBuffer.Clear();
// No need to clear as Blit will overwrite everything.
_EffectCommandBuffer.GetTemporaryRT(ShaderIDs.s_CameraColorTexture, descriptor);
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
_EffectCommandBuffer.SetGlobalVector(ShaderIDs.s_LightColor0, sun.FinalColor());
_EffectCommandBuffer.SetGlobalVector(ShaderIDs.s_WorldSpaceLightPos0, -sun.transform.forward);
}
// Create a separate stencil buffer context by copying the depth texture.
if (UseStencilBuffer)
{
descriptor.colorFormat = RenderTextureFormat.Depth;
descriptor.depthBufferBits = (int)k_DepthBits;
// bindMS is necessary in this case for depth.
descriptor.SetMSAASamples(camera);
descriptor.bindMS = descriptor.msaaSamples > 1;
// No need to clear as Blit will overwrite everything.
_EffectCommandBuffer.GetTemporaryRT(ShaderIDs.s_WaterVolumeStencil, descriptor);
// Use blit for MSAA. We should be able to use CopyTexture. Might be the following bug:
// https://issuetracker.unity3d.com/product/unity/issues/guid/1308132
if (Helpers.IsMSAAEnabled(camera))
{
// Blit with a depth write shader to populate the depth buffer.
Helpers.Blit(_EffectCommandBuffer, _DepthStencilTarget, Helpers.UtilityMaterial, (int)Helpers.UtilityPass.CopyDepth);
}
else
{
// Copy depth then clear stencil.
_EffectCommandBuffer.CopyTexture(BuiltinRenderTextureType.Depth, _DepthStencilTarget);
Helpers.Blit(_EffectCommandBuffer, _DepthStencilTarget, Helpers.UtilityMaterial, (int)Helpers.UtilityPass.ClearStencil);
}
}
CopyColorTexture(_EffectCommandBuffer);
_EffectCommandBuffer.SetGlobalTexture(ShaderIDs.s_CameraColorTexture, _ColorCopyTarget);
ExecuteEffect(camera, _EffectCommandBuffer, _CopyColor);
_EffectCommandBuffer.ReleaseTemporaryRT(ShaderIDs.s_CameraColorTexture);
if (UseStencilBuffer)
{
_EffectCommandBuffer.ReleaseTemporaryRT(ShaderIDs.s_WaterVolumeStencil);
}
}
internal void ExecuteEffect(Camera camera, CommandBuffer buffer, System.Action<CommandBuffer> copyColor, MaterialPropertyBlock properties = null)
{
if (camera.cameraType == CameraType.Reflection)
{
buffer.DrawProcedural
(
Matrix4x4.identity,
_VolumeMaterial,
shaderPass: (int)EffectPass.Reflections,
MeshTopology.Triangles,
vertexCount: 3,
instanceCount: 1,
properties
);
}
#if d_CrestPortals
else if (_Portals.Active && _Portals.Mode != Portals.PortalMode.Tunnel)
{
_Portals.RenderEffect(camera, buffer, _VolumeMaterial, copyColor, properties);
}
#endif
else
{
buffer.DrawProcedural
(
Matrix4x4.identity,
_VolumeMaterial,
shaderPass: (int)EffectPass.FullScreen,
MeshTopology.Triangles,
vertexCount: 3,
instanceCount: 1,
properties
);
}
}
internal static void UpdateGlobals(Material waterMaterial)
{
// We will have the wrong color values if we do not use linear:
// https://forum.unity.com/threads/fragment-shader-output-colour-has-incorrect-values-when-hardcoded.377657/
// _CrestAbsorption is already set as global in Water Renderer.
Shader.SetGlobalColor(WaterRenderer.ShaderIDs.s_Scattering, waterMaterial.GetColor(WaterRenderer.ShaderIDs.s_Scattering).MaybeLinear());
Shader.SetGlobalFloat(WaterRenderer.ShaderIDs.s_Anisotropy, waterMaterial.GetFloat(WaterRenderer.ShaderIDs.s_Anisotropy));
}
internal void UpdateEffectMaterial(Camera camera, bool isFirstRender)
{
// Copy water material parameters to underwater material.
{
var material = _SurfaceMaterial;
if (_CopyWaterMaterialParametersEachFrame || isFirstRender || material != _CurrentWaterMaterial)
{
_CurrentWaterMaterial = material;
if (material != null)
{
_VolumeMaterial.CopyMatchingPropertiesFromMaterial(material);
if (_EnableShaderAPI)
{
UpdateGlobals(material);
}
}
}
}
// Enabling/disabling keywords each frame don't seem to have large measurable overhead
_VolumeMaterial.SetKeyword(k_KeywordDebugVisualizeMask, _Debug._VisualizeMask);
_VolumeMaterial.SetKeyword(k_KeywordDebugVisualizeStencil, _Debug._VisualizeStencil);
// We use this for caustics to get the displacement.
_VolumeMaterial.SetInteger(Lod.ShaderIDs.s_LodIndex, 0);
if (!Portaled && camera.cameraType != CameraType.Reflection)
{
var seaLevel = _Water.SeaLevel;
// We don't both setting the horizon value if we know we are going to be having to apply the effect
// full-screen anyway.
var forceFullShader = _Water.ViewerHeightAboveWater < -2f;
if (!forceFullShader)
{
var maxWaterVerticalDisplacement = _Water.MaximumVerticalDisplacement * 0.5f;
var cameraYPosition = camera.transform.position.y;
float nearPlaneFrustumWorldHeight;
{
var current = camera.ViewportToWorldPoint(new(0f, 0f, camera.nearClipPlane)).y;
float maxY = current, minY = current;
current = camera.ViewportToWorldPoint(new(0f, 1f, camera.nearClipPlane)).y;
maxY = Mathf.Max(maxY, current);
minY = Mathf.Min(minY, current);
current = camera.ViewportToWorldPoint(new(1f, 0f, camera.nearClipPlane)).y;
maxY = Mathf.Max(maxY, current);
minY = Mathf.Min(minY, current);
current = camera.ViewportToWorldPoint(new(1f, 1f, camera.nearClipPlane)).y;
maxY = Mathf.Max(maxY, current);
minY = Mathf.Min(minY, current);
nearPlaneFrustumWorldHeight = maxY - minY;
}
forceFullShader = (cameraYPosition + nearPlaneFrustumWorldHeight + maxWaterVerticalDisplacement) <= seaLevel;
}
_VolumeMaterial.SetKeyword(k_KeywordFullScreenEffect, forceFullShader);
}
// Project water normal onto camera plane.
{
var projectedNormal = new Vector2
(
Vector3.Dot(Vector3.up, camera.transform.right),
Vector3.Dot(Vector3.up, camera.transform.up)
);
_VolumeMaterial.SetVector(ShaderIDs.s_HorizonNormal, projectedNormal);
}
// Compute ambient lighting SH.
{
// We could pass in a renderer which would prime this lookup. However it doesnt make sense to use an existing render
// at different position, as this would then thrash it and negate the priming functionality. We could create a dummy invis GO
// with a dummy Renderer which might be enough, but this is hacky enough that we'll wait for it to become a problem
// rather than add a pre-emptive hack.
UnityEngine.Profiling.Profiler.BeginSample("Crest: Underwater Sample Spherical Harmonics");
LightProbes.GetInterpolatedProbe(camera.transform.position, null, out var sphericalHarmonicsL2);
sphericalHarmonicsL2.Evaluate(_SphericalHarmonicsData._DirectionsSH, _SphericalHarmonicsData._AmbientLighting);
Helpers.SetShaderVector(_VolumeMaterial, ShaderIDs.s_AmbientLighting, _SphericalHarmonicsData._AmbientLighting[0], _EnableShaderAPI);
UnityEngine.Profiling.Profiler.EndSample();
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 95fe330fa426a41c0b6379a1a2aae608
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,120 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest
{
partial class UnderwaterRenderer
{
const float k_DepthOutScattering = 0.25f;
Light _EnvironmentalLight;
float _EnvironmentalLightIntensity;
float _EnvironmentalAmbientIntensity;
float _EnvironmentalReflectionIntensity;
float _EnvironmentalFogDensity;
float _EnvironmentalAverageDensity = 0f;
bool _EnvironmentalInitialized = false;
bool _EnvironmentalNeedsRestoring;
void EnableEnvironmentalLighting()
{
if (!_EnvironmentalLightingEnable)
{
return;
}
#if d_UnitySRP
if (_EnvironmentalLightingVolume == null && !RenderPipelineHelper.IsLegacy)
{
// Create volume to weigh in underwater profile
var go = new GameObject();
go.transform.parent = _Water.Container.transform;
go.hideFlags = HideFlags.HideAndDontSave;
go.name = "Underwater Lighting Volume";
_EnvironmentalLightingVolume = go.AddComponent<Volume>();
_EnvironmentalLightingVolume.weight = 0;
_EnvironmentalLightingVolume.priority = 1000;
_EnvironmentalLightingVolume.profile = _EnvironmentalLightingVolumeProfile;
}
#endif
_EnvironmentalInitialized = true;
}
void DisableEnvironmentalLighting()
{
RestoreEnvironmentalLighting();
_EnvironmentalInitialized = false;
}
void RestoreEnvironmentalLighting()
{
if (!_EnvironmentalInitialized || !_EnvironmentalNeedsRestoring)
{
return;
}
// Restore lighting settings.
if (_EnvironmentalLight != null) _EnvironmentalLight.intensity = _EnvironmentalLightIntensity;
_EnvironmentalLight = null;
RenderSettings.ambientIntensity = _EnvironmentalAmbientIntensity;
RenderSettings.reflectionIntensity = _EnvironmentalReflectionIntensity;
RenderSettings.fogDensity = _EnvironmentalFogDensity;
Shader.SetGlobalFloat(ShaderIDs.s_UnderwaterEnvironmentalLightingWeight, 0f);
if (_EnvironmentalLightingVolume != null) _EnvironmentalLightingVolume.weight = 0;
_EnvironmentalNeedsRestoring = false;
}
void UpdateEnvironmentalLighting(Camera camera, Vector3 extinction, float height)
{
if (!_EnvironmentalInitialized)
{
return;
}
if (!_Water.Material.HasColor(WaterRenderer.ShaderIDs.s_AbsorptionColor))
{
return;
}
// Store lighting settings.
{
_EnvironmentalLight = _Water.PrimaryLight;
if (_EnvironmentalLight) _EnvironmentalLightIntensity = _EnvironmentalLight.intensity;
_EnvironmentalAmbientIntensity = RenderSettings.ambientIntensity;
_EnvironmentalReflectionIntensity = RenderSettings.reflectionIntensity;
_EnvironmentalFogDensity = RenderSettings.fogDensity;
}
var density = extinction;
_EnvironmentalAverageDensity = (density.x + density.y + density.z) / 3f;
var multiplier = Mathf.Exp(_EnvironmentalAverageDensity * Mathf.Min(height * k_DepthOutScattering, 0f) * _EnvironmentalLightingWeight);
// Darken environmental lighting when viewer underwater.
if (_EnvironmentalLight != null)
{
_EnvironmentalLight.intensity = Mathf.Lerp(0, _EnvironmentalLightIntensity, multiplier);
}
RenderSettings.ambientIntensity = Mathf.Lerp(0, _EnvironmentalAmbientIntensity, multiplier);
RenderSettings.reflectionIntensity = Mathf.Lerp(0, _EnvironmentalReflectionIntensity, multiplier);
RenderSettings.fogDensity = Mathf.Lerp(0, _EnvironmentalFogDensity, multiplier);
Shader.SetGlobalFloat(ShaderIDs.s_UnderwaterEnvironmentalLightingWeight, 1f - multiplier);
#if d_UnitySRP
if (_EnvironmentalLightingVolume != null)
{
_EnvironmentalLightingVolume.weight = 1f - multiplier;
}
#endif
_EnvironmentalNeedsRestoring = true;
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 42735f62770724c7488928b7e8185c9c
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,101 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
namespace WaveHarmonic.Crest
{
partial class UnderwaterRenderer
{
bool _HasMaskCommandBuffersBeenRegistered;
bool _HasEffectCommandBuffersBeenRegistered;
void OnEnableLegacy()
{
SetupMask();
OnEnableMask();
SetupUnderwaterEffect();
// Handle this internally rather than relying on Water Renderer.
Camera.onPreRender -= OnBeforeLegacyRender;
Camera.onPreRender += OnBeforeLegacyRender;
Camera.onPostRender -= OnAfterLegacyRender;
Camera.onPostRender += OnAfterLegacyRender;
RenderPipelineManager.activeRenderPipelineTypeChanged -= OnDisableLegacy;
RenderPipelineManager.activeRenderPipelineTypeChanged += OnDisableLegacy;
}
void OnDisableLegacy()
{
Camera.onPreRender -= OnBeforeLegacyRender;
Camera.onPostRender -= OnAfterLegacyRender;
RenderPipelineManager.activeRenderPipelineTypeChanged -= OnDisableLegacy;
OnDisableMask();
}
internal void LateUpdate()
{
if (!Active)
{
return;
}
if (!RenderPipelineHelper.IsLegacy)
{
return;
}
Helpers.SetGlobalKeyword("CREST_UNDERWATER_BEFORE_TRANSPARENT", _EnableShaderAPI);
}
void OnBeforeLegacyRender(Camera camera)
{
XRHelpers.Update(camera);
XRHelpers.SetInverseViewProjectionMatrix(camera);
if (ShouldRender(camera, Pass.Mask))
{
// It could be either one event.
camera.AddCommandBuffer(CameraEvent.BeforeGBuffer, _MaskCommandBuffer);
camera.AddCommandBuffer(CameraEvent.BeforeDepthTexture, _MaskCommandBuffer);
OnPreRenderMask(camera);
_HasMaskCommandBuffersBeenRegistered = true;
}
if (ShouldRender(camera, Pass.Effect))
{
var @event = _EnableShaderAPI ? CameraEvent.BeforeForwardAlpha : CameraEvent.AfterForwardAlpha;
camera.AddCommandBuffer(@event, _EffectCommandBuffer);
OnPreRenderUnderwaterEffect(camera);
_HasEffectCommandBuffersBeenRegistered = true;
}
_FirstRender = false;
}
void OnAfterLegacyRender(Camera camera)
{
if (_HasMaskCommandBuffersBeenRegistered)
{
// It could be either one event.
camera.RemoveCommandBuffer(CameraEvent.BeforeGBuffer, _MaskCommandBuffer);
camera.RemoveCommandBuffer(CameraEvent.BeforeDepthTexture, _MaskCommandBuffer);
_MaskCommandBuffer?.Clear();
}
if (_HasEffectCommandBuffersBeenRegistered)
{
var @event = _EnableShaderAPI ? CameraEvent.BeforeForwardAlpha : CameraEvent.AfterForwardAlpha;
camera.RemoveCommandBuffer(@event, _EffectCommandBuffer);
_EffectCommandBuffer?.Clear();
}
_HasMaskCommandBuffersBeenRegistered = false;
_HasEffectCommandBuffersBeenRegistered = false;
OnAfterCameraRender(camera);
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: f72cef5b74e7e43c3bfceff42401fa82
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,253 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
partial class UnderwaterRenderer
{
// Adapted from:
// Packages/com.unity.render-pipelines.universal/Runtime/UniversalRenderer.cs
#if UNITY_SWITCH || UNITY_ANDROID || UNITY_EMBEDDED_LINUX || UNITY_QNX
internal const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D24_UNorm_S8_UInt;
internal const int k_DepthBufferBits = 24;
internal const DepthBits k_DepthBits = DepthBits.Depth24;
#else
internal const GraphicsFormat k_DepthStencilFormat = GraphicsFormat.D32_SFloat_S8_UInt;
internal const int k_DepthBufferBits = 32;
internal const DepthBits k_DepthBits = DepthBits.Depth32;
#endif
internal const int k_ShaderPassWaterSurfaceMask = 0;
internal const int k_ShaderPassWaterSurfaceDepth = 1;
internal const int k_ShaderPassWaterHorizonMask = 2;
// NOTE: Must match CREST_MASK_BELOW_SURFACE in Constants.hlsl.
const float k_MaskBelowSurface = -1f;
// NOTE: Must match CREST_MASK_BELOW_SURFACE_CULLED in Constants.hlsl.
const float k_MaskBelowSurfaceCull = -2f;
internal const string k_ComputeShaderKernelFillMaskArtefacts = "FillMaskArtefacts";
static partial class ShaderIDs
{
// Local
public static readonly int s_FarPlaneOffset = Shader.PropertyToID("_Crest_FarPlaneOffset");
public static readonly int s_MaskBelowSurface = Shader.PropertyToID("_Crest_MaskBelowSurface");
// Global
public static readonly int s_WaterMaskTexture = Shader.PropertyToID("_Crest_WaterMaskTexture");
public static readonly int s_WaterMaskDepthTexture = Shader.PropertyToID("_Crest_WaterMaskDepthTexture");
public static readonly int s_StencilRef = Shader.PropertyToID("_StencilRef");
}
internal Material _MaskMaterial;
internal RenderTargetIdentifier _MaskTarget;
internal RenderTargetIdentifier _DepthTarget;
internal readonly Plane[] _CameraFrustumPlanes = new Plane[6];
CommandBuffer _MaskCommandBuffer;
internal RenderTexture _MaskRT;
RenderTexture _DepthRT;
ComputeShader _ArtifactsShader;
bool _ArtifactsShaderInitialized;
int _ArtifactsKernel;
uint _ArtifactsThreadGroupSizeX;
uint _ArtifactsThreadGroupSizeY;
void SetupMask()
{
_MaskCommandBuffer ??= new()
{
name = "Crest: Underwater Mask",
};
}
internal void OnEnableMask()
{
// Create a reference to handle the RT. The RT properties will be replaced with a descriptor before the
// native object is created, and since it is lazy it is near zero cost.
Helpers.CreateRenderTargetTextureReference(ref _MaskRT, ref _MaskTarget);
_MaskRT.name = "_Crest_WaterMaskTexture";
Helpers.CreateRenderTargetTextureReference(ref _DepthRT, ref _DepthTarget);
_DepthRT.name = "_Crest_WaterMaskDepthTexture";
SetUpArtifactsShader();
}
internal void OnDisableMask()
{
if (_MaskRT != null) _MaskRT.Release();
if (_DepthRT != null) _DepthRT.Release();
}
internal void SetUpArtifactsShader()
{
if (_ArtifactsShaderInitialized)
{
return;
}
_ArtifactsKernel = _ArtifactsShader.FindKernel(k_ComputeShaderKernelFillMaskArtefacts);
_ArtifactsShader.GetKernelThreadGroupSizes
(
_ArtifactsKernel,
out _ArtifactsThreadGroupSizeX,
out _ArtifactsThreadGroupSizeY,
out _
);
_ArtifactsShaderInitialized = true;
}
internal void SetUpMaskTextures(RenderTextureDescriptor descriptor)
{
if (!Helpers.RenderTargetTextureNeedsUpdating(_MaskRT, descriptor))
{
return;
}
// This will disable MSAA for our textures as MSAA will break sampling later on. This looks safe to do as
// Unity's CopyDepthPass does the same, but a possible better way or supporting MSAA is worth looking into.
descriptor.msaaSamples = 1;
// @Memory: We could investigate making this an 8-bit texture instead to reduce GPU memory usage.
// @Memory: We could potentially try a half resolution mask as the mensicus could mask resolution issues.
// Intel iGPU for Metal and DirectX both had issues with R16. 2021.11.18
descriptor.colorFormat = Helpers.IsIntelGPU() ? RenderTextureFormat.RFloat : RenderTextureFormat.RHalf;
descriptor.depthBufferBits = 0;
descriptor.enableRandomWrite = true;
_MaskRT.Release();
_MaskRT.descriptor = descriptor;
descriptor.colorFormat = RenderTextureFormat.Depth;
descriptor.depthBufferBits = (int)k_DepthBits;
descriptor.enableRandomWrite = false;
_DepthRT.Release();
_DepthRT.descriptor = descriptor;
}
void OnPreRenderMask(Camera camera)
{
_MaskCommandBuffer.Clear();
var descriptor = XRHelpers.GetRenderTextureDescriptor(camera);
descriptor.useDynamicScale = camera.allowDynamicResolution;
// Keywords and other things.
SetUpMaskTextures(descriptor);
#if d_CrestPortals
// Populate water volume before mask so we can use the stencil.
if (_Portals.Active)
{
_Portals.ReAllocate(descriptor);
_Portals.RenderMask(camera, _MaskCommandBuffer, _MaskMaterial);
_Portals.RenderStencil(_MaskCommandBuffer, _DepthRT, _DepthTarget);
}
#endif
_MaskCommandBuffer.SetRenderTarget(_MaskTarget, _DepthTarget);
SetUpMask(_MaskCommandBuffer, _MaskTarget, _DepthTarget);
PopulateMask(_MaskCommandBuffer, camera);
FixMaskArtefacts(_MaskCommandBuffer, descriptor, _MaskTarget);
}
internal void SetUpMask(CommandBuffer buffer, RenderTargetIdentifier maskTarget, RenderTargetIdentifier depthTarget)
{
// When using the stencil we are already clearing depth and do not want to clear the stencil too. Clear
// color only when using the stencil as the horizon effectively clears it when not using it.
buffer.ClearRenderTarget(!UseStencilBuffer, UseStencilBuffer, Color.black);
buffer.SetGlobalTexture(ShaderIDs.s_WaterMaskTexture, maskTarget);
buffer.SetGlobalTexture(ShaderIDs.s_WaterMaskDepthTexture, depthTarget);
}
internal void FixMaskArtefacts(CommandBuffer buffer, RenderTextureDescriptor descriptor, RenderTargetIdentifier target)
{
if (_Debug._DisableArtifactCorrection)
{
return;
}
buffer.SetComputeTextureParam(_ArtifactsShader, _ArtifactsKernel, ShaderIDs.s_WaterMaskTexture, target);
// XR SPI will have a volume depth of two. If using RTHandles, then set manually as will be two for all cameras.
_ArtifactsShader.SetKeyword("STEREO_INSTANCING_ON", descriptor.dimension == TextureDimension.Tex2DArray);
buffer.DispatchCompute
(
_ArtifactsShader,
_ArtifactsKernel,
// Viewport sizes are not perfect so round up to cover.
Mathf.CeilToInt((float)descriptor.width / _ArtifactsThreadGroupSizeX),
Mathf.CeilToInt((float)descriptor.height / _ArtifactsThreadGroupSizeY),
descriptor.volumeDepth
);
}
// Populates a screen space mask which will inform the underwater postprocess. As a future optimisation we may
// be able to avoid this pass completely if we can reuse the camera depth after transparents are rendered.
internal void PopulateMask(CommandBuffer commandBuffer, Camera camera)
{
// Render horizon into mask using a fullscreen triangle at the far plane. Horizon must be rendered first or
// it will overwrite the mask with incorrect values.
{
var zBufferParameters = Helpers.GetZBufferParameters(camera);
// Take 0-1 linear depth and convert non-linear depth.
_MaskMaterial.SetFloat(ShaderIDs.s_FarPlaneOffset, Helpers.LinearDepthToNonLinear(_FarPlaneMultiplier, zBufferParameters));
// Render fullscreen triangle with horizon mask pass.
commandBuffer.DrawProcedural(Matrix4x4.identity, _MaskMaterial, shaderPass: k_ShaderPassWaterHorizonMask, MeshTopology.Triangles, 3, 1);
}
GeometryUtility.CalculateFrustumPlanes(camera, _CameraFrustumPlanes);
// Get all water chunks and render them using cmd buffer, but with mask shader.
if (!_Debug._DisableMask)
{
// Spends approx 0.2-0.3ms here on 2018 Dell XPS 15.
foreach (var chunk in _Water.Chunks)
{
var renderer = chunk.Rend;
// Can happen in edit mode.
if (renderer == null) continue;
var bounds = renderer.bounds;
if (GeometryUtility.TestPlanesAABB(_CameraFrustumPlanes, bounds))
{
if ((!chunk._WaterDataHasBeenBound) && chunk.enabled)
{
chunk.Bind(camera);
}
// Handle culled tiles for when underwater is rendered before the transparent pass.
chunk._MaterialPropertyBlock.SetFloat(ShaderIDs.s_MaskBelowSurface, !_EnableShaderAPI || renderer.enabled ? k_MaskBelowSurface : k_MaskBelowSurfaceCull);
renderer.SetPropertyBlock(chunk._MaterialPropertyBlock);
commandBuffer.DrawRenderer(renderer, _MaskMaterial, submeshIndex: 0, shaderPass: k_ShaderPassWaterSurfaceMask);
chunk._Visible = true;
}
chunk._WaterDataHasBeenBound = false;
}
#if d_CrestPortals
if (_Portals.Active)
{
_Portals.PopulateMask(commandBuffer, _MaskMaterial);
}
#endif // d_CrestPortals
}
}
}
}

View File

@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: f64799bb3430e498a926913b81241f06
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1,475 +0,0 @@
// Crest Water System
// Copyright © 2024 Wave Harmonic. All rights reserved.
using UnityEngine;
using UnityEngine.Rendering;
using WaveHarmonic.Crest.Internal;
namespace WaveHarmonic.Crest
{
/// <summary>
/// Renders the underwater effect.
/// </summary>
[System.Serializable]
public sealed partial class UnderwaterRenderer
{
[SerializeField, HideInInspector]
#pragma warning disable 414
int _Version = 0;
#pragma warning restore 414
internal const float k_CullLimitMinimum = 0.000001f;
internal const float k_CullLimitMaximum = 0.01f;
[@Space(10)]
[Tooltip("Whether the underwater effect is enabled.\n\nAllocates/releases resources if state has changed.")]
[@GenerateAPI(Setter.Custom)]
[@DecoratedField, SerializeField]
internal bool _Enabled = true;
[Tooltip("Any camera or probe with this layer in its culling mask will render underwater.")]
[@Layer]
[@GenerateAPI]
[SerializeField]
int _Layer = 4; // Water
[Tooltip("The underwater material. The water surface material is copied into this material.")]
[@AttachMaterialEditor]
[@MaterialField("Crest/Underwater", name: "Underwater", title: "Create Underwater Material")]
[@GenerateAPI]
[SerializeField]
internal Material _Material;
[@Heading("Environmental Lighting")]
[@Label("Enable")]
[Tooltip("Provides out-scattering based on the camera's underwater depth.\n\nIt scales down environmental lighting (sun, reflections, ambient etc) with the underwater depth. This works with vanilla lighting, but uncommon or custom lighting will require a custom solution (use this for reference)")]
[@GenerateAPI(Setter.Custom, name: "AffectsEnvironmentalLighting")]
[@DecoratedField, SerializeField]
internal bool _EnvironmentalLightingEnable;
[@Label("Weight")]
[Tooltip("How much this effect applies.\n\nValues less than 1 attenuate light less underwater. Value of 1 is physically based.")]
[@Range(0, 3)]
[@GenerateAPI]
[SerializeField]
internal float _EnvironmentalLightingWeight = 1f;
#if d_UnitySRP
[@Label("Volume")]
[Tooltip("This profile will be weighed in the deeper underwater the camera goes.")]
[@Predicated(RenderPipeline.HighDefinition, hide: true)]
[@DecoratedField, SerializeField]
VolumeProfile _EnvironmentalLightingVolumeProfile = null;
Volume _EnvironmentalLightingVolume;
#endif
[@Heading("Shader API")]
[Tooltip("Renders the underwater effect before the transparent pass (instead of after).\n\nSo one can apply the underwater fog themselves to transparent objects. Cannot be changed at runtime.")]
[@DecoratedField, SerializeField]
[HideInInspector]
bool _EnableShaderAPI = false;
internal bool EnableShaderAPI { get => _EnableShaderAPI; set => _EnableShaderAPI = value; }
[@Predicated(nameof(_EnableShaderAPI))]
[@Predicated(RenderPipeline.Legacy, inverted: true, hide: true)]
[@DecoratedField, SerializeField]
[HideInInspector]
internal LayerMask _TransparentObjectLayers;
[@Heading("Advanced")]
[Tooltip("Whether to execute for all cameras.\n\nIf disabled, then additionally ignore any camera that is not the view camera or our reflection camera. It will require managing culling masks of all cameras.")]
[@GenerateAPI]
[@DecoratedField, SerializeField]
bool _AllCameras;
[Tooltip("Copying parameters each frame ensures underwater appearance stays consistent with the water surface.\n\nHas a small overhead so should be disabled if not needed.")]
[@GenerateAPI]
[@DecoratedField, SerializeField]
bool _CopyWaterMaterialParametersEachFrame = true;
[Tooltip("Adjusts the far plane for horizon line calculation. Helps with horizon line issue.")]
[@Range(0f, 1f)]
[@GenerateAPI]
[SerializeField]
float _FarPlaneMultiplier = 0.68f;
[Tooltip("Proportion of visibility below which the water surface will be culled when underwater.\n\nThe larger the number, the closer to the camera the water tiles will be culled.")]
[@Range(k_CullLimitMinimum, k_CullLimitMaximum)]
[@GenerateAPI]
[SerializeField]
internal float _CullLimit = 0.001f;
[@Space(10)]
[@DecoratedField, SerializeField]
DebugFields _Debug = new();
[System.Serializable]
sealed class DebugFields
{
[SerializeField]
internal bool _VisualizeMask;
[SerializeField]
internal bool _DisableMask;
[SerializeField]
internal bool _VisualizeStencil;
[SerializeField]
internal bool _DisableHeightAboveWaterOptimization;
[SerializeField]
internal bool _DisableArtifactCorrection;
[SerializeField]
internal bool _OnlyReflectionCameras;
}
internal WaterRenderer _Water;
#if d_CrestPortals
// BUG: NonSerialized as Unity shows a serialization depth warning even though field is internal.
[System.NonSerialized]
internal Portals.PortalRenderer _Portals;
bool Portaled => _Portals.Active;
#else
bool Portaled => false;
#endif
bool _FirstRender = true;
internal bool UseStencilBuffer { get; set; }
internal enum Pass
{
Culling,
Mask,
Effect,
}
// These are the materials we actually use, overridable by Water Body.
Material _SurfaceMaterial;
Material _VolumeMaterial;
readonly SampleCollisionHelper _SamplingHeightHelper = new();
float _ViewerWaterHeight;
internal static partial class ShaderIDs
{
// Empty.
}
// Disable underwater effect if height enough above surface.
internal bool Active => _Enabled && _Material != null && _ViewerWaterHeight < 2f || Portaled || _Debug._DisableHeightAboveWaterOptimization;
internal void OnEnable()
{
_VolumeMaterial = _Material;
if (_MaskMaterial == null)
{
_MaskMaterial = new(WaterResources.Instance.Shaders._UnderwaterMask);
}
if (_ArtifactsShader == null)
{
_ArtifactsShader = WaterResources.Instance.Compute._UnderwaterArtifacts;
}
if (RenderPipelineHelper.IsUniversal)
{
#if d_UnityURP
UnderwaterMaskPassURP.Enable(this);
UnderwaterEffectPassURP.Enable(this);
#endif
}
else if (RenderPipelineHelper.IsHighDefinition)
{
#if d_UnityHDRP
UnderwaterMaskPassHDRP.Enable(this);
UnderwaterEffectPassHDRP.Enable(this);
#endif
}
else
{
OnEnableLegacy();
}
EnableEnvironmentalLighting();
RenderPipelineManager.activeRenderPipelineTypeChanged -= OnActiveRenderPipelineTypeChanged;
RenderPipelineManager.activeRenderPipelineTypeChanged += OnActiveRenderPipelineTypeChanged;
}
void OnActiveRenderPipelineTypeChanged()
{
// Disable is handled by another handler so we need to run enabled.
if (_Water.isActiveAndEnabled)
{
OnEnable();
}
}
internal void OnDisable()
{
RenderPipelineManager.activeRenderPipelineTypeChanged -= OnActiveRenderPipelineTypeChanged;
#if d_UnityURP
UnderwaterMaskPassURP.Disable();
UnderwaterEffectPassURP.Disable();
#endif
#if d_UnityHDRP
UnderwaterMaskPassHDRP.Disable();
UnderwaterEffectPassHDRP.Disable();
#endif
OnDisableLegacy();
DisableEnvironmentalLighting();
_ArtifactsShader = null;
}
internal void OnDestroy()
{
Helpers.Destroy(_MaskMaterial);
}
internal bool ShouldRender(Camera camera, Pass pass)
{
if (_Water == null)
{
return false;
}
if (!Helpers.MaskIncludesLayer(camera.cullingMask, _Layer))
{
return false;
}
#if UNITY_EDITOR
// Do not execute when editor is not active to conserve power and prevent possible leaks.
if (!UnityEditorInternal.InternalEditorUtility.isApplicationActive)
{
return false;
}
if (GL.wireframe)
{
return false;
}
// Skip camera if fog is disabled. Do not skip if mask pass and a portal or volume as we want it to still
// mask the water surface.
if ((pass != Pass.Mask || !Portaled) && !IsFogEnabledForEditorCamera(camera))
{
return false;
}
if (_Water.IsProxyPlaneRendering)
{
return false;
}
if (camera.cameraType == CameraType.Preview)
{
return false;
}
#endif
var isReflectionCamera = camera.cameraType == CameraType.Reflection;
// Mask or culling is not needed for reflections.
if (isReflectionCamera && pass != Pass.Effect)
{
return false;
}
if (_Debug._OnlyReflectionCameras && !isReflectionCamera)
{
return false;
}
// Option to exclude cameras that is not the view camera or our reflection camera.
// Otherwise, filtering depends on the camera's culling mask which is not always
// accessible like with the global "Reflection Probes Camera". But whether those
// cameras triggering camera events is a bug is TBD as it is intermittent.
if (!_AllCameras && camera != _Water.Viewer && camera.cameraType != CameraType.SceneView && camera != WaterReflections.CurrentCamera)
{
return false;
}
if (pass != Pass.Culling && !Active)
{
return false;
}
return true;
}
void RevertCulling()
{
foreach (var tile in _Water.Chunks)
{
if (tile.Rend == null || tile._Culled)
{
continue;
}
tile.Rend.enabled = true;
}
}
internal void OnBeforeCameraRender(Camera camera)
{
if (!ShouldRender(camera, Pass.Culling))
{
return;
}
var viewpoint = camera.transform.position;
_SamplingHeightHelper.SampleHeight(System.HashCode.Combine(GetHashCode(), camera.GetHashCode()), viewpoint, out var height, allowMultipleCallsPerFrame: true);
_ViewerWaterHeight = viewpoint.y - height;
// ShouldRender has a special case for this pass which skips the Active check so we
// can always continue sampling.
if (!Active)
{
return;
}
_SurfaceMaterial = _Water.AboveOrBelowSurfaceMaterial;
_VolumeMaterial = _Material;
// Grab material from a water body if camera is within its XZ bounds.
foreach (var body in WaterBody.WaterBodies)
{
if (body.AboveOrBelowSurfaceMaterial == null && body._VolumeMaterial == null)
{
continue;
}
var bounds = body.AABB;
var contained =
viewpoint.x >= bounds.min.x && viewpoint.x <= bounds.max.x &&
viewpoint.z >= bounds.min.z && viewpoint.z <= bounds.max.z;
if (contained)
{
if (body.AboveOrBelowSurfaceMaterial != null) _SurfaceMaterial = body.AboveOrBelowSurfaceMaterial;
if (body.VolumeMaterial != null) _VolumeMaterial = body.VolumeMaterial;
// Water bodies should not overlap so grab the first one.
break;
}
}
var extinction = Vector3.zero;
float minimumFogDensity = 0;
// Calculate extinction.
if (_SurfaceMaterial != null)
{
var densityFactor = _VolumeMaterial.GetFloat(ShaderIDs.s_ExtinctionMultiplier);
// Get absorption from current material.
if (_SurfaceMaterial.HasVector(WaterRenderer.ShaderIDs.s_Absorption))
{
extinction = _SurfaceMaterial.GetVector(WaterRenderer.ShaderIDs.s_Absorption);
Shader.SetGlobalVector(WaterRenderer.ShaderIDs.s_Absorption, extinction);
}
// Do not use for culling because:
// - Scattering is not uniform due to anisotropy
// - Also need to take sun light into account
if (_SurfaceMaterial.HasProperty(WaterRenderer.ShaderIDs.s_Scattering))
{
var volumeExtinction = extinction + _SurfaceMaterial.GetVector(WaterRenderer.ShaderIDs.s_Scattering).XYZ();
volumeExtinction *= densityFactor;
minimumFogDensity = Mathf.Min(Mathf.Min(volumeExtinction.x, volumeExtinction.y), volumeExtinction.z);
Shader.SetGlobalFloat(WaterRenderer.ShaderIDs.s_VolumeExtinctionLength, -Mathf.Log(k_CullLimitMinimum) / minimumFogDensity);
}
extinction *= densityFactor;
minimumFogDensity = Mathf.Min(Mathf.Min(extinction.x, extinction.y), extinction.z);
// Prevent divide by zero.
minimumFogDensity = Mathf.Max(minimumFogDensity, 0.0001f);
}
UpdateEnvironmentalLighting(camera, extinction, _ViewerWaterHeight);
if (Portaled || _ViewerWaterHeight > -5f)
{
RevertCulling();
return;
}
var extinctionLength = -Mathf.Log(_CullLimit) / minimumFogDensity;
foreach (var tile in _Water.Chunks)
{
if (tile.Rend == null || tile._Culled)
{
continue;
}
// Cull tiles the viewer cannot see through the underwater fog.
// Only run optimisation in play mode due to shared height above water.
if ((viewpoint - tile.Rend.bounds.ClosestPoint(viewpoint)).magnitude >= extinctionLength)
{
tile.Rend.enabled = false;
}
else
{
// Previous camera might have culled in underwater pass.
tile.Rend.enabled = true;
}
}
}
internal void OnAfterCameraRender(Camera camera)
{
RestoreEnvironmentalLighting();
RevertCulling();
}
void SetEnabled(bool previous, bool current)
{
if (previous == current) return;
if (_Water == null || !_Water.isActiveAndEnabled) return;
if (_Enabled) OnEnable(); else OnDisable();
}
void SetAffectsEnvironmentalLighting(bool previous, bool current)
{
if (previous == current) return;
if (_Water == null || !_Water.isActiveAndEnabled || !_Enabled) return;
if (_EnvironmentalLightingEnable) EnableEnvironmentalLighting(); else DisableEnvironmentalLighting();
}
#if UNITY_EDITOR
[@OnChange]
void OnChange(string propertyPath, object previousValue)
{
switch (propertyPath)
{
case nameof(_Enabled):
SetEnabled((bool)previousValue, _Enabled);
break;
case nameof(_EnvironmentalLightingEnable):
SetAffectsEnvironmentalLighting((bool)previousValue, _EnvironmentalLightingEnable);
break;
}
}
#endif
}
}

View File

@@ -1,16 +0,0 @@
fileFormatVersion: 2
guid: f2a407448acc440bab3414c17e06e7ba
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences:
- _VolumeGeometry: {instanceID: 0}
- _EffectMaterial: {instanceID: 0}
- _MaskMaterial: {instanceID: 0}
- _VolumeMaterial: {instanceID: 0}
- _ArtifactsShader: {fileID: 7200000, guid: 08549c36146ad4899a07193754b21ea2, type: 3}
executionOrder: 201
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: