// Copyright (c) Wojciech Figat. All rights reserved.

#ifndef __VOLUMETRIC_FOG__
#define __VOLUMETRIC_FOG__

#include "./Flax/Noise.hlsl"

#define VOLUMETRIC_FOG_GRID_Z_LINEAR 1

// Structure that contains information about volumetric fog
struct VolumetricFogData
{
    float4 GridSliceParameters;
    float2 ScreenSize;
    float2 VolumeTexelSize; // Scaled for dithering
};

float GetDepthFromSlice(float4 gridSliceParameters, float zSlice)
{
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
	return (zSlice - gridSliceParameters.z) * gridSliceParameters.x;
#else
    return (exp2(zSlice / gridSliceParameters.z) - gridSliceParameters.y) / gridSliceParameters.x;
#endif
}

float GetSliceFromDepth(float4 gridSliceParameters, float sceneDepth)
{
#if VOLUMETRIC_FOG_GRID_Z_LINEAR
    return sceneDepth * gridSliceParameters.y + gridSliceParameters.z;
#else
	return (log2(sceneDepth * gridSliceParameters.x + gridSliceParameters.y) * gridSliceParameters.z);
#endif
}

float4 SampleVolumetricFog(Texture3D volumetricFogTexture, VolumetricFogData volumetricFogData, float3 viewVector, float2 uv, float4 temporalAAJitter = 0)
{
    // Project view vector to get 3D frustum UVW coordinates
    float sceneDepth = length(viewVector);
    float zSlice = GetSliceFromDepth(volumetricFogData.GridSliceParameters, sceneDepth) * volumetricFogData.GridSliceParameters.w;
    float3 volumeUV = float3(uv, zSlice);

    // Dither to reduce banding artifacts
    float2 noiseUV = volumeUV.xy + temporalAAJitter.xy;
    float2 noise = rand2dTo2d(noiseUV * volumetricFogData.ScreenSize) * 2.0f - 1.0f;
    volumeUV.xy += noise * volumetricFogData.VolumeTexelSize;

    // Sample 3D texture
    return volumetricFogTexture.SampleLevel(SamplerLinearClamp, volumeUV, 0);
}

float4 CombineVolumetricFog(float4 fog, float4 volumetricFog)
{
    return float4(volumetricFog.rgb + fog.rgb * volumetricFog.a, volumetricFog.a * fog.a);
}

#endif
