Files
2024-11-20 15:21:28 +01:00

329 lines
14 KiB
HLSL

#ifndef VOLUMETRIC_INCLUDED
#define VOLUMETRIC_INCLUDED
#include "../FluidUtilsShaderGraph.hlsl"
static const float VOL_EPSILON = 0.00000001;
void IGN_float(in int pixelX, in int pixelY, in int frame, out float noise)
{
frame = fmod(frame,64); // need to periodically reset frame to avoid numerical issues
float x = float(pixelX) + 5.588238f * float(frame);
float y = float(pixelY) + 5.588238f * float(frame);
noise = fmod(52.9829189f * fmod(0.06711056f*x + 0.00583715f*y, 1.0), 1.0);
}
float3 AdvectUV(in float2 uv, in float2 velocity, in float time, in float offset, in float phase)
{
float3 flowUV;
float progress = frac(time + phase * 0.5);
flowUV.xy = uv - velocity * (progress + offset);
flowUV.xy += (time - progress) * 0.25;
flowUV.z = 1 - abs(1 - 2 * progress);
return flowUV;
}
void RayBoxIntersection_float(in float3 rayOrigin, in float3 rayDirection, in float4 lightVector,
in float3 boxSize,
in float3 scenePosition,
in int numSteps,
out float3 rayIntersection, out float3 intersection, out float3 direction, out float4 light)
{
// ensure a minimum size for the box:
boxSize = max(boxSize, VOL_EPSILON);
// convert all input from object to volume (normalized) space.
rayOrigin /= boxSize;
rayDirection /= boxSize;
scenePosition /= boxSize;
lightVector.xyz /= boxSize;
direction = rayDirection / max(length(rayDirection), VOL_EPSILON);
light = lightVector;
// calculate intersection of the ray with the volume box:
float3 invraydir = 1 / direction;
float3 firstintersections = (-0.5 - rayOrigin) * invraydir;
float3 secondintersections = (0.5 - rayOrigin) * invraydir;
float3 closest = min(firstintersections, secondintersections);
float3 furthest = max(firstintersections, secondintersections);
// calculate coords in the ray:
float t0 = max(closest.x, max(closest.y, closest.z));
float t1 = min(furthest.x, min(furthest.y, furthest.z));
// clamp end of the ray to the scene depth:
t1 = min(t1, length(scenePosition - rayOrigin));
// align sample to camera plane:
float planeoffset = 1 - frac( ( t0 - length(rayOrigin) ) * numSteps );
float t0aligned = t0 + (planeoffset / numSteps);
rayIntersection = float3(t0, t0aligned, t1);
intersection = rayOrigin;
}
float4 SampleDensity(in Texture2D tex, in SamplerState sm,
in float3 sampleCoord,
in float volumeOffset,
in float volumeExtrusion,
in float volumeFalloff)
{
float4 sample = SAMPLE_TEXTURE2D_LOD(tex,sm,sampleCoord.xy,0);
float x = abs(sampleCoord.z - volumeOffset);
float thickness = volumeExtrusion * sample.a;
float falloff = 1 - smoothstep(thickness - volumeFalloff,thickness,x);
return sample * falloff;
}
float SampleNoise(in Texture2D vel, in Texture3D noise, in SamplerState sm,
in float3 sampleCoord,
in float3 volumeSize,
in float noiseScale,
in float noisePow,
in float2 advection)
{
float2 velo = SAMPLE_TEXTURE2D_LOD(vel,sm,sampleCoord.xy,0).xy * advection.x;
float time = _Time.y * advection.y;
float3 noiseUV = sampleCoord * volumeSize / noiseScale;
float3 flowuv1 = AdvectUV(noiseUV.xy, velo, time, -0.5, 0);
float3 flowuv2 = AdvectUV(noiseUV.xy, velo, time, -0.5, 1);
float n = SAMPLE_TEXTURE3D_LOD(noise,sm,float3(flowuv1.xy,noiseUV.z),0).r * flowuv1.z +
SAMPLE_TEXTURE3D_LOD(noise,sm,float3(flowuv2.xy,noiseUV.z),0).r * flowuv2.z;
return pow(abs(n),noisePow);
}
float4 SampleVolume(in Texture2D tex, in Texture2D vel, in Texture3D noise, in SamplerState sm, in SamplerState noiseSampler,
in float3 sampleCoord,
in float volumeOffset,
in float volumeExtrusion,
in float volumeFalloff,
in float3 volumeSize,
in float noiseScale,
in float noisePow,
in float2 advection)
{
float2 uv;
TileToUV_float(sampleCoord.xy, _TileIndex, uv);
float4 density = SampleDensity(tex, sm, float3(uv,sampleCoord.z), volumeOffset, volumeExtrusion, volumeFalloff);
#if _NOISE_ON
density *= SampleNoise(vel, noise, noiseSampler, sampleCoord, volumeSize, noiseScale, noisePow, advection);
#endif
return density;
}
void SingleScattering(in Texture2D tex, in Texture2D vel, in Texture3D noise, in SamplerState sm, in SamplerState noiseSampler,
in float3 volumeSize, in float volumeOffset, in float volumeExtrusion, in float volumeFalloff,
in float3 rayOrigin, in float rayOffset, in float stepSize,
in float4 lightVector, in float4 lightColor, in float lightSteps, in float lightStepSize,
in float densityScale,
in float3 ambientDirection,
in float3 ambientColor,
in float ambientDensity,
in float3 shadowExtinction,
in float shadowDensity,
in float shadowThreshold,
in float noiseScale,
in float noisePow,
in float2 advection,
inout float3 scattering, inout float transmittance)
{
float3 sampleCoords = rayOrigin + 0.5;
float4 sample = SampleVolume(tex, vel, noise, sm, noiseSampler, sampleCoords, volumeOffset, volumeExtrusion, volumeFalloff, volumeSize, noiseScale, noisePow, advection) * densityScale;
float extinction = sample.a;
if (extinction > 0.001)
{
float currentTransmittance = exp(-extinction * stepSize);
#if defined(_LIGHTSOURCE_DIRECTIONAL) || defined(_LIGHTSOURCE_POINT)
// calculate light direction vector:
float3 lightDirection;
#if defined(_LIGHTSOURCE_POINT)
lightDirection = (lightVector.xyz - rayOrigin) * volumeSize;
#else
lightDirection = -lightVector.xyz;
#endif
float distanceSqr = max(dot(lightDirection, lightDirection),0.0001);
lightDirection /= sqrt(distanceSqr);
// calculate attenuation factor:
float attenuation = 1;
#if defined (_LIGHTSOURCE_POINT)
half factor = distanceSqr * lightVector.w;
half rangeAtten = saturate(1 - factor * factor);
attenuation = (rangeAtten * rangeAtten) / distanceSqr;
#endif
lightDirection /= volumeSize;
lightDirection *= lightStepSize;
// step in the direction of the light source:
float shadowAccum = 0;
float3 lightRayOrigin = rayOrigin + lightDirection * rayOffset;
for (int j = 0; j < lightSteps; ++j)
{
lightRayOrigin += lightDirection;
float3 lightSampleCoords = lightRayOrigin + 0.5;
float lightSample = SampleVolume(tex, vel, noise, sm, noiseSampler, lightSampleCoords, volumeOffset, volumeExtrusion, volumeFalloff, volumeSize, noiseScale, noisePow, advection).a;
shadowAccum += lightSample;
if(shadowAccum > shadowThreshold)
break;
}
float3 shadow = exp(-shadowAccum * shadowDensity / shadowExtinction) * attenuation;
float3 luminance = shadow * lightColor;
//ambient light
float amb = 0;
float ambientStepSize = 0.01;
sampleCoords += ambientDirection * rayOffset * ambientStepSize;
amb += SampleVolume(tex, vel, noise, sm, noiseSampler, saturate(sampleCoords + ambientDirection * ambientStepSize), volumeOffset, volumeExtrusion, volumeFalloff, volumeSize, noiseScale, noisePow, advection).a;
amb += SampleVolume(tex, vel, noise, sm, noiseSampler, saturate(sampleCoords + ambientDirection * ambientStepSize * 2), volumeOffset, volumeExtrusion, volumeFalloff, volumeSize, noiseScale, noisePow, advection).a;
amb += SampleVolume(tex, vel, noise, sm, noiseSampler, saturate(sampleCoords + ambientDirection * ambientStepSize * 3), volumeOffset, volumeExtrusion, volumeFalloff, volumeSize, noiseScale, noisePow, advection).a;
luminance += exp(-amb * ambientDensity) * ambientColor;
#else
float3 luminance = float3(1,1,1);
#endif
// analytical integration:
float3 intScattering = (luminance - luminance * currentTransmittance) / max(extinction, 0.00001);
scattering += transmittance * sample.rgb * intScattering;
transmittance *= currentTransmittance;
}
}
void Raymarch_float(in Texture2D tex, in Texture2D vel, in Texture3D noise, in SamplerState sm, in SamplerState noiseSampler,
in float3 volumeSize, in float volumeOffset, in float volumeExtrusion, in float volumeFalloff,
in float3 rayIntersection, in float3 rayOrigin, in float3 rayDirection,
in float4 lightVector, in float4 lightColor, in float rayOffset,
in float numSteps,
in float lightSteps,
in float densityScale,
in float densityThreshold,
in float3 ambientDirection,
in float3 ambientColor,
in float ambientDensity,
in float3 shadowExtinction,
in float shadowDensity,
in float shadowThreshold,
in float noiseScale,
in float noisePow,
in float advectSpeed,
in float advectTime,
out float4 color)
{
float transmittance = 1;
float3 scattering = float3(0,0,0);
float2 advection = float2(advectSpeed, advectTime);
// ensure a minimum value for extinction:
shadowExtinction = max(shadowExtinction, VOL_EPSILON);
// ensure a minimum size for the volume:
volumeSize = max(volumeSize, VOL_EPSILON);
// when inside the volume, the plane-aligned intersection
// might be behind the cam so we clamp it:
float clampedT = max(0,rayIntersection.y);
// align ray with camera facing plane:
rayOrigin += rayDirection * clampedT;
// compute distance traversed by the ray inside the box:
float volumeThickness = max(0, rayIntersection.z - clampedT);
// calculate step size:
lightSteps = clamp(lightSteps,0,64);
float stepSize = 1 / numSteps;
float lightStepSize = 1 / lightSteps;
// scale rays by step size:
rayDirection *= stepSize;
shadowDensity *= lightStepSize;
// number of steps:
numSteps = clamp(floor(volumeThickness / stepSize),0,64);
// calculate shadow threshold:
float shadowThr = -log(shadowThreshold) / shadowDensity;
// jitter ray origin:
rayOrigin += rayDirection * rayOffset;
// first sample:
if (rayIntersection.y > 0)
{
float firstStep = min(rayIntersection.y,rayIntersection.z) - rayIntersection.x;
SingleScattering(tex, vel, noise, sm, noiseSampler,
volumeSize, volumeOffset, volumeExtrusion, volumeFalloff,
rayOrigin, rayOffset, firstStep,
lightVector, lightColor, lightSteps, lightStepSize,
densityScale,
ambientDirection,
ambientColor,
ambientDensity,
shadowExtinction,
shadowDensity,
shadowThr, noiseScale, noisePow, advection,
scattering, transmittance);
}
// main raymarch loop:
for (int i = 0; i < numSteps; ++i)
{
rayOrigin += rayDirection;
SingleScattering(tex, vel, noise, sm, noiseSampler,
volumeSize, volumeOffset, volumeExtrusion, volumeFalloff,
rayOrigin, rayOffset, stepSize,
lightVector, lightColor, lightSteps, lightStepSize,
densityScale,
ambientDirection,
ambientColor,
ambientDensity,
shadowExtinction,
shadowDensity,
shadowThr, noiseScale, noisePow, advection,
scattering, transmittance);
if (transmittance < densityThreshold)
break;
}
// last sample:
if (transmittance >= densityThreshold)
{
float lastStep = volumeThickness - numSteps * stepSize;
rayOrigin += rayDirection / stepSize * lastStep;
SingleScattering(tex, vel, noise, sm, noiseSampler,
volumeSize, volumeOffset, volumeExtrusion, volumeFalloff,
rayOrigin, rayOffset, lastStep,
lightVector, lightColor, lightSteps, lightStepSize,
densityScale,
ambientDirection,
ambientColor,
ambientDensity,
shadowExtinction,
shadowDensity,
shadowThr, noiseScale, noisePow, advection,
scattering, transmittance);
}
color = saturate(float4(scattering,transmittance));
}
#endif