Files
UnrealEngineUWP/Engine/Shaders/Private/PathTracing/Volume/PathTracingAtmosphereCommon.ush
chris kulla b46d29e962 Implement Sky Atmosphere and Exponential Height Fog support in the Path Tracer
This is the start of volumetric support in the path tracer, so a lot of basic infrastructure had to be put into place, making this changelist fairly large. Some shuffling of light parameters had to be done to make room for the volumetric scattering multiplier.

The integration strategy is to use null tracking (with Spectral MIS) for choosing a random scatter point along the ray. This point is chosen similarly to transparent hits, so surface and volume shading are unified. However, these volume hits are chosen proportionally to transmittance times scattering which is not optimal for lights embedded in the volume. To handle the latter, we also allow the main trace call to return a volume segment over which we can compute the direct lighting from local light sources. To simplify the handling of overlapping media and inter-mixed transparent hits, we stochastically select a ray segment. From this point on, we can evaluate direct lighting using equi-angular sampling. The MIS combination of equi-angular sampling and null tracked spectral density sampling was prototyped but found not to bring any improvement for the currently implemented volume types. This will likely have to be revisited in the future.

To improve quality and reduce the amount of ray-marching required, the volume API allows for analytic implementations of transmittance for cases where this is possible to do more efficiently than by ray-marching.

Implementation details for Atmosphere: This volume type is a planet sized, spherically symetric model. Because the default units in UE are centimeters, objects like the planet that are kilometer sized will run into all sorts of numerical precision artifacts. To solve this, an implementation of double-word arithmetic was added which allows enough decimal digits to robustly intersect the planet, as well as cary out the lookups required. The Transmittance is cached in a lookup table indexed by height above the ground and viewing angle cosine. This is similar to the LUT used by the realtime version but with a different parameterization which covers the full range of angles/heights with high precision. This lookup table is automatically baked on demand when atmosphere parameters change. The volumetric sky model is only used when "reference atmosphere" is enabled in the post process volume. This is because the existing approached (cached into a skylight) is generally a bit faster and supports clouds. This toggle may be removed as the support for volumes matures.

Implementation details for ExponentialHeightFog: This volume is represented as a finite slab centered around the camera. Transmittance is easily computed analytically for this volume. We only add this volume when the volumetric fog checkbox is enabled, as the default parameters are not fully physically based. We limit the fog to be present only within a certain radius of the camera, to prevent rays from scattering forever.

#preflight 620ad32d583261b0a66af216
#rb Sebastien.Hillaire,Patrick.Kelly
#preflight 620dd2270931bfd925e5936b

[CL 19031069 by chris kulla in ue5-main branch]
2022-02-17 00:21:09 -05:00

54 lines
1.7 KiB
Plaintext

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
// Atmosphere function that are independent of any shader parameters
float Gain(float x, float k)
{
float a = 1 - 2 * x;
return 0.5 - 0.5 * sign(a) * pow(abs(a), k);
}
float ComputeHorizonCosine(float ViewHeight, float BotRadius)
{
const float R = BotRadius / ViewHeight;
return -sqrt(saturate(1.0 - R * R));
}
float2 ToAtmosphereOpticalDepthLUTUV(float Height, float Cosine, float BotRadius, float TopRadius)
{
const float HorizonCosine = ComputeHorizonCosine(Height, BotRadius);
const float Q = 0.5; // line up horizon line to u=Q
const float2 ab = Cosine >= HorizonCosine
? float2((HorizonCosine - 1) / (Q - 1), (Q - HorizonCosine) / (Q - 1)) // above horizon
: float2((1 + HorizonCosine) / Q, -1); // below horizon
float2 UV = float2((Cosine - ab.y) / ab.x, sqrt(saturate((Height - BotRadius) / (TopRadius - BotRadius))));
// un-squish
const float Sq = 2.0;
UV.x = Gain(UV.x, 1.0 / Sq);
return UV;
}
float2 FromAtmosphereOpticalDepthLUTUV(float2 UV, float BotRadius, float TopRadius)
{
const float Height = lerp(BotRadius, TopRadius, Pow2(UV.y)); // more resolution toward the ground
// from this height, at what cosine is the horizon line?
const float HorizonCosine = ComputeHorizonCosine(Height, BotRadius);
// squish around 0.5 to increase resolution near the horizon
const float Sq = 2.0;
const float U = Gain(UV.x, Sq);
const float Q = 0.5; // line up horizon line to u=Q
const float2 ab = U > Q
? float2((HorizonCosine - 1) / (Q - 1), (Q - HorizonCosine) / (Q - 1)) // above horizon
: float2((1 + HorizonCosine) / Q, -1); // below horizon
const float Cosine = ab.x * U + ab.y;
return float2(Height, Cosine);
}