移除水
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c581581e08ff40e689d952358cea7a0
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 03d7c1db420e64f7c9894f9c2bdaae4c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fba7ac72b29f4b12a08eb07d80a2703
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5ab3e34da699e48eaa28a35fba152510
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 034fbbb00c45d493294db385ff38a629
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fa78b61faddf4493ae6381f85fb2572
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ce8d0e0aca6a47a9b0b5a8a7544a064
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
@@ -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:
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 95fe330fa426a41c0b6379a1a2aae608
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 42735f62770724c7488928b7e8185c9c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f72cef5b74e7e43c3bfceff42401fa82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f64799bb3430e498a926913b81241f06
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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:
|
||||
Reference in New Issue
Block a user