Rasagar/Library/PackageCache/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricClouds/HDRenderPipeline.VolumetricClouds.cs
2024-08-26 23:07:20 +03:00

936 lines
47 KiB
C#

using System;
using Unity.Mathematics;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.HighDefinition
{
partial class VolumetricCloudsSystem
{
// Flag that allows us to track the resources that have been allocated
bool m_ActiveVolumetricClouds = false;
HDRenderPipeline m_RenderPipeline;
VolumetricCloudsRuntimeResources m_RuntimeResources;
// Intermediate values for ambient probe evaluation
ZonalHarmonicsL2 m_PhaseZHClouds;
// Cloud preset maps
Texture2D m_CustomPresetMap;
Texture2D m_CustomLutPresetMap;
const int k_CustomLutMapResolution = 64;
readonly Color[] m_CustomLutColorArray = new Color[k_CustomLutMapResolution];
// Compute shader kernels
ComputeShader m_VolumetricCloudsCS;
ComputeShader m_VolumetricCloudsTraceCS;
// Cloud rendering kernels
int m_CloudRenderKernel;
// Reprojection / First upscale
int m_ReprojectCloudsKernel;
int m_ReprojectCloudsRejectionKernel;
int m_PreUpscaleCloudsKernel;
// Second Upscale + Fog
int m_UpscaleCloudsKernel;
int m_UpscaleCloudsPerceptualKernel;
// Fog only
int m_CombineCloudsKernel;
int m_CombineCloudsPerceptualKernel;
// Combine pass via hardware blending.
Material m_CloudCombinePass;
LocalKeyword m_OutputFogTransmittanceKeyword;
float m_CloudsAnimationLastTime;
internal VolumetricClouds.AnimationData m_CloudsAnimationData;
struct VolumetricCloudsCameraData
{
public TVolumetricCloudsCameraType cameraType;
public int traceWidth;
public int traceHeight;
public int intermediateWidth;
public int intermediateHeight;
public int finalWidth;
public int finalHeight;
public bool enableExposureControl;
public bool lowResolution;
public bool enableIntegration;
}
public void Initialize(HDRenderPipeline hdPipeline)
{
m_RenderPipeline = hdPipeline;
m_ActiveVolumetricClouds = hdPipeline.asset.currentPlatformRenderPipelineSettings.supportVolumetricClouds;
if (!m_ActiveVolumetricClouds)
return;
m_RuntimeResources = GraphicsSettings.GetRenderPipelineSettings<VolumetricCloudsRuntimeResources>();
// Allocate the buffers for ambient probe evaluation
m_PhaseZHClouds = new ZonalHarmonicsL2();
m_PhaseZHClouds.coeffs = new float[3];
// Grab the kernels we need
m_VolumetricCloudsCS = m_RuntimeResources.volumetricCloudsCS;
m_ReprojectCloudsKernel = m_VolumetricCloudsCS.FindKernel("ReprojectClouds");
m_ReprojectCloudsRejectionKernel = m_VolumetricCloudsCS.FindKernel("ReprojectCloudsRejection");
m_PreUpscaleCloudsKernel = m_VolumetricCloudsCS.FindKernel("PreUpscaleClouds");
m_UpscaleCloudsKernel = m_VolumetricCloudsCS.FindKernel("UpscaleClouds");
m_UpscaleCloudsPerceptualKernel = m_VolumetricCloudsCS.FindKernel("UpscaleCloudsPerceptual");
m_CombineCloudsKernel = m_VolumetricCloudsCS.FindKernel("CombineClouds");
m_CombineCloudsPerceptualKernel = m_VolumetricCloudsCS.FindKernel("CombineCloudsPerceptual");
// Create the material needed for the combination
m_CloudCombinePass = CoreUtils.CreateEngineMaterial(m_RuntimeResources.volumetricCloudsCombinePS);
m_OutputFogTransmittanceKeyword = new LocalKeyword(m_CloudCombinePass.shader, "OUTPUT_TRANSMITTANCE_BUFFER");
m_VolumetricCloudsTraceCS = m_RuntimeResources.volumetricCloudsTraceCS;
m_CloudRenderKernel = m_VolumetricCloudsTraceCS.FindKernel("RenderClouds");
// Allocate all the texture initially
AllocatePresetTextures();
// Initialize cloud animation
m_CloudsAnimationLastTime = -1.0f;
m_CloudsAnimationData = new()
{
cloudOffset = new Vector2(0.0f, 0.0f),
verticalShapeOffset = 0.0f,
verticalErosionOffset = 0.0f,
};
// Initialize the additional sub components
InitializeVolumetricCloudsMap();
InitializeVolumetricCloudsShadows();
InitializeVolumetricCloudsAmbientProbe();
}
public void Cleanup()
{
if (!m_ActiveVolumetricClouds)
return;
// Destroy allocated resources
CoreUtils.Destroy(m_CloudCombinePass);
CoreUtils.Destroy(m_CustomPresetMap);
CoreUtils.Destroy(m_CustomLutPresetMap);
// Release the additional sub components
ReleaseVolumetricCloudsMap();
ReleaseVolumetricCloudsAmbientProbe();
}
void AllocatePresetTextures()
{
m_CustomPresetMap = new Texture2D(1, 1, GraphicsFormat.R8G8B8A8_UNorm, TextureCreationFlags.None) { name = "Default Cloud Map Texture" };
m_CustomPresetMap.SetPixel(0, 0, new Color(0.9f, 0.0f, 0.25f, 1.0f));
m_CustomPresetMap.Apply();
}
float Square(float x)
{
return x * x;
}
float ComputeNormalizationFactor(float earthRadius, float lowerCloudRadius)
{
// TODO: figure out what this function does
const float k_EarthRadius = 6378100.0f;
return Mathf.Sqrt((k_EarthRadius + lowerCloudRadius) * (k_EarthRadius + lowerCloudRadius) - k_EarthRadius * earthRadius);
}
internal struct CloudModelData
{
public float densityMultiplier;
// Shape
public float shapeFactor;
public float shapeScale;
// Erosion
public float erosionFactor;
public float erosionScale;
public VolumetricClouds.CloudErosionNoise erosionNoise;
// Micro erosion
public float microErosionFactor;
public float microErosionScale;
}
float ErosionNoiseTypeToErosionCompensation(VolumetricClouds.CloudErosionNoise noiseType)
{
switch (noiseType)
{
case VolumetricClouds.CloudErosionNoise.Worley32:
return 1.0f;
case VolumetricClouds.CloudErosionNoise.Perlin32:
return 0.75f;
}
return 1.0f;
}
void PrepareCustomLutData(in VolumetricClouds clouds)
{
if (m_CustomLutPresetMap == null)
{
m_CustomLutPresetMap = new Texture2D(1, k_CustomLutMapResolution, GraphicsFormat.R16G16B16A16_SFloat, TextureCreationFlags.None)
{
name = "Custom LUT Curve",
filterMode = FilterMode.Bilinear,
wrapMode = TextureWrapMode.Clamp
};
m_CustomLutPresetMap.hideFlags = HideFlags.HideAndDontSave;
}
var pixels = m_CustomLutColorArray;
var densityCurve = clouds.densityCurve.value;
var erosionCurve = clouds.erosionCurve.value;
var ambientOcclusionCurve = clouds.ambientOcclusionCurve.value;
if (densityCurve == null || densityCurve.length == 0)
{
for (int i = 0; i < k_CustomLutMapResolution; i++)
pixels[i] = Color.white;
}
else
{
float step = 1.0f / (k_CustomLutMapResolution - 1f);
for (int i = 0; i < k_CustomLutMapResolution; i++)
{
float currTime = step * i;
float density = (i == 0 || i == k_CustomLutMapResolution - 1) ? 0 : Mathf.Clamp(densityCurve.Evaluate(currTime), 0.0f, 1.0f);
float erosion = Mathf.Clamp(erosionCurve.Evaluate(currTime), 0.0f, 1.0f);
float ambientOcclusion = Mathf.Clamp(1.0f - ambientOcclusionCurve.Evaluate(currTime), 0.0f, 1.0f);
pixels[i] = new Color(density, erosion, ambientOcclusion, 1.0f);
}
}
m_CustomLutPresetMap.SetPixels(pixels);
m_CustomLutPresetMap.Apply();
}
// Function to evaluate if a camera should have volumetric clouds
static internal bool HasVolumetricClouds(HDCamera hdCamera, in VolumetricClouds settings)
{
// If the current volume does not enable the feature, quit right away.
return hdCamera.frameSettings.IsEnabled(FrameSettingsField.VolumetricClouds) && settings.enable.value;
}
static internal bool HasVolumetricClouds(HDCamera hdCamera)
{
VolumetricClouds settings = hdCamera.volumeStack.GetComponent<VolumetricClouds>();
// If the current volume does not enable the feature, quit right away.
return HasVolumetricClouds(hdCamera, in settings);
}
Texture2D GetPresetCloudMapTexture()
{
// Textures may become null if a new scene was loaded in the editor (and maybe other reasons).
if (m_CustomPresetMap == null || Object.ReferenceEquals(m_CustomPresetMap, null))
AllocatePresetTextures();
return m_CustomPresetMap;
}
internal enum TVolumetricCloudsCameraType
{
Default,
RealtimeReflection,
BakedReflection,
Sky
};
TVolumetricCloudsCameraType GetCameraType(HDCamera hdCamera)
{
if (hdCamera.camera.cameraType == CameraType.Reflection)
{
if (hdCamera.realtimeReflectionProbe)
return TVolumetricCloudsCameraType.RealtimeReflection;
else
return TVolumetricCloudsCameraType.BakedReflection;
}
else
return TVolumetricCloudsCameraType.Default;
}
CloudModelData GetCloudModelData(VolumetricClouds settings)
{
CloudModelData cloudModelData;
// General
cloudModelData.densityMultiplier = settings.densityMultiplier.value;
// Shape
cloudModelData.shapeFactor = settings.shapeFactor.value;
cloudModelData.shapeScale = settings.shapeScale.value;
// Erosion
cloudModelData.erosionFactor = settings.erosionFactor.value;
cloudModelData.erosionScale = settings.erosionScale.value;
cloudModelData.erosionNoise = settings.erosionNoiseType.value;
// Micro erosion
cloudModelData.microErosionFactor = settings.microErosionFactor.value;
cloudModelData.microErosionScale = settings.microErosionScale.value;
return cloudModelData;
}
void UpdateShaderVariablesClouds(ref ShaderVariablesClouds cb, HDCamera hdCamera, VolumetricClouds settings,
in VolumetricCloudsCameraData cameraData, in CloudModelData cloudModelData, bool shadowPass)
{
// Planet properties
cb._LowestCloudAltitude = hdCamera.planet.radius + settings.bottomAltitude.value;
cb._HighestCloudAltitude = cb._LowestCloudAltitude + settings.altitudeRange.value;
cb._NumPrimarySteps = settings.numPrimarySteps.value;
cb._NumLightSteps = settings.numLightSteps.value;
cb._MaxStepSize = settings.altitudeRange.value / 8.0f;
cb._CloudMapTiling.Set(settings.cloudTiling.value.x, settings.cloudTiling.value.y, settings.cloudOffset.value.x, settings.cloudOffset.value.y);
cb._ScatteringTint = Color.white - settings.scatteringTint.value * 0.75f;
cb._PowderEffectIntensity = settings.powderEffectIntensity.value;
cb._NormalizationFactor = ComputeNormalizationFactor(hdCamera.planet.radius, (cb._LowestCloudAltitude + cb._HighestCloudAltitude) * 0.5f - hdCamera.planet.radius);
// We need 16 samples per pixel and we are alternating between 4 pixels (16 x 4 = 64)
int frameIndex = HDRenderPipeline.RayTracingFrameIndex(hdCamera, 64);
cb._AccumulationFrameIndex = frameIndex / 4;
cb._SubPixelIndex = frameIndex % 4;
// PB Sun/Sky settings
Light currentSun = m_RenderPipeline.GetMainLight();
HDAdditionalLightData additionalLightData = null;
if (currentSun != null)
{
// Grab the target sun additional data
additionalLightData = m_RenderPipeline.GetMainLightAdditionalData();
cb._SunLightColor = additionalLightData.EvaluateLightColor() * settings.sunLightDimmer.value * additionalLightData.lightDimmer;
cb._SunDirection = -currentSun.transform.forward;
}
else
{
cb._SunLightColor = Vector3.zero;
cb._SunDirection = Vector3.up;
}
// Compute the theta angle for the wind direction
float theta = settings.orientation.GetValue(hdCamera) / 180.0f * Mathf.PI;
// We apply a minus to see something moving in the right direction
cb._WindDirection = new Vector2(-Mathf.Cos(theta), -Mathf.Sin(theta));
cb._WindVector = m_CloudsAnimationData.cloudOffset;
cb._VerticalShapeWindDisplacement = m_CloudsAnimationData.verticalShapeOffset;
cb._VerticalErosionWindDisplacement = m_CloudsAnimationData.verticalErosionOffset;
cb._LargeWindSpeed = settings.cloudMapSpeedMultiplier.value;
cb._MediumWindSpeed = settings.shapeSpeedMultiplier.value;
cb._SmallWindSpeed = settings.erosionSpeedMultiplier.value;
cb._AltitudeDistortion = settings.altitudeDistortion.value * 0.25f;
cb._MultiScattering = 1.0f - settings.multiScattering.value * 0.95f;
// The density multiplier is not used linearly
cb._DensityMultiplier = cloudModelData.densityMultiplier * cloudModelData.densityMultiplier * 2.0f;
// Shape
cb._ShapeFactor = cloudModelData.shapeFactor;
cb._ShapeScale = cloudModelData.shapeScale;
cb._ShapeNoiseOffset = new Vector2(settings.shapeOffset.value.x, settings.shapeOffset.value.z);
cb._VerticalShapeNoiseOffset = settings.shapeOffset.value.y;
// Erosion
cb._ErosionFactor = cloudModelData.erosionFactor;
cb._ErosionScale = cloudModelData.erosionScale;
// Micro erosion
cb._MicroErosionFactor = cloudModelData.microErosionFactor;
cb._MicroErosionScale = cloudModelData.microErosionScale;
// If the sun has moved more than 2.0°, reduce significantly the history accumulation
float sunAngleDifference = 0.0f;
if (additionalLightData != null)
sunAngleDifference = Quaternion.Angle(additionalLightData.previousTransform.rotation, additionalLightData.transform.localToWorldMatrix.rotation);
cb._CloudHistoryInvalidation = Mathf.Lerp(1.0f, 0.0f, Mathf.Clamp((sunAngleDifference) / 10.0f, 0.0f, 1.0f));
cb._TemporalAccumulationFactor = settings.temporalAccumulationFactor.value;
cb._ImprovedTransmittanceBlend = settings.perceptualBlending.value;
if (settings.fadeInMode.value == VolumetricClouds.CloudFadeInMode.Automatic)
{
cb._FadeInStart = Mathf.Max((cb._HighestCloudAltitude - cb._LowestCloudAltitude) * 0.2f, hdCamera.camera.nearClipPlane);
cb._FadeInDistance = (cb._HighestCloudAltitude - cb._LowestCloudAltitude) * 0.3f;
}
else
{
cb._FadeInStart = Mathf.Max(settings.fadeInStart.value, hdCamera.camera.nearClipPlane);
cb._FadeInDistance = settings.fadeInDistance.value;
}
cb._FinalScreenSize.Set((float)cameraData.finalWidth, (float)cameraData.finalHeight, 1.0f / (float)cameraData.finalWidth, 1.0f / (float)cameraData.finalHeight);
cb._IntermediateScreenSize.Set((float)cameraData.intermediateWidth, (float)cameraData.intermediateHeight, 1.0f / (float)cameraData.intermediateWidth, 1.0f / (float)cameraData.intermediateHeight);
cb._TraceScreenSize.Set((float)cameraData.traceWidth, (float)cameraData.traceHeight, 1.0f / (float)cameraData.traceWidth, 1.0f / (float)cameraData.traceHeight);
cb._ErosionOcclusion = settings.erosionOcclusion.value;
cb._ErosionFactorCompensation = ErosionNoiseTypeToErosionCompensation(settings.erosionNoiseType.value);
if (!shadowPass)
UpdateMatricesForXR(ref cb, hdCamera);
Vector3 cameraPosPS = hdCamera.mainViewConstants.worldSpaceCameraPos - hdCamera.planet.center;
Vector3 prevCameraPosPS = cameraPosPS + hdCamera.mainViewConstants.prevWorldSpaceCameraPos; // prev pos is camera relative
float previousNearPlane = math.max(GetCloudNearPlane(prevCameraPosPS, cb._LowestCloudAltitude, cb._HighestCloudAltitude), hdCamera.camera.nearClipPlane);
cb._CloudNearPlane = math.max(GetCloudNearPlane(cameraPosPS, cb._LowestCloudAltitude, cb._HighestCloudAltitude), hdCamera.camera.nearClipPlane);
cb._NearPlaneReprojection = cb._CloudNearPlane / previousNearPlane;
cb._EnableFastToneMapping = cameraData.enableExposureControl ? 1 : 0;
bool quarterRes = cameraData.intermediateWidth != cameraData.finalWidth;
cb._LowResolutionEvaluation = cameraData.lowResolution ? 1 : 0;
cb._EnableIntegration = cameraData.enableIntegration ? 1 : 0;
cb._CameraSpace = hdCamera.planet.renderingSpace == RenderingSpace.Camera ? 1 : 0;
cb._ValidSceneDepth = cameraData.cameraType != TVolumetricCloudsCameraType.Sky ? 1 : 0;
cb._IntermediateResolutionScale = cameraData.intermediateWidth == cameraData.finalWidth ? 1u : 2u;
cb._ReprojDepthMipOffset = hdCamera.depthBufferMipChainInfo.mipLevelOffsetsCheckerboard[quarterRes ? 1 : 0];
unsafe
{
for (int p = 0; p < 4; ++p)
for (int i = 0; i < 9; ++i)
cb._DistanceBasedWeights[12 * p + i] = BilateralUpsample.distanceBasedWeights_3x3[9 * p + i];
}
}
unsafe internal void UpdateMatricesForXR(ref ShaderVariablesClouds cb, HDCamera hdCamera)
{
for (int viewIndex = 0; viewIndex < hdCamera.viewCount; ++viewIndex)
{
var vp = hdCamera.m_XRViewConstants[viewIndex].prevViewProjMatrix;
// Correct prev view proj matrix for local mode
if (hdCamera.planet.renderingSpace == RenderingSpace.Camera)
vp *= Matrix4x4.Translate(new Vector3(0.0f, hdCamera.m_XRViewConstants[viewIndex].prevWorldSpaceCameraPos.y, 0.0f));
for (int j = 0; j < 16; ++j)
{
cb._CloudsPixelCoordToViewDirWS[viewIndex * 16 + j] = hdCamera.m_XRViewConstants[viewIndex].pixelCoordToViewDirWS[j];
cb._CameraPrevViewProjection[viewIndex * 16 + j] = vp[j];
}
}
}
struct VolumetricCloudCommonData
{
// Resolution parameters
public TVolumetricCloudsCameraType cameraType;
public bool enableExposureControl;
public bool perceptualBlending;
public bool microErosion;
public bool simplePreset;
public bool pbrSkyActive;
public bool traceForSky;
// Static textures
public Texture3D worley128RGBA;
public Texture3D erosionNoise;
public Texture cloudMapTexture;
public Texture cloudLutTexture;
public BlueNoise.DitheredTextureSet ditheredTextureSet;
public Light sunLight;
// Compute shader and kernels
public ComputeShader volumetricCloudsCS;
public ComputeShader volumetricCloudsTraceCS;
public int renderKernel;
// Cloud constant buffer buffer
public ShaderVariablesClouds cloudsCB;
}
Texture3D ErosionNoiseTypeToTexture(VolumetricClouds.CloudErosionNoise noiseType)
{
switch (noiseType)
{
case VolumetricClouds.CloudErosionNoise.Worley32:
return m_RuntimeResources.worleyNoise32RGB;
case VolumetricClouds.CloudErosionNoise.Perlin32:
return m_RuntimeResources.perlinNoise32RGB;
}
return m_RuntimeResources.worleyNoise32RGB;
}
void FillVolumetricCloudsCommonData(HDCamera hdCamera, bool enableExposureControl, VolumetricClouds settings, TVolumetricCloudsCameraType cameraType, in CloudModelData cloudModelData, ref VolumetricCloudCommonData commonData)
{
commonData.cameraType = cameraType;
commonData.volumetricCloudsCS = m_VolumetricCloudsCS;
commonData.volumetricCloudsTraceCS = m_VolumetricCloudsTraceCS;
commonData.renderKernel = m_CloudRenderKernel;
commonData.pbrSkyActive = hdCamera.volumeStack.GetComponent<VisualEnvironment>().skyType.value == (int)SkyType.PhysicallyBased;
commonData.traceForSky = cameraType == TVolumetricCloudsCameraType.Sky;
commonData.perceptualBlending = cameraType == TVolumetricCloudsCameraType.Default && !hdCamera.msaaEnabled && settings.perceptualBlending.value > 0.0f;
// Static textures
commonData.simplePreset = settings.cloudControl.value == VolumetricClouds.CloudControl.Simple;
if (commonData.simplePreset)
{
commonData.cloudMapTexture = GetPresetCloudMapTexture();
PrepareCustomLutData(settings);
commonData.cloudLutTexture = m_CustomLutPresetMap;
commonData.microErosion = settings.cloudSimpleMode.value == VolumetricClouds.CloudSimpleMode.Quality;
}
else if (settings.cloudControl.value == VolumetricClouds.CloudControl.Advanced)
{
commonData.cloudMapTexture = m_AdvancedCloudMap;
commonData.cloudLutTexture = m_RuntimeResources.cloudLutRainAO;
commonData.microErosion = settings.microErosion.value;
}
else
{
commonData.cloudMapTexture = settings.cloudMap.value != null ? settings.cloudMap.value : Texture2D.blackTexture;
commonData.cloudLutTexture = settings.cloudLut.value != null ? settings.cloudLut.value : Texture2D.blackTexture;
commonData.microErosion = settings.microErosion.value;
}
commonData.worley128RGBA = m_RuntimeResources.worleyNoise128RGBA;
commonData.erosionNoise = ErosionNoiseTypeToTexture(cloudModelData.erosionNoise);
commonData.ditheredTextureSet = m_RenderPipeline.GetBlueNoiseManager().DitheredTextureSet8SPP();
commonData.sunLight = m_RenderPipeline.GetMainLight();
commonData.enableExposureControl = enableExposureControl;
}
void UpdateVolumetricClouds(HDCamera hdCamera, in VolumetricClouds settings)
{
// don't update cloud animation for anything but the main camera
if (GetCameraType(hdCamera) != TVolumetricCloudsCameraType.Default)
return;
// The system needs to updated if the previous frame history is valid
if (EvaluateVolumetricCloudsHistoryValidity(hdCamera))
{
float totalTime = Application.isPlaying ? Time.time : Time.realtimeSinceStartup;
float deltaTime = totalTime - m_CloudsAnimationLastTime;
if (m_CloudsAnimationLastTime == -1.0f)
deltaTime = 0.0f;
#if UNITY_EDITOR
if (UnityEditor.EditorApplication.isPaused)
deltaTime = 0.0f;
#endif
// Conversion from km/h to m/s is the 0.277778f factor
// We apply a minus to see something moving in the right direction
deltaTime *= -0.277778f;
// Compute the wind direction
float theta = settings.orientation.GetValue(hdCamera) / 180.0f * Mathf.PI;
Vector2 windDirection = new Vector2(Mathf.Cos(theta), Mathf.Sin(theta));
// Animate the offsets
m_CloudsAnimationLastTime = totalTime;
m_CloudsAnimationData.cloudOffset += deltaTime * settings.globalWindSpeed.GetValue(hdCamera) * windDirection;
m_CloudsAnimationData.verticalShapeOffset += deltaTime * settings.verticalShapeWindSpeed.value;
m_CloudsAnimationData.verticalErosionOffset += deltaTime * settings.verticalErosionWindSpeed.value;
}
}
class VolumetricCloudsCombineOpaqueData
{
// Material
public Material cloudsCombineMaterial;
public bool perPixelSorting;
public Matrix4x4 pixelCoordToViewDir;
// Input buffers
public TextureHandle volumetricCloudsLightingTexture;
public TextureHandle volumetricCloudsDepthTexture;
public TextureHandle depthAndStencil;
public BufferHandle waterLine;
public BufferHandle cameraHeightBuffer;
public BufferHandle waterSurfaceProfiles;
public TextureHandle waterGBuffer3;
public bool needOpticalFogTransmittance;
public LocalKeyword outputFogTransmittanceKeyword;
}
internal void CombineVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle resolvedDepthBuffer, in HDRenderPipeline.TransparentPrepassOutput transparentPrepass, ref TextureHandle opticalFogTransmittance)
{
if (!transparentPrepass.clouds.valid)
return;
using (var builder = renderGraph.AddRenderPass<VolumetricCloudsCombineOpaqueData>("Volumetric Clouds Combine", out var passData, ProfilingSampler.Get(HDProfileId.VolumetricCloudsCombine)))
{
// Parameters
passData.cloudsCombineMaterial = m_CloudCombinePass;
passData.perPixelSorting = transparentPrepass.enablePerPixelSorting;
passData.pixelCoordToViewDir = hdCamera.mainViewConstants.pixelCoordToViewDirWS;
// Input buffers
passData.volumetricCloudsLightingTexture = builder.ReadTexture(transparentPrepass.clouds.lightingBuffer);
passData.volumetricCloudsDepthTexture = builder.ReadTexture(transparentPrepass.clouds.depthBuffer);
if (passData.perPixelSorting)
{
passData.depthAndStencil = builder.ReadTexture(resolvedDepthBuffer);
passData.waterLine = builder.ReadBuffer(transparentPrepass.waterLine);
passData.cameraHeightBuffer = builder.ReadBuffer(transparentPrepass.waterGBuffer.cameraHeight);
passData.waterSurfaceProfiles = builder.ReadBuffer(transparentPrepass.waterSurfaceProfiles);
passData.waterGBuffer3 = builder.ReadTexture(transparentPrepass.waterGBuffer.waterGBuffer3);
}
// Output buffers
builder.UseColorBuffer(colorBuffer, 0);
int opticalFogBufferIndex = 1;
if (passData.perPixelSorting)
{
builder.UseDepthBuffer(transparentPrepass.beforeRefraction, DepthAccess.Read); // Dummy buffer to avoid 'Setting MRT without a depth buffer is not supported'
builder.UseColorBuffer(transparentPrepass.beforeRefraction, 1);
builder.UseColorBuffer(transparentPrepass.beforeRefractionAlpha, 2);
opticalFogBufferIndex = 3;
}
passData.needOpticalFogTransmittance = LensFlareCommonSRP.IsCloudLayerOpacityNeeded(hdCamera.camera);
passData.outputFogTransmittanceKeyword = m_OutputFogTransmittanceKeyword;
if (passData.needOpticalFogTransmittance)
{
if (!opticalFogTransmittance.IsValid())
opticalFogTransmittance = renderGraph.CreateTexture(HDRenderPipeline.GetOpticalFogTransmittanceDesc(hdCamera));
builder.UseColorBuffer(opticalFogTransmittance, opticalFogBufferIndex);
}
builder.SetRenderFunc(
(VolumetricCloudsCombineOpaqueData data, RenderGraphContext ctx) =>
{
data.cloudsCombineMaterial.SetTexture(HDShaderIDs._VolumetricCloudsLightingTexture, data.volumetricCloudsLightingTexture);
data.cloudsCombineMaterial.SetTexture(HDShaderIDs._VolumetricCloudsDepthTexture, data.volumetricCloudsDepthTexture);
data.cloudsCombineMaterial.SetMatrix(HDShaderIDs._PixelCoordToViewDirWS, data.pixelCoordToViewDir);
if (data.perPixelSorting)
{
data.cloudsCombineMaterial.SetTexture(HDShaderIDs._RefractiveDepthBuffer, data.depthAndStencil, RenderTextureSubElement.Depth);
data.cloudsCombineMaterial.SetTexture(HDShaderIDs._StencilTexture, data.depthAndStencil, RenderTextureSubElement.Stencil);
data.cloudsCombineMaterial.SetBuffer(HDShaderIDs._WaterCameraHeightBuffer, data.cameraHeightBuffer);
data.cloudsCombineMaterial.SetBuffer(HDShaderIDs._WaterSurfaceProfiles, data.waterSurfaceProfiles);
data.cloudsCombineMaterial.SetTexture(HDShaderIDs._WaterGBufferTexture3, data.waterGBuffer3);
data.cloudsCombineMaterial.SetBuffer(HDShaderIDs._WaterLineBuffer, data.waterLine);
}
ctx.cmd.SetKeyword(data.cloudsCombineMaterial, data.outputFogTransmittanceKeyword, data.needOpticalFogTransmittance);
ctx.cmd.DrawProcedural(Matrix4x4.identity, data.cloudsCombineMaterial, data.perPixelSorting ? 7 : 0, MeshTopology.Triangles, 3);
});
}
}
internal struct VolumetricCloudsOutput
{
public TextureHandle lightingBuffer;
public TextureHandle depthBuffer;
public bool valid;
}
internal void RenderVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle depthPyramid,
ref HDRenderPipeline.TransparentPrepassOutput transparentPrepass, ref TextureHandle opticalFogTransmittance)
{
// If the current volume does not enable the feature, quit right away.
VolumetricClouds settings = hdCamera.volumeStack.GetComponent<VolumetricClouds>();
bool skipCloudRendering = m_RenderPipeline.m_CurrentDebugDisplaySettings.DebugHideVolumetricClouds(hdCamera) || !HasVolumetricClouds(hdCamera, in settings);
#if UNITY_EDITOR
skipCloudRendering |= !hdCamera.camera.renderCloudsInSceneView;
#endif
if (skipCloudRendering)
{
transparentPrepass.clouds = new VolumetricCloudsOutput()
{
lightingBuffer = renderGraph.defaultResources.whiteTextureXR,
depthBuffer = renderGraph.defaultResources.blackTextureXR,
valid = false,
};
return;
}
// Make sure the volumetric clouds are animated properly
UpdateVolumetricClouds(hdCamera, in settings);
// Evaluate which version of the clouds we should be using
TVolumetricCloudsCameraType cameraType = GetCameraType(hdCamera);
bool accumulationClouds = cameraType == TVolumetricCloudsCameraType.Default;
bool fullResolutionClouds = cameraType == TVolumetricCloudsCameraType.BakedReflection;
// Render the clouds
if (accumulationClouds)
transparentPrepass.clouds = RenderVolumetricClouds_Accumulation(renderGraph, hdCamera, cameraType, colorBuffer, depthPyramid);
else if (fullResolutionClouds)
transparentPrepass.clouds = RenderVolumetricClouds_FullResolution(renderGraph, hdCamera, cameraType, colorBuffer, depthPyramid);
else // realtime reflection
transparentPrepass.clouds = RenderVolumetricClouds_LowResolution(renderGraph, hdCamera, cameraType, colorBuffer, depthPyramid);
// Push the texture to the debug menu
if (m_RenderPipeline.m_CurrentDebugDisplaySettings.data.volumetricCloudDebug == VolumetricCloudsDebug.Lighting)
m_RenderPipeline.PushFullScreenDebugTexture(renderGraph, transparentPrepass.clouds.lightingBuffer, FullScreenDebugMode.VolumetricClouds);
else
m_RenderPipeline.PushFullScreenDebugTexture(renderGraph, transparentPrepass.clouds.depthBuffer, FullScreenDebugMode.VolumetricClouds, GraphicsFormat.R32_SFloat);
}
internal void PreRenderVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera)
{
if (m_RenderPipeline.m_CurrentDebugDisplaySettings.DebugHideSky(hdCamera))
return;
// Grab the volume settings
VolumetricClouds settings = hdCamera.volumeStack.GetComponent<VolumetricClouds>();
// If the clouds are enabled on this camera
if (!HasVolumetricClouds(hdCamera, in settings))
return;
// Evaluate the cloud map
PreRenderVolumetricCloudMap(renderGraph, hdCamera, in settings);
// Render the cloud shadows
RenderVolumetricCloudsShadows(renderGraph, hdCamera, in settings);
}
GraphicsFormat GetCloudsColorFormat(VolumetricClouds settings, bool isHistoryBuffer)
{
// When neighborhood clamping is disabled, using R11G11B10 format for reprojection causes color shift
if (!settings.ghostingReduction.value && isHistoryBuffer) return GraphicsFormat.R16G16B16A16_SFloat;
return (GraphicsFormat)m_RenderPipeline.asset.currentPlatformRenderPipelineSettings.colorBufferFormat;
}
void CreateTracingTextures(RenderGraph renderGraph, RenderGraphBuilder builder, VolumetricClouds settings, float scale, out TextureHandle cloudsLighting, out TextureHandle cloudsDepth)
{
cloudsLighting = builder.CreateTransientTexture(new TextureDesc(Vector2.one * scale, true, true)
{ colorFormat = GetCloudsColorFormat(settings, false), enableRandomWrite = true, name = "Traced Clouds Lighting" });
cloudsDepth = builder.CreateTransientTexture(new TextureDesc(Vector2.one * scale, true, true)
{ colorFormat = GraphicsFormat.R16G16_SFloat, enableRandomWrite = true, name = "Traced Clouds Depth" });
}
void CreateIntermediateTextures(RenderGraph renderGraph, RenderGraphBuilder builder, VolumetricClouds settings, out TextureHandle intermediate1, out TextureHandle intermediate2)
{
intermediate1 = builder.CreateTransientTexture(new TextureDesc(Vector2.one * 0.5f, true, true)
{ colorFormat = GetCloudsColorFormat(settings, false), enableRandomWrite = true, name = "Temporary Clouds Lighting Buffer 1" });
intermediate2 = builder.CreateTransientTexture(new TextureDesc(Vector2.one * 0.5f, true, true)
{ colorFormat = GraphicsFormat.R16G16B16A16_SFloat, enableRandomWrite = true, name = "Temporary Clouds Lighting Buffer 2" });
}
void CreateOutputTextures(RenderGraph renderGraph, RenderGraphBuilder builder, VolumetricClouds settings, out TextureHandle cloudsLighting, out TextureHandle cloudsDepth)
{
cloudsLighting = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GetCloudsColorFormat(settings, false), enableRandomWrite = true, name = "Volumetric Clouds Lighting Texture" }));
cloudsDepth = builder.WriteTexture(renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
{ colorFormat = GraphicsFormat.R16G16_SFloat, enableRandomWrite = true, name = "Volumetric Clouds Depth Texture" }));
}
static void DoVolumetricCloudsTrace(CommandBuffer cmd, int traceTX, int traceTY, int viewCount, in VolumetricCloudCommonData commonData,
GraphicsBuffer ambientProbe, RTHandle colorBuffer, RTHandle depthPyramid,
RTHandle cloudsLightingOutput, RTHandle cloudsDepthOutput)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.VolumetricCloudsTrace)))
{
CoreUtils.SetKeyword(cmd, "PERCEPTUAL_TRANSMITTANCE", commonData.perceptualBlending);
CoreUtils.SetKeyword(cmd, "CLOUDS_SIMPLE_PRESET", commonData.simplePreset);
CoreUtils.SetKeyword(cmd, "CLOUDS_MICRO_EROSION", commonData.microErosion);
CoreUtils.SetKeyword(cmd, "PHYSICALLY_BASED_SUN", commonData.pbrSkyActive);
CoreUtils.SetKeyword(cmd, "TRACE_FOR_SKY", commonData.traceForSky);
cmd.SetComputeBufferParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._VolumetricCloudsAmbientProbeBuffer, ambientProbe);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CameraColorTexture, colorBuffer);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CameraDepthTexture, depthPyramid);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._Worley128RGBA, commonData.worley128RGBA);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._ErosionNoise, commonData.erosionNoise);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CloudMapTexture, commonData.cloudMapTexture);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CloudLutTexture, commonData.cloudLutTexture);
// Output buffers
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CloudsLightingTextureRW, cloudsLightingOutput);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._CloudsDepthTextureRW, cloudsDepthOutput);
cmd.DispatchCompute(commonData.volumetricCloudsTraceCS, commonData.renderKernel, traceTX, traceTY, viewCount);
}
}
static void DoVolumetricCloudsReproject(CommandBuffer cmd, int kernel, int traceTX, int traceTY, int viewCount, in VolumetricCloudCommonData commonData,
RTHandle tracedCloudsLighting, RTHandle tracedCloudsDepth, RTHandle depthPyramid,
bool withHistory, bool clearHistory, RTHandle previousHistory0Buffer, RTHandle previousHistory1Buffer,
RTHandle lightingOutput, RTHandle additionalOutput)
{
var marker = withHistory ? HDProfileId.VolumetricCloudsReproject : HDProfileId.VolumetricCloudsPreUpscale;
using (new ProfilingScope(cmd, ProfilingSampler.Get(marker)))
{
if (withHistory)
{
if (clearHistory)
{
CoreUtils.SetRenderTarget(cmd, previousHistory0Buffer, clearFlag: ClearFlag.Color, clearColor: Color.black);
CoreUtils.SetRenderTarget(cmd, previousHistory1Buffer, clearFlag: ClearFlag.Color, clearColor: Color.black);
}
// History buffers
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._HistoryVolumetricClouds0Texture, previousHistory0Buffer);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._HistoryVolumetricClouds1Texture, previousHistory1Buffer);
}
// Input textures
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CameraDepthTexture, depthPyramid);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsLightingTexture, tracedCloudsLighting);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsDepthTexture, tracedCloudsDepth);
// Output textures
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsLightingTextureRW, lightingOutput);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsAdditionalTextureRW, additionalOutput);
// Re-project from the previous frame
cmd.DispatchCompute(commonData.volumetricCloudsCS, kernel, traceTX, traceTY, viewCount);
}
}
static void DoVolumetricCloudsUpscale(CommandBuffer cmd, int kernel, int traceTX, int traceTY, int viewCount, in VolumetricCloudCommonData commonData,
RTHandle currentHistory0Buffer, RTHandle currentHistory1Buffer, RTHandle colorBuffer, RTHandle depthPyramid,
RTHandle cloudsLighting, RTHandle cloudsDepth)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.VolumetricCloudsUpscale)))
{
// Compute the final resolution parameters
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._VolumetricCloudsTexture, currentHistory0Buffer);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._DepthStatusTexture, currentHistory1Buffer);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CameraColorTexture, colorBuffer);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CameraDepthTexture, depthPyramid);
// Output clouds texture (scattering + transmittance)
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._VolumetricCloudsLightingTextureRW, cloudsLighting);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._VolumetricCloudsDepthTextureRW, cloudsDepth);
cmd.DispatchCompute(commonData.volumetricCloudsCS, kernel, traceTX, traceTY, viewCount);
}
}
static float GetCloudNearPlane(float3 originPS, float lowerBoundPS, float higherBoundPS)
{
float radialDistance = math.length(originPS);
float rcpRadialDistance = math.rcp(radialDistance);
float cosChi = 1.0f;
Vector2 tInner = PhysicallyBasedSky.IntersectSphere(lowerBoundPS, cosChi, radialDistance, rcpRadialDistance);
Vector2 tOuter = PhysicallyBasedSky.IntersectSphere(higherBoundPS, -cosChi, radialDistance, rcpRadialDistance);
if (tInner.x < 0.0 && tInner.y >= 0.0) // Below the lower bound
return tInner.y;
else // Inside or above the cloud volume
return math.max(tOuter.x, 0.0f);
}
}
[Serializable]
[SupportedOnRenderPipeline(typeof(HDRenderPipelineAsset))]
[Categorization.CategoryInfo(Name = "R: Volumetric Clouds", Order = 1000), HideInInspector]
class VolumetricCloudsRuntimeResources : IRenderPipelineResources
{
public int version => 0;
#region Shaders
[Header("Shaders")]
[SerializeField, ResourcePath("Runtime/Lighting/VolumetricClouds/VolumetricClouds.compute")]
private ComputeShader m_VolumetricCloudsCS;
public ComputeShader volumetricCloudsCS
{
get => m_VolumetricCloudsCS;
set => this.SetValueAndNotify(ref m_VolumetricCloudsCS, value);
}
[SerializeField, ResourcePath("Runtime/Lighting/VolumetricClouds/VolumetricCloudsTrace.compute")]
private ComputeShader m_VolumetricCloudsTraceCS;
public ComputeShader volumetricCloudsTraceCS
{
get => m_VolumetricCloudsTraceCS;
set => this.SetValueAndNotify(ref m_VolumetricCloudsTraceCS, value);
}
[SerializeField, ResourcePath("Runtime/Lighting/VolumetricClouds/VolumetricCloudsTraceShadows.compute")]
private ComputeShader m_VolumetricCloudsTraceShadowsCS;
public ComputeShader volumetricCloudsTraceShadowsCS
{
get => m_VolumetricCloudsTraceShadowsCS;
set => this.SetValueAndNotify(ref m_VolumetricCloudsTraceShadowsCS, value);
}
[SerializeField, ResourcePath("Runtime/Lighting/VolumetricClouds/VolumetricCloudsShadowFilter.compute")]
private ComputeShader m_VolumetricCloudsShadowFilterCS;
public ComputeShader volumetricCloudsShadowFilterCS
{
get => m_VolumetricCloudsShadowFilterCS;
set => this.SetValueAndNotify(ref m_VolumetricCloudsShadowFilterCS, value);
}
[SerializeField, ResourcePath("Editor/Lighting/VolumetricClouds/CloudMapGenerator.compute")]
private ComputeShader m_VolumetricCloudMapGeneratorCS;
public ComputeShader volumetricCloudMapGeneratorCS
{
get => m_VolumetricCloudMapGeneratorCS;
set => this.SetValueAndNotify(ref m_VolumetricCloudMapGeneratorCS, value);
}
[SerializeField, ResourcePath("Runtime/Lighting/VolumetricClouds/VolumetricCloudsCombine.shader")]
private Shader m_VolumetricCloudsCombinePS;
public Shader volumetricCloudsCombinePS
{
get => m_VolumetricCloudsCombinePS;
set => this.SetValueAndNotify(ref m_VolumetricCloudsCombinePS, value);
}
#endregion
#region Textures
[Header("Textures")]
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Texture/VolumetricClouds/CloudLutRainAO.png")]
private Texture2D m_CloudLutRainAO;
public Texture2D cloudLutRainAO
{
get => m_CloudLutRainAO;
set => this.SetValueAndNotify(ref m_CloudLutRainAO, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Texture/VolumetricClouds/WorleyNoise128RGBA.png")]
private Texture3D m_WorleyNoise128RGBA;
public Texture3D worleyNoise128RGBA
{
get => m_WorleyNoise128RGBA;
set => this.SetValueAndNotify(ref m_WorleyNoise128RGBA, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Texture/VolumetricClouds/WorleyNoise32RGB.png")]
private Texture3D m_WorleyNoise32RGB;
public Texture3D worleyNoise32RGB
{
get => m_WorleyNoise32RGB;
set => this.SetValueAndNotify(ref m_WorleyNoise32RGB, value);
}
[SerializeField][ResourcePath("Runtime/RenderPipelineResources/Texture/VolumetricClouds/PerlinNoise32RGB.png")]
private Texture3D m_PerlinNoise32RGB;
public Texture3D perlinNoise32RGB
{
get => m_PerlinNoise32RGB;
set => this.SetValueAndNotify(ref m_PerlinNoise32RGB, value);
}
#endregion
}
}