Files
DeviantMobile-Rohan/Assets/Volumetric/Runtime/VolumetricFogRenderPass.cs

455 lines
20 KiB
C#

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
}