Skip to content

Instantly share code, notes, and snippets.

@sociofall
Created August 29, 2025 14:14
Show Gist options
  • Select an option

  • Save sociofall/951756a413a679c3e4b60f2ff01f6dcc to your computer and use it in GitHub Desktop.

Select an option

Save sociofall/951756a413a679c3e4b60f2ff01f6dcc to your computer and use it in GitHub Desktop.
Tricubic Sampling for Unreal Engine – Volumetric Fog modifications
# Unreal Engine – Tricubic Sampling for Volumetric Fog
# This method provides better terminators, hiding the aliasing
## 1. Paste the function
Navigate to: Engine/Shaders/Private/HeightFogCommon.ush
At the top of the file (ex. after FogStructData struct) insert the following function:
// tricubic interpolation function for sampling the volumetric fog 3D texture
// ported from https://www.shadertoy.com/view/cts3Rj
float4 textureTriCubic(Texture3D sourceTex, SamplerState sourceSampler, float3 texCoords)
{
// setup
uint width, height, depth;
sourceTex.GetDimensions(width, height, depth);
float3 texSize = float3(width, height, depth);
float3 coord_grid = texCoords * texSize - 0.5;
float3 index = floor(coord_grid);
float3 fraction = coord_grid - index;
fraction = saturate(fraction);
float3 one_frac = 1.0 - fraction;
float3 w0 = 1.0/6.0 * one_frac * one_frac * one_frac;
float3 w1 = 2.0/3.0 - 0.5 * fraction * fraction * (2.0 - fraction);
float3 w2 = 2.0/3.0 - 0.5 * one_frac * one_frac * (2.0 - one_frac);
float3 w3 = 1.0/6.0 * fraction * fraction * fraction;
float3 g0 = w0 + w1;
float3 g1 = w2 + w3;
float3 mult = 1.0 / texSize;
float3 h0 = mult * ((w1 / g0) - 0.5 + index);
float3 h1 = mult * ((w3 / g1) + 1.5 + index);
// clamp to valid uv range, since actual uv is located within this rectangle -> View.VolumetricFogUVMax.xy
const float2 actualmaxuv_xy = View.VolumetricFogUVMax.xy;
h0.xy = clamp(h0.xy, 0, actualmaxuv_xy);
h1.xy = clamp(h1.xy, 0, actualmaxuv_xy);
// fetching and weighting is interleaved for potential latency hiding
float4 tex000 = sourceTex.SampleLevel(sourceSampler, h0, 0.0);
float4 tex100 = sourceTex.SampleLevel(sourceSampler, float3(h1.x, h0.y, h0.z), 0.0);
tex000 = lerp(tex100, tex000, g0.x);
float4 tex010 = sourceTex.SampleLevel(sourceSampler, float3(h0.x, h1.y, h0.z), 0.0);
float4 tex110 = sourceTex.SampleLevel(sourceSampler, float3(h1.x, h1.y, h0.z), 0.0);
tex010 = lerp(tex110, tex010, g0.x);
tex000 = lerp(tex010, tex000, g0.y);
float4 tex001 = sourceTex.SampleLevel(sourceSampler, float3(h0.x, h0.y, h1.z), 0.0);
float4 tex101 = sourceTex.SampleLevel(sourceSampler, float3(h1.x, h0.y, h1.z), 0.0);
tex001 = lerp(tex101, tex001, g0.x);
float4 tex011 = sourceTex.SampleLevel(sourceSampler, float3(h0.x, h1.y, h1.z), 0.0);
float4 tex111 = sourceTex.SampleLevel(sourceSampler, h1, 0.0);
tex011 = lerp(tex111, tex011, g0.x);
tex001 = lerp(tex011, tex001, g0.y);
return lerp(tex001, tex000, g0.z);
}
## 2. Replace fog sampling calls
Inside CombineVolumetricFog() function within same file, replace all instances of:
VolumetricFogLookup = Texture3DSampleLevel(FogStruct.IntegratedLightScattering, SharedIntegratedLightScatteringSampler, VolumeUV, 0);
with:
VolumetricFogLookup = textureTriCubic(FogStruct.IntegratedLightScattering, SharedIntegratedLightScatteringSampler, VolumeUV);
## 3. Done.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment