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.
144 lines
5.5 KiB
144 lines
5.5 KiB
#ifndef UNITY_PATH_TRACING_VOLUME_INCLUDED
|
|
#define UNITY_PATH_TRACING_VOLUME_INCLUDED
|
|
|
|
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/PathTracing/Shaders/PathTracingLight.hlsl"
|
|
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/VolumeRendering.hlsl"
|
|
|
|
float ComputeHeightFogMultiplier(float height)
|
|
{
|
|
return ComputeHeightFogMultiplier(height, _HeightFogBaseHeight, _HeightFogExponents);
|
|
}
|
|
|
|
bool SampleVolumeScatteringPosition(uint2 pixelCoord, inout float inputSample, inout float t, inout float pdf, out bool sampleLocalLights, out float3 lightPosition)
|
|
{
|
|
sampleLocalLights = false;
|
|
|
|
if (!_FogEnabled || !_EnableVolumetricFog)
|
|
return false;
|
|
|
|
// This will determine the interval in which volumetric scattering can occur
|
|
float tMin, tMax;
|
|
float pdfVol = 1.0;
|
|
float tFog = min(t, _MaxFogDistance);
|
|
|
|
if (_FogDirectionalOnly)
|
|
{
|
|
if (!_DirectionalLightCount)
|
|
return false;
|
|
|
|
tMin = 0.0;
|
|
tMax = tFog;
|
|
}
|
|
else // Directional and local lights
|
|
{
|
|
float pickedLightWeight;
|
|
float localWeight = PickLocalLightInterval(WorldRayOrigin(), WorldRayDirection(), inputSample, lightPosition, pickedLightWeight, tMin, tMax);
|
|
|
|
if (localWeight < 0.0)
|
|
return false;
|
|
|
|
sampleLocalLights = inputSample < localWeight;
|
|
if (sampleLocalLights)
|
|
{
|
|
tMax = min(tMax, tFog);
|
|
if (tMin >= tMax)
|
|
return false;
|
|
|
|
inputSample = RescaleSampleUnder(inputSample, localWeight);
|
|
pdfVol *= localWeight * pickedLightWeight;
|
|
}
|
|
else
|
|
{
|
|
tMin = 0.0;
|
|
tMax = tFog;
|
|
|
|
inputSample = RescaleSampleOver(inputSample, localWeight);
|
|
pdfVol *= 1.0 - localWeight;
|
|
}
|
|
}
|
|
|
|
// FIXME: not quite sure what the sigmaT value is supposed to be...
|
|
const float sigmaT = _HeightFogBaseExtinction;
|
|
const float transmittanceTMin = max(exp(-tMin * sigmaT), 0.01);
|
|
const float transmittanceTMax = max(exp(-tMax * sigmaT), 0.01);
|
|
const float transmittanceThreshold = t < FLT_MAX ? 1.0 - min(0.5, transmittanceTMax) : 1.0;
|
|
|
|
if (inputSample >= transmittanceThreshold)
|
|
{
|
|
inputSample = RescaleSampleOver(inputSample, transmittanceThreshold);
|
|
pdf *= 1.0 - transmittanceThreshold;
|
|
return false;
|
|
}
|
|
|
|
inputSample = RescaleSampleUnder(inputSample, transmittanceThreshold);
|
|
pdf *= pdfVol * transmittanceThreshold;
|
|
|
|
// Exponential sampling
|
|
float transmittance = transmittanceTMax + inputSample * (transmittanceTMin - transmittanceTMax);
|
|
t = -log(transmittance) / sigmaT;
|
|
pdf *= sigmaT * transmittance / (transmittanceTMin - transmittanceTMax);
|
|
|
|
return true;
|
|
}
|
|
|
|
// Function responsible for volume scattering
|
|
void ComputeVolumeScattering(inout PathPayload payload : SV_RayPayload, float3 inputSample, bool sampleLocalLights, float3 lightPosition)
|
|
{
|
|
// Reset the payload color, which will store our final result
|
|
payload.value = 0.0;
|
|
|
|
SetPathTracingFlag(payload, PATHTRACING_FLAG_VOLUME_INTERACTION);
|
|
|
|
// Check if we want to compute direct lighting for current depth
|
|
bool minDepthAllowsDirect = payload.segmentID + 1 >= _RaytracingMinRecursion - 1;
|
|
// Check if we want to send more rays after this segment
|
|
bool haveReachedMaxDepth = payload.segmentID + 1 > _RaytracingMaxRecursion - 1;
|
|
|
|
// Compute the scattering position
|
|
float3 scatteringPosition = WorldRayOrigin() + payload.rayTHit * WorldRayDirection();
|
|
float3 incomingDirection = WorldRayDirection();
|
|
|
|
// Create the list of active lights (a local light can be forced by providing its position)
|
|
LightList lightList = CreateLightList(scatteringPosition, sampleLocalLights, lightPosition);
|
|
payload.lightListParams = float4(lightPosition, sampleLocalLights ? 1 : 0);
|
|
|
|
float pdf, shadowOpacity;
|
|
float3 value;
|
|
float3 sampleRayDirection;
|
|
float sampleRayDistance;
|
|
|
|
if (minDepthAllowsDirect && !haveReachedMaxDepth)
|
|
{
|
|
float scatteringHeight = dot(scatteringPosition, _PlanetUp);
|
|
|
|
// Light sampling
|
|
if (SampleLights(lightList, inputSample, scatteringPosition, 0.0, true, sampleRayDirection, value, pdf, sampleRayDistance, shadowOpacity))
|
|
{
|
|
// Apply phase function and divide by PDF
|
|
float phasePdf = HenyeyGreensteinPhaseFunction(_GlobalFogAnisotropy, dot(incomingDirection, sampleRayDirection));
|
|
value *= _HeightFogBaseScattering.xyz * ComputeHeightFogMultiplier(scatteringPosition.y) * phasePdf / pdf;
|
|
|
|
if (GetCurrentExposureMultiplier() * Luminance(value) > 0.0001)
|
|
{
|
|
PushLightSampleQuery(scatteringPosition, sampleRayDirection, sampleRayDistance - _RayTracingRayBias, PowerHeuristic(pdf, phasePdf) * value, shadowOpacity, payload);
|
|
}
|
|
}
|
|
|
|
// Phase function sampling
|
|
if (SampleHenyeyGreenstein(incomingDirection, _GlobalFogAnisotropy, inputSample, sampleRayDirection, pdf))
|
|
{
|
|
// Applying phase function and dividing by PDF cancels out
|
|
value = _HeightFogBaseScattering.xyz * ComputeHeightFogMultiplier(scatteringHeight);
|
|
|
|
if (Luminance(value) > 0.001)
|
|
{
|
|
payload.throughput *= value;
|
|
payload.interactionThroughput *= value;
|
|
payload.materialSamplePdf = pdf;
|
|
PushMaterialSampleQuery(scatteringPosition, sampleRayDirection, payload);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif // UNITY_PATH_TRACING_VOLUME_INCLUDED
|