You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

823 lines
41 KiB

using Unity.Mathematics;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
namespace UnityEngine.Rendering.HighDefinition
{
public partial class HDRenderPipeline
{
// 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;
// Prepass kernels
int m_CloudDownscaleDepthKernel;
// 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;
// Flag that allows us to track the resources that habe been allocated
bool m_ActiveVolumetricClouds;
// Combine pass via hardware blending.
Material m_CloudCombinePass;
LocalKeyword m_OutputFogTransmittanceKeyword;
// Animation time is shared for all cameras, but only updated by the main camera
internal struct VolumetricCloudsAnimationData
{
internal float time;
public Vector2 cloudOffset;
public float verticalShapeOffset;
public float verticalErosionOffset;
}
internal VolumetricCloudsAnimationData 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;
}
void InitializeVolumetricClouds()
{
// Keep track of the state for the release
m_ActiveVolumetricClouds = m_Asset.currentPlatformRenderPipelineSettings.supportVolumetricClouds;
if (!m_ActiveVolumetricClouds)
return;
// Allocate the buffers for ambient probe evaluation
m_PhaseZHClouds = new ZonalHarmonicsL2();
m_PhaseZHClouds.coeffs = new float[3];
// Grab the kernels we need
m_VolumetricCloudsCS = runtimeShaders.volumetricCloudsCS;
m_CloudDownscaleDepthKernel = m_VolumetricCloudsCS.FindKernel("DownscaleDepth");
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(runtimeShaders.volumetricCloudsCombinePS);
m_OutputFogTransmittanceKeyword = new LocalKeyword(m_CloudCombinePass.shader, "OUTPUT_TRANSMITTANCE_BUFFER");
m_VolumetricCloudsTraceCS = runtimeShaders.volumetricCloudsTraceCS;
m_CloudRenderKernel = m_VolumetricCloudsTraceCS.FindKernel("RenderClouds");
// Allocate all the texture initially
AllocatePresetTextures();
// Initialize cloud animation
m_CloudsAnimationData = new()
{
time = -1.0f,
cloudOffset = new Vector2(0.0f, 0.0f),
verticalShapeOffset = 0.0f,
verticalErosionOffset = 0.0f,
};
// Initialize the additional sub components
InitializeVolumetricCloudsMap();
InitializeVolumetricCloudsShadows();
InitializeVolumetricCloudsAmbientProbe();
}
void ReleaseVolumetricClouds()
{
if (!m_ActiveVolumetricClouds)
return;
// Destroy the material
CoreUtils.Destroy(m_CloudCombinePass);
// Release the additional sub components
ReleaseVolumetricCloudsMap();
ReleaseVolumetricCloudsShadows();
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 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 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 = RayTracingFrameIndex(hdCamera, 64);
cb._AccumulationFrameIndex = frameIndex / 4;
cb._SubPixelIndex = frameIndex % 4;
// PB Sun/Sky settings
Light currentSun = GetMainLight();
HDAdditionalLightData additionalLightData = null;
if (currentSun != null)
{
// Grab the target sun additional data
additionalLightData = m_CurrentSunLightAdditionalLightData;
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;
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;
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;
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 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 runtimeTextures.worleyNoise32RGB;
case VolumetricClouds.CloudErosionNoise.Perlin32:
return runtimeTextures.perlinNoise32RGB;
}
return runtimeTextures.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;
// 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 = runtimeTextures.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 = runtimeTextures.worleyNoise128RGBA;
commonData.erosionNoise = ErosionNoiseTypeToTexture(cloudModelData.erosionNoise);
BlueNoise blueNoise = GetBlueNoiseManager();
commonData.ditheredTextureSet = blueNoise.DitheredTextureSet8SPP();
commonData.sunLight = 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_CloudsAnimationData.time;
if (m_CloudsAnimationData.time == -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_CloudsAnimationData.time = 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;
}
void CombineVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle resolvedDepthBuffer, in 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(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;
}
void RenderVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera, TextureHandle colorBuffer, TextureHandle depthPyramid,
TextureHandle volumetricLighting, ref 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_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, volumetricLighting);
else if (fullResolutionClouds)
transparentPrepass.clouds = RenderVolumetricClouds_FullResolution(renderGraph, hdCamera, cameraType, colorBuffer, depthPyramid, volumetricLighting);
else // realtime reflection
transparentPrepass.clouds = RenderVolumetricClouds_LowResolution(renderGraph, hdCamera, cameraType, colorBuffer, depthPyramid, volumetricLighting);
// Push the texture to the debug menu
if (m_CurrentDebugDisplaySettings.data.volumetricCloudDebug == VolumetricCloudsDebug.Lighting)
PushFullScreenDebugTexture(m_RenderGraph, transparentPrepass.clouds.lightingBuffer, FullScreenDebugMode.VolumetricClouds);
else
PushFullScreenDebugTexture(m_RenderGraph, transparentPrepass.clouds.depthBuffer, FullScreenDebugMode.VolumetricClouds, GraphicsFormat.R32_SFloat);
}
class AccumulateOpticalFogTransmittancePassData
{
public TextureHandle cloudsLighting;
public TextureHandle opticalFogTransmittance;
public Material cloudCombinePass;
}
void PreRenderVolumetricClouds(RenderGraph renderGraph, HDCamera hdCamera)
{
if (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);
}
// Computes a half res buffer of the scene depth (TODO: share that with other effects)
static void DoVolumetricCloudsDepthDownscale(CommandBuffer cmd, int kernel, int traceTX, int traceTY, int viewCount, in VolumetricCloudCommonData commonData,
RTHandle depthPyramid, RTHandle halfResDepthBuffer)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.VolumetricCloudsDepthDownscale)))
{
// Compute the alternative version of the mip 1 of the depth (min instead of max that is required to handle high frequency meshes (vegetation, hair)
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._DepthTexture, depthPyramid);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._HalfResDepthBufferRW, halfResDepthBuffer);
cmd.DispatchCompute(commonData.volumetricCloudsCS, kernel, traceTX, traceTY, viewCount);
}
}
static void DoVolumetricCloudsTrace(CommandBuffer cmd, int traceTX, int traceTY, int viewCount, in VolumetricCloudCommonData commonData,
RTHandle volumetricLightingTexture, RTHandle sceneDepth, GraphicsBuffer ambientProbe,
RTHandle cloudsLightingOutput, RTHandle cloudsDepthOutput)
{
using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.VolumetricCloudsTrace)))
{
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.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._VBufferLighting, volumetricLightingTexture);
cmd.SetComputeTextureParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._VolumetricCloudsSourceDepth, sceneDepth);
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);
cmd.SetComputeBufferParam(commonData.volumetricCloudsTraceCS, commonData.renderKernel, HDShaderIDs._VolumetricCloudsAmbientProbeBuffer, ambientProbe);
// 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 halfResCloudsLighting, RTHandle halfResCloudsDepth, RTHandle halfResDepthBuffer,
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);
}
// Re-project the result from the previous frame
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsLightingTexture, halfResCloudsLighting);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._CloudsDepthTexture, halfResCloudsDepth);
cmd.SetComputeTextureParam(commonData.volumetricCloudsCS, kernel, HDShaderIDs._HalfResDepthBuffer, halfResDepthBuffer);
// 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 currentDepthBuffer,
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._DepthTexture, currentDepthBuffer);
// 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);
}
}
}