chunk 1: core gameplay scripts scenes runtime assets
This commit is contained in:
455
Assets/Volumetric/Runtime/VolumetricFogRenderPass.cs
Normal file
455
Assets/Volumetric/Runtime/VolumetricFogRenderPass.cs
Normal file
@@ -0,0 +1,455 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Experimental.Rendering;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
using System;
|
||||
using UnityEngine.Rendering.RenderGraphModule;
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// The volumetric fog render pass.
|
||||
/// </summary>
|
||||
public sealed class VolumetricFogRenderPass : ScriptableRenderPass
|
||||
{
|
||||
#region Definitions
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
|
||||
/// <summary>
|
||||
/// The subpasses the volumetric fog render pass is made of.
|
||||
/// </summary>
|
||||
private enum PassStage : byte
|
||||
{
|
||||
DownsampleDepth,
|
||||
RenderFog,
|
||||
BlurFog,
|
||||
CompositeFog
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Holds the data needed by the execution of the volumetric fog render pass subpasses.
|
||||
/// </summary>
|
||||
private class PassData
|
||||
{
|
||||
public PassStage stage;
|
||||
|
||||
public TextureHandle target;
|
||||
public TextureHandle source;
|
||||
|
||||
public Material material;
|
||||
public int materialPassIndex;
|
||||
|
||||
public UniversalLightData lightData;
|
||||
public TextureHandle halfResCameraDepthTarget;
|
||||
public TextureHandle volumetricFogTarget;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Private Attributes
|
||||
|
||||
private static readonly int FrameCountId = Shader.PropertyToID("_FrameCount");
|
||||
private static readonly int CustomAdditionalLightsCountId = Shader.PropertyToID("_CustomAdditionalLightsCount");
|
||||
private static readonly int DistanceId = Shader.PropertyToID("_Distance");
|
||||
private static readonly int BaseHeightId = Shader.PropertyToID("_BaseHeight");
|
||||
private static readonly int MaximumHeightId = Shader.PropertyToID("_MaximumHeight");
|
||||
private static readonly int GroundHeightId = Shader.PropertyToID("_GroundHeight");
|
||||
private static readonly int DensityId = Shader.PropertyToID("_Density");
|
||||
private static readonly int AbsortionId = Shader.PropertyToID("_Absortion");
|
||||
private static readonly int MainLightAnisotropyId = Shader.PropertyToID("_MainLightAnisotropy");
|
||||
private static readonly int MainLightScatteringId = Shader.PropertyToID("_MainLightScattering");
|
||||
private static readonly int MainLightColorTintId = Shader.PropertyToID("_MainLightColorTint");
|
||||
private static readonly int AdditionalLightsAnisotropyId = Shader.PropertyToID("_AdditionalLightsAnisotropy");
|
||||
private static readonly int AdditionalLightsScatteringId = Shader.PropertyToID("_AdditionalLightsScattering");
|
||||
private static readonly int AdditionalLightsRadiusSqId = Shader.PropertyToID("_AdditionalLightsRadiusSq");
|
||||
private static readonly int MaxStepsId = Shader.PropertyToID("_MaxSteps");
|
||||
|
||||
private static readonly int HalfResCameraDepthTextureId = Shader.PropertyToID("_HalfResCameraDepthTexture");
|
||||
private static readonly int VolumetricFogTextureId = Shader.PropertyToID("_VolumetricFogTexture");
|
||||
|
||||
private Material downsampleDepthMaterial;
|
||||
private Material volumetricFogMaterial;
|
||||
|
||||
private RTHandle halfResCameraDepthRTHandle;
|
||||
private RTHandle volumetricFogRenderRTHandle;
|
||||
private RTHandle volumetricFogAuxRenderRTHandle;
|
||||
private RTHandle volumetricFogCompositionRTHandle;
|
||||
|
||||
private ProfilingSampler downsampleDepthProfilingSampler;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization Methods
|
||||
|
||||
public VolumetricFogRenderPass(Material downsampleDepthMaterial, Material volumetricFogMaterial) : base()
|
||||
{
|
||||
// Use BeforeRenderingPostprocessing instead of AfterRenderingTransparents. It works better
|
||||
// with motion blur. BeforeRenderingTransparents is also an option depending on the needs.
|
||||
profilingSampler = new ProfilingSampler("Volumetric Fog");
|
||||
downsampleDepthProfilingSampler = new ProfilingSampler("Downsample Depth");
|
||||
renderPassEvent = RenderPassEvent.BeforeRenderingPostProcessing;
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
requiresIntermediateTexture = false;
|
||||
#endif
|
||||
|
||||
this.downsampleDepthMaterial = downsampleDepthMaterial;
|
||||
this.volumetricFogMaterial = volumetricFogMaterial;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scriptable Render Pass Methods
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="cmd"></param>
|
||||
/// <param name="renderingData"></param>
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[Obsolete]
|
||||
#endif
|
||||
public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
|
||||
{
|
||||
base.OnCameraSetup(cmd, ref renderingData);
|
||||
|
||||
RenderTextureDescriptor cameraTargetDescriptor = renderingData.cameraData.cameraTargetDescriptor;
|
||||
cameraTargetDescriptor.depthBufferBits = (int)DepthBits.None;
|
||||
|
||||
RenderTextureFormat originalColorFormat = cameraTargetDescriptor.colorFormat;
|
||||
Vector2Int originalResolution = new Vector2Int(cameraTargetDescriptor.width, cameraTargetDescriptor.height);
|
||||
|
||||
cameraTargetDescriptor.width /= 2;
|
||||
cameraTargetDescriptor.height /= 2;
|
||||
cameraTargetDescriptor.graphicsFormat = GraphicsFormat.R32_SFloat;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref halfResCameraDepthRTHandle, cameraTargetDescriptor, FilterMode.Point, TextureWrapMode.Clamp, name: "_HalfResCameraDepth");
|
||||
|
||||
cameraTargetDescriptor.colorFormat = RenderTextureFormat.ARGBHalf;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref volumetricFogRenderRTHandle, cameraTargetDescriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_VolumetricFog");
|
||||
RenderingUtils.ReAllocateIfNeeded(ref volumetricFogAuxRenderRTHandle, cameraTargetDescriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_VolumetricFogAux");
|
||||
|
||||
cameraTargetDescriptor.width = originalResolution.x;
|
||||
cameraTargetDescriptor.height = originalResolution.y;
|
||||
cameraTargetDescriptor.colorFormat = originalColorFormat;
|
||||
RenderingUtils.ReAllocateIfNeeded(ref volumetricFogCompositionRTHandle, cameraTargetDescriptor, FilterMode.Point, TextureWrapMode.Clamp, name: "_VolumetricFogComposition");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="renderingData"></param>
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[Obsolete]
|
||||
#endif
|
||||
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
||||
{
|
||||
CommandBuffer cmd = CommandBufferPool.Get();
|
||||
|
||||
using (new ProfilingScope(cmd, downsampleDepthProfilingSampler))
|
||||
{
|
||||
Blitter.BlitCameraTexture(cmd, halfResCameraDepthRTHandle, halfResCameraDepthRTHandle, downsampleDepthMaterial, 0);
|
||||
volumetricFogMaterial.SetTexture(HalfResCameraDepthTextureId, halfResCameraDepthRTHandle);
|
||||
}
|
||||
|
||||
using (new ProfilingScope(cmd, profilingSampler))
|
||||
{
|
||||
VolumetricFogVolumeComponent fogVolume = VolumeManager.instance.stack.GetComponent<VolumetricFogVolumeComponent>();
|
||||
|
||||
int frameCount = Time.renderedFrameCount % 64;
|
||||
float absortion = 1.0f / fogVolume.attenuationDistance.value;
|
||||
|
||||
EnableMainLightContribution(volumetricFogMaterial, fogVolume.enableMainLightContribution.value);
|
||||
EnableAdditionalLightsContribution(volumetricFogMaterial, fogVolume.enableAdditionalLightsContribution.value);
|
||||
volumetricFogMaterial.SetInteger(FrameCountId, frameCount);
|
||||
volumetricFogMaterial.SetInteger(CustomAdditionalLightsCountId, renderingData.lightData.additionalLightsCount);
|
||||
volumetricFogMaterial.SetFloat(DistanceId, fogVolume.distance.value);
|
||||
volumetricFogMaterial.SetFloat(BaseHeightId, fogVolume.baseHeight.value);
|
||||
volumetricFogMaterial.SetFloat(MaximumHeightId, fogVolume.maximumHeight.value);
|
||||
UpdateGroundHeightFromMaterial(volumetricFogMaterial, fogVolume);
|
||||
volumetricFogMaterial.SetFloat(DensityId, fogVolume.density.value);
|
||||
volumetricFogMaterial.SetFloat(AbsortionId, absortion);
|
||||
volumetricFogMaterial.SetColor(MainLightColorTintId, fogVolume.mainLightColorTint.value);
|
||||
volumetricFogMaterial.SetFloat(MainLightAnisotropyId, fogVolume.mainLightAnisotropy.value);
|
||||
volumetricFogMaterial.SetFloat(MainLightScatteringId, fogVolume.mainLightScattering.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsAnisotropyId, fogVolume.additionalLightsAnisotropy.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsScatteringId, fogVolume.additionalLightsScattering.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsRadiusSqId, fogVolume.additionalLightsRadius.value * fogVolume.additionalLightsRadius.value);
|
||||
volumetricFogMaterial.SetInteger(MaxStepsId, fogVolume.maxSteps.value);
|
||||
|
||||
Blitter.BlitCameraTexture(cmd, volumetricFogRenderRTHandle, volumetricFogRenderRTHandle, volumetricFogMaterial, 0);
|
||||
|
||||
for (int i = 0; i < fogVolume.blurIterations.value; ++i)
|
||||
{
|
||||
Blitter.BlitCameraTexture(cmd, volumetricFogRenderRTHandle, volumetricFogAuxRenderRTHandle, volumetricFogMaterial, 1);
|
||||
Blitter.BlitCameraTexture(cmd, volumetricFogAuxRenderRTHandle, volumetricFogRenderRTHandle, volumetricFogMaterial, 2);
|
||||
}
|
||||
|
||||
volumetricFogMaterial.SetTexture(VolumetricFogTextureId, volumetricFogRenderRTHandle);
|
||||
|
||||
RTHandle cameraColorRt = renderingData.cameraData.renderer.cameraColorTargetHandle;
|
||||
Blitter.BlitCameraTexture(cmd, cameraColorRt, volumetricFogCompositionRTHandle, volumetricFogMaterial, 3);
|
||||
Blitter.BlitCameraTexture(cmd, volumetricFogCompositionRTHandle, cameraColorRt);
|
||||
}
|
||||
|
||||
context.ExecuteCommandBuffer(cmd);
|
||||
|
||||
cmd.Clear();
|
||||
|
||||
CommandBufferPool.Release(cmd);
|
||||
}
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="renderGraph"></param>
|
||||
/// <param name="frameData"></param>
|
||||
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
||||
{
|
||||
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
||||
UniversalLightData lightData = frameData.Get<UniversalLightData>();
|
||||
UniversalResourceData resourceData = frameData.Get<UniversalResourceData>();
|
||||
|
||||
CreateRenderGraphTextures(renderGraph, cameraData, out TextureHandle halfResCameraDepthTarget, out TextureHandle volumetricFogRenderTarget, out TextureHandle volumetricFogAuxRenderTarget, out TextureHandle volumetricFogCompositionTarget);
|
||||
|
||||
using (IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass("Downsample Depth Pass", out PassData passData, downsampleDepthProfilingSampler))
|
||||
{
|
||||
passData.stage = PassStage.DownsampleDepth;
|
||||
passData.target = halfResCameraDepthTarget;
|
||||
passData.source = resourceData.cameraDepthTexture;
|
||||
passData.material = downsampleDepthMaterial;
|
||||
passData.materialPassIndex = 0;
|
||||
|
||||
builder.SetRenderAttachment(halfResCameraDepthTarget, 0, AccessFlags.WriteAll);
|
||||
builder.UseTexture(resourceData.cameraDepthTexture);
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context));
|
||||
}
|
||||
|
||||
using (IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass("Volumetric Fog Render Pass", out PassData passData, profilingSampler))
|
||||
{
|
||||
passData.stage = PassStage.RenderFog;
|
||||
passData.target = volumetricFogRenderTarget;
|
||||
passData.source = halfResCameraDepthTarget;
|
||||
passData.material = volumetricFogMaterial;
|
||||
passData.materialPassIndex = 0;
|
||||
passData.lightData = lightData;
|
||||
passData.halfResCameraDepthTarget = halfResCameraDepthTarget;
|
||||
|
||||
builder.SetRenderAttachment(volumetricFogRenderTarget, 0, AccessFlags.WriteAll);
|
||||
builder.UseTexture(halfResCameraDepthTarget);
|
||||
if (resourceData.mainShadowsTexture.IsValid())
|
||||
builder.UseTexture(resourceData.mainShadowsTexture);
|
||||
if (resourceData.additionalShadowsTexture.IsValid())
|
||||
builder.UseTexture(resourceData.additionalShadowsTexture);
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context));
|
||||
}
|
||||
|
||||
using (IUnsafeRenderGraphBuilder builder = renderGraph.AddUnsafePass("Volumetric Fog Blur Pass", out PassData passData, profilingSampler))
|
||||
{
|
||||
passData.stage = PassStage.BlurFog;
|
||||
passData.target = volumetricFogAuxRenderTarget;
|
||||
passData.source = volumetricFogRenderTarget;
|
||||
passData.material = volumetricFogMaterial;
|
||||
|
||||
// Access flags are theoretically incorrect for one separable blur pass, but it is not
|
||||
// going to make any difference.
|
||||
builder.UseTexture(volumetricFogRenderTarget, AccessFlags.ReadWrite);
|
||||
builder.UseTexture(volumetricFogAuxRenderTarget, AccessFlags.ReadWrite);
|
||||
builder.SetRenderFunc((PassData data, UnsafeGraphContext context) => ExecuteUnsafeBlurPass(data, context));
|
||||
}
|
||||
|
||||
using (IRasterRenderGraphBuilder builder = renderGraph.AddRasterRenderPass("Volumetric Fog Composition Pass", out PassData passData, profilingSampler))
|
||||
{
|
||||
passData.stage = PassStage.CompositeFog;
|
||||
passData.target = volumetricFogCompositionTarget;
|
||||
passData.source = resourceData.cameraColor;
|
||||
passData.material = volumetricFogMaterial;
|
||||
passData.materialPassIndex = 3;
|
||||
passData.halfResCameraDepthTarget = halfResCameraDepthTarget;
|
||||
passData.volumetricFogTarget = volumetricFogRenderTarget;
|
||||
|
||||
builder.SetRenderAttachment(volumetricFogCompositionTarget, 0, AccessFlags.WriteAll);
|
||||
builder.UseTexture(resourceData.cameraColor);
|
||||
builder.UseTexture(resourceData.cameraDepthTexture);
|
||||
builder.UseTexture(halfResCameraDepthTarget);
|
||||
builder.UseTexture(volumetricFogRenderTarget);
|
||||
builder.SetRenderFunc((PassData data, RasterGraphContext context) => ExecutePass(data, context));
|
||||
}
|
||||
|
||||
resourceData.cameraColor = volumetricFogCompositionTarget;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the computations from the main light to influence the volumetric fog.
|
||||
/// </summary>
|
||||
/// <param name="volumetricFogMaterial"></param>
|
||||
/// <param name="enabled"></param>
|
||||
private static void EnableMainLightContribution(Material volumetricFogMaterial, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
volumetricFogMaterial.DisableKeyword("_MAIN_LIGHT_CONTRIBUTION_DISABLED");
|
||||
else
|
||||
volumetricFogMaterial.EnableKeyword("_MAIN_LIGHT_CONTRIBUTION_DISABLED");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enables or disables the computations from additional lights to influence the volumetric fog.
|
||||
/// </summary>
|
||||
/// <param name="volumetricFogMaterial"></param>
|
||||
/// <param name="enabled"></param>
|
||||
private static void EnableAdditionalLightsContribution(Material volumetricFogMaterial, bool enabled)
|
||||
{
|
||||
if (enabled)
|
||||
volumetricFogMaterial.DisableKeyword("_ADDITIONAL_LIGHTS_CONTRIBUTION_DISABLED");
|
||||
else
|
||||
volumetricFogMaterial.EnableKeyword("_ADDITIONAL_LIGHTS_CONTRIBUTION_DISABLED");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the ground height parameter from the material.
|
||||
/// </summary>
|
||||
/// <param name="volumetricFogMaterial"></param>
|
||||
/// <param name="volume"></param>
|
||||
private static void UpdateGroundHeightFromMaterial(Material volumetricFogMaterial, VolumetricFogVolumeComponent volume)
|
||||
{
|
||||
float groundValue = float.MinValue;
|
||||
groundValue = (volume.enableGround.overrideState && volume.enableGround.value) ? volume.groundHeight.value : groundValue;
|
||||
volumetricFogMaterial.SetFloat(GroundHeightId, groundValue);
|
||||
}
|
||||
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
|
||||
/// <summary>
|
||||
/// Creates and returns all the necessary render graph textures.
|
||||
/// </summary>
|
||||
/// <param name="renderGraph"></param>
|
||||
/// <param name="cameraData"></param>
|
||||
/// <param name="halfResCameraDepthTarget"></param>
|
||||
/// <param name="volumetricFogRenderTarget"></param>
|
||||
/// <param name="volumetricFogAuxRenderTarget"></param>
|
||||
/// <param name="volumetricFogCompositionTarget"></param>
|
||||
private void CreateRenderGraphTextures(RenderGraph renderGraph, UniversalCameraData cameraData, out TextureHandle halfResCameraDepthTarget, out TextureHandle volumetricFogRenderTarget, out TextureHandle volumetricFogAuxRenderTarget, out TextureHandle volumetricFogCompositionTarget)
|
||||
{
|
||||
RenderTextureDescriptor cameraTargetDescriptor = cameraData.cameraTargetDescriptor;
|
||||
cameraTargetDescriptor.depthBufferBits = (int)DepthBits.None;
|
||||
|
||||
RenderTextureFormat originalColorFormat = cameraTargetDescriptor.colorFormat;
|
||||
Vector2Int originalResolution = new Vector2Int(cameraTargetDescriptor.width, cameraTargetDescriptor.height);
|
||||
|
||||
cameraTargetDescriptor.width /= 2;
|
||||
cameraTargetDescriptor.height /= 2;
|
||||
cameraTargetDescriptor.graphicsFormat = GraphicsFormat.R32_SFloat;
|
||||
halfResCameraDepthTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, cameraTargetDescriptor, "_HalfResCameraDepth", false);
|
||||
|
||||
cameraTargetDescriptor.colorFormat = RenderTextureFormat.ARGBHalf;
|
||||
volumetricFogRenderTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, cameraTargetDescriptor, "_VolumetricFog", false, FilterMode.Bilinear);
|
||||
volumetricFogAuxRenderTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, cameraTargetDescriptor, "_VolumetricFogAux", false, FilterMode.Bilinear);
|
||||
|
||||
cameraTargetDescriptor.width = originalResolution.x;
|
||||
cameraTargetDescriptor.height = originalResolution.y;
|
||||
cameraTargetDescriptor.colorFormat = originalColorFormat;
|
||||
volumetricFogCompositionTarget = UniversalRenderer.CreateRenderGraphTexture(renderGraph, cameraTargetDescriptor, "_VolumetricFogComposition", false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the material properties that are needed to render the volumetric fog.
|
||||
/// </summary>
|
||||
/// <param name="passData"></param>
|
||||
private static void UpdateVolumetricFogMaterialProperties(PassData passData)
|
||||
{
|
||||
PassStage stage = passData.stage;
|
||||
|
||||
if (stage == PassStage.RenderFog)
|
||||
{
|
||||
VolumetricFogVolumeComponent fogVolume = VolumeManager.instance.stack.GetComponent<VolumetricFogVolumeComponent>();
|
||||
|
||||
int frameCount = Time.renderedFrameCount % 64;
|
||||
float absortion = 1.0f / fogVolume.attenuationDistance.value;
|
||||
|
||||
Material volumetricFogMaterial = passData.material;
|
||||
EnableMainLightContribution(volumetricFogMaterial, fogVolume.enableMainLightContribution.value);
|
||||
EnableAdditionalLightsContribution(volumetricFogMaterial, fogVolume.enableAdditionalLightsContribution.value);
|
||||
volumetricFogMaterial.SetTexture(HalfResCameraDepthTextureId, passData.halfResCameraDepthTarget);
|
||||
volumetricFogMaterial.SetInteger(FrameCountId, frameCount);
|
||||
volumetricFogMaterial.SetInteger(CustomAdditionalLightsCountId, passData.lightData.additionalLightsCount);
|
||||
volumetricFogMaterial.SetFloat(DistanceId, fogVolume.distance.value);
|
||||
volumetricFogMaterial.SetFloat(BaseHeightId, fogVolume.baseHeight.value);
|
||||
volumetricFogMaterial.SetFloat(MaximumHeightId, fogVolume.maximumHeight.value);
|
||||
UpdateGroundHeightFromMaterial(volumetricFogMaterial, fogVolume);
|
||||
volumetricFogMaterial.SetFloat(DensityId, fogVolume.density.value);
|
||||
volumetricFogMaterial.SetFloat(AbsortionId, absortion);
|
||||
volumetricFogMaterial.SetColor(MainLightColorTintId, fogVolume.mainLightColorTint.value);
|
||||
volumetricFogMaterial.SetFloat(MainLightAnisotropyId, fogVolume.mainLightAnisotropy.value);
|
||||
volumetricFogMaterial.SetFloat(MainLightScatteringId, fogVolume.mainLightScattering.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsAnisotropyId, fogVolume.additionalLightsAnisotropy.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsScatteringId, fogVolume.additionalLightsScattering.value);
|
||||
volumetricFogMaterial.SetFloat(AdditionalLightsRadiusSqId, fogVolume.additionalLightsRadius.value * fogVolume.additionalLightsRadius.value);
|
||||
volumetricFogMaterial.SetInteger(MaxStepsId, fogVolume.maxSteps.value);
|
||||
}
|
||||
else if (stage == PassStage.CompositeFog)
|
||||
{
|
||||
Material volumetricFogMaterial = passData.material;
|
||||
volumetricFogMaterial.SetTexture(VolumetricFogTextureId, passData.volumetricFogTarget);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the pass with the information from the pass data.
|
||||
/// </summary>
|
||||
/// <param name="passData"></param>
|
||||
/// <param name="context"></param>
|
||||
private static void ExecutePass(PassData passData, RasterGraphContext context)
|
||||
{
|
||||
UpdateVolumetricFogMaterialProperties(passData);
|
||||
|
||||
Blitter.BlitTexture(context.cmd, passData.source, Vector2.one, passData.material, passData.materialPassIndex);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the unsafe pass that does up to multiple separable blurs to the volumetric fog.
|
||||
/// </summary>
|
||||
/// <param name="passData"></param>
|
||||
/// <param name="context"></param>
|
||||
private static void ExecuteUnsafeBlurPass(PassData passData, UnsafeGraphContext context)
|
||||
{
|
||||
CommandBuffer unsafeCmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
||||
|
||||
TextureHandle source = passData.source;
|
||||
TextureHandle target = passData.target;
|
||||
|
||||
int blurIterations = VolumeManager.instance.stack.GetComponent<VolumetricFogVolumeComponent>().blurIterations.value;
|
||||
|
||||
for (int i = 0; i < blurIterations; ++i)
|
||||
{
|
||||
Blitter.BlitCameraTexture(unsafeCmd, source, target, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 1);
|
||||
Blitter.BlitCameraTexture(unsafeCmd, target, source, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store, passData.material, 2);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the resources used by this pass.
|
||||
/// </summary>
|
||||
public void Dispose()
|
||||
{
|
||||
halfResCameraDepthRTHandle?.Release();
|
||||
volumetricFogRenderRTHandle?.Release();
|
||||
volumetricFogAuxRenderRTHandle?.Release();
|
||||
volumetricFogCompositionRTHandle?.Release();
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
11
Assets/Volumetric/Runtime/VolumetricFogRenderPass.cs.meta
Normal file
11
Assets/Volumetric/Runtime/VolumetricFogRenderPass.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fea71bdcd6ce4d41bd781fc3e9d1813
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
115
Assets/Volumetric/Runtime/VolumetricFogRendererFeature.cs
Normal file
115
Assets/Volumetric/Runtime/VolumetricFogRendererFeature.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
/// <summary>
|
||||
/// The volumetric fog renderer feature.
|
||||
/// </summary>
|
||||
[Tooltip("Adds support to render volumetric fog.")]
|
||||
[DisallowMultipleRendererFeature("Volumetric Fog")]
|
||||
public sealed class VolumetricFogRendererFeature : ScriptableRendererFeature
|
||||
{
|
||||
#region Private Attributes
|
||||
|
||||
[HideInInspector]
|
||||
[SerializeField] private Shader downsampleDepthShader;
|
||||
[HideInInspector]
|
||||
[SerializeField] private Shader volumetricFogShader;
|
||||
|
||||
private Material downsampleDepthMaterial;
|
||||
private Material volumetricFogMaterial;
|
||||
private VolumetricFogRenderPass volumetricFogRenderPass;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Scriptable Renderer Feature Methods
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
public override void Create()
|
||||
{
|
||||
ValidateResourcesForVolumetricFogRenderPass(true);
|
||||
|
||||
volumetricFogRenderPass = new VolumetricFogRenderPass(downsampleDepthMaterial, volumetricFogMaterial);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="renderer"></param>
|
||||
/// <param name="renderingData"></param>
|
||||
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
|
||||
{
|
||||
bool isPostProcessEnabled = renderingData.postProcessingEnabled && renderingData.cameraData.postProcessEnabled;
|
||||
bool shouldAddVolumetricFogRenderPass = isPostProcessEnabled && ShouldAddVolumetricFogRenderPass(renderingData.cameraData.cameraType);
|
||||
|
||||
if (shouldAddVolumetricFogRenderPass)
|
||||
{
|
||||
volumetricFogRenderPass.ConfigureInput(ScriptableRenderPassInput.Depth);
|
||||
renderer.EnqueuePass(volumetricFogRenderPass);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <param name="disposing"></param>
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
volumetricFogRenderPass?.Dispose();
|
||||
|
||||
CoreUtils.Destroy(downsampleDepthMaterial);
|
||||
CoreUtils.Destroy(volumetricFogMaterial);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
||||
/// <summary>
|
||||
/// Validates the resources used by the volumetric fog render pass.
|
||||
/// </summary>
|
||||
/// <param name="forceRefresh"></param>
|
||||
/// <returns></returns>
|
||||
private bool ValidateResourcesForVolumetricFogRenderPass(bool forceRefresh)
|
||||
{
|
||||
if (forceRefresh)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
downsampleDepthShader = Shader.Find("Hidden/DownsampleDepth");
|
||||
volumetricFogShader = Shader.Find("Hidden/VolumetricFog");
|
||||
#endif
|
||||
CoreUtils.Destroy(downsampleDepthMaterial);
|
||||
downsampleDepthMaterial = CoreUtils.CreateEngineMaterial(downsampleDepthShader);
|
||||
|
||||
CoreUtils.Destroy(volumetricFogMaterial);
|
||||
volumetricFogMaterial = CoreUtils.CreateEngineMaterial(volumetricFogShader);
|
||||
}
|
||||
|
||||
bool okDepth = downsampleDepthShader != null && downsampleDepthMaterial != null;
|
||||
bool okVolumetric = volumetricFogShader != null && volumetricFogMaterial != null;
|
||||
|
||||
return okDepth && okVolumetric;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the volumetric fog render pass should be enqueued to the renderer.
|
||||
/// </summary>
|
||||
/// <param name="cameraType"></param>
|
||||
/// <returns></returns>
|
||||
private bool ShouldAddVolumetricFogRenderPass(CameraType cameraType)
|
||||
{
|
||||
VolumetricFogVolumeComponent fogVolume = VolumeManager.instance.stack.GetComponent<VolumetricFogVolumeComponent>();
|
||||
|
||||
bool isVolumeOk = fogVolume != null && fogVolume.IsActive();
|
||||
bool isCameraOk = cameraType != CameraType.Preview && cameraType != CameraType.Reflection;
|
||||
bool areResourcesOk = ValidateResourcesForVolumetricFogRenderPass(false);
|
||||
|
||||
return isActive && isVolumeOk && isCameraOk && areResourcesOk;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d69a19d29ef66464ea0afc7cd84fd6c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
116
Assets/Volumetric/Runtime/VolumetricFogVolumeComponent.cs
Normal file
116
Assets/Volumetric/Runtime/VolumetricFogVolumeComponent.cs
Normal file
@@ -0,0 +1,116 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
/// <summary>
|
||||
/// Volume component for the volumetric fog.
|
||||
/// </summary>
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
[VolumeComponentMenu("Custom/Volumetric Fog")]
|
||||
#if UNITY_6000_0_OR_NEWER
|
||||
[VolumeRequiresRendererFeatures(typeof(VolumetricFogRendererFeature))]
|
||||
#endif
|
||||
[SupportedOnRenderPipeline(typeof(UniversalRenderPipelineAsset))]
|
||||
#else
|
||||
[VolumeComponentMenuForRenderPipeline("Custom/Volumetric Fog", typeof(UniversalRenderPipeline))]
|
||||
#endif
|
||||
public sealed class VolumetricFogVolumeComponent : VolumeComponent, IPostProcessComponent
|
||||
{
|
||||
#region Public Attributes
|
||||
|
||||
[Header("Distances")]
|
||||
[Tooltip("The maximum distance from the camera that the fog will be rendered up to.")]
|
||||
public ClampedFloatParameter distance = new ClampedFloatParameter(128.0f, 0.0f, 512.0f);
|
||||
[Tooltip("The world height at which the fog will have the density specified in the volume.")]
|
||||
public FloatParameter baseHeight = new FloatParameter(0.0f, true);
|
||||
[Tooltip("The world height at which the fog will have no density at all.")]
|
||||
public FloatParameter maximumHeight = new FloatParameter(50.0f, true);
|
||||
|
||||
[Header("Ground")]
|
||||
[Tooltip("When enabled, allows to define a world height. Below it, fog will have no density at all.")]
|
||||
public BoolParameter enableGround = new BoolParameter(false, BoolParameter.DisplayType.Checkbox, true);
|
||||
[Tooltip("Below this world height, fog will have no density at all.")]
|
||||
public FloatParameter groundHeight = new FloatParameter(0.0f);
|
||||
|
||||
[Header("Lighting")]
|
||||
[Tooltip("How dense is the fog.")]
|
||||
public ClampedFloatParameter density = new ClampedFloatParameter(0.2f, 0.0f, 1.0f);
|
||||
[Tooltip("Value that defines how much the fog attenuates light as distance increases. Lesser values lead to a darker image.")]
|
||||
public MinFloatParameter attenuationDistance = new MinFloatParameter(128.0f, 0.05f);
|
||||
|
||||
[Header("Main Light")]
|
||||
[Tooltip("Disabling this will avoid computing the main light contribution to fog, which in most cases will lead to better performance.")]
|
||||
public BoolParameter enableMainLightContribution = new BoolParameter(false, BoolParameter.DisplayType.Checkbox, true);
|
||||
[Tooltip("Higher values will make the fog affected by the main light to appear brighter when directly looking to the main light. The higher the value the less the brightness will spread when looking away from the light.")]
|
||||
public ClampedFloatParameter mainLightAnisotropy = new ClampedFloatParameter(0.4f, 0.0f, 0.99f);
|
||||
[Tooltip("Higher values will make fog affected by the main light to appear brighter.")]
|
||||
public ClampedFloatParameter mainLightScattering = new ClampedFloatParameter(0.15f, 0.0f, 1.0f);
|
||||
[Tooltip("A multiplier color to tint the main light fog.")]
|
||||
public ColorParameter mainLightColorTint = new ColorParameter(Color.white, true, false, true);
|
||||
|
||||
[Header("Additional Lights")]
|
||||
[Tooltip("Disabling this will avoid computing additional lights contribution to fog, which in most cases will lead to better performance.")]
|
||||
public BoolParameter enableAdditionalLightsContribution = new BoolParameter(false, BoolParameter.DisplayType.Checkbox, true);
|
||||
[Tooltip("Higher values will make the fog affected by additional lights to appear brighter when directly looking to the light source. The higher the value the less the brightness will spread when looking away from the light.")]
|
||||
public ClampedFloatParameter additionalLightsAnisotropy = new ClampedFloatParameter(0.25f, 0.0f, 0.99f);
|
||||
[Tooltip("Higher values will make fog affected by additional lights to appear brighter.")]
|
||||
public ClampedFloatParameter additionalLightsScattering = new ClampedFloatParameter(1.0f, 0.0f, 16.0f);
|
||||
[Tooltip("Sets a falloff radius for point and spotlights. A higher value reduces noise towards the origin of the light")]
|
||||
public ClampedFloatParameter additionalLightsRadius = new ClampedFloatParameter(0.2f, 0.0f, 1.0f);
|
||||
|
||||
[Header("Performance & Quality")]
|
||||
[Tooltip("Raymarching steps. Greater values will increase the fog quality at the expense of performance.")]
|
||||
public ClampedIntParameter maxSteps = new ClampedIntParameter(64, 8, 256);
|
||||
[Tooltip("The number of times that the fog texture will be blurred. Higher values lead to softer volumetric god rays at the cost of some performance.")]
|
||||
public ClampedIntParameter blurIterations = new ClampedIntParameter(2, 1, 4);
|
||||
[Tooltip("Disabling this will completely remove any feature from the volumetric fog from being rendered at all.")]
|
||||
public BoolParameter enabled = new BoolParameter(false, BoolParameter.DisplayType.Checkbox, true);
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialization Methods
|
||||
|
||||
public VolumetricFogVolumeComponent() : base()
|
||||
{
|
||||
displayName = "Volumetric Fog";
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Volume Component Methods
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
maximumHeight.overrideState = baseHeight.overrideState;
|
||||
maximumHeight.value = Mathf.Max(baseHeight.value, maximumHeight.value);
|
||||
baseHeight.value = Mathf.Min(baseHeight.value, maximumHeight.value);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region IPostProcessComponent Methods
|
||||
|
||||
#if !UNITY_2023_1_OR_NEWER
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsTileCompatible()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// <inheritdoc/>
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool IsActive()
|
||||
{
|
||||
return enabled.value && distance.value > 0.0f && groundHeight.value < maximumHeight.value && density.value > 0.0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a074ec59f6e910b4e8c49f57e56cf95a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "com.cqf.urpvolumetricfog.runtime",
|
||||
"rootNamespace": "",
|
||||
"references": [
|
||||
"Unity.RenderPipelines.Core.Runtime",
|
||||
"Unity.RenderPipelines.Universal.Runtime"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: da73c4ed88505644497dd650af00dc26
|
||||
AssemblyDefinitionImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user