Files
XCEngine/unity/NanoVDB/NanoVolumePass.hlsl

277 lines
6.1 KiB
HLSL

#ifndef NANO_VOLUME_PASS
#define NANO_VOLUME_PASS
#define MIN_TRANSMITTANCE 0.05
#define MIN_DENSITY 0.01
#define CLOUD_COLOR float3(1, 1, 1)
#define COLOR_NONE float4(0, 0, 0, 0)
#define COLOR_RED float4(1, 0, 0, 1)
#define COLOR_GREEN float4(0, 1, 0, 1)
#define COLOR_BLUE float4(0, 0, 1, 1)
#define PNANOVDB_HLSL
#include "PNanoVDB.hlsl"
uniform pnanovdb_buf_t buf : register(t1);
uniform float4 _LightDir; // directionalLight.transform.forward
uniform float3 _Light;
uniform float3 _Scattering;
uniform float _DensityScale;
uniform float _LightRayLength;
uniform float _LightAbsorbation;
uniform float _ClipPlaneMin;
uniform float _ClipPlaneMax;
uniform int _RayMarchSamples;
uniform int _LightSamples;
uniform int _VisualizeSteps;
struct Ray
{
float3 origin;
float3 direction;
float tmin;
float tmax;
};
struct NanoVolume
{
pnanovdb_grid_handle_t grid;
pnanovdb_grid_type_t grid_type;
pnanovdb_readaccessor_t acc;
};
void initVolume(inout NanoVolume volume)
{
pnanovdb_grid_handle_t grid = { {0} };
pnanovdb_grid_type_t grid_type = pnanovdb_buf_read_uint32(buf, PNANOVDB_GRID_OFF_GRID_TYPE);
pnanovdb_tree_handle_t tree = pnanovdb_grid_get_tree(buf, grid);
pnanovdb_root_handle_t root = pnanovdb_tree_get_root(buf, tree);
pnanovdb_readaccessor_t acc;
pnanovdb_readaccessor_init(acc, root);
volume.grid = grid;
volume.grid_type = grid_type;
volume.acc = acc;
}
float get_value_coord(inout pnanovdb_readaccessor_t acc, pnanovdb_vec3_t pos)
{
pnanovdb_coord_t ijk = pnanovdb_hdda_pos_to_ijk(pos);
pnanovdb_address_t address = pnanovdb_readaccessor_get_value_address(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, ijk);
return pnanovdb_read_float(buf, address);
}
uint get_dim_coord(inout pnanovdb_readaccessor_t acc, pnanovdb_vec3_t pos)
{
pnanovdb_coord_t ijk = pnanovdb_hdda_pos_to_ijk(pos);
return pnanovdb_readaccessor_get_dim(PNANOVDB_GRID_TYPE_FLOAT, buf, acc, ijk);
}
bool get_hdda_hit(inout pnanovdb_readaccessor_t acc, inout Ray ray, inout float valueAtHit)
{
float thit;
bool hit = pnanovdb_hdda_tree_marcher(
PNANOVDB_GRID_TYPE_FLOAT,
buf,
acc,
ray.origin, ray.tmin,
ray.direction, ray.tmax,
thit,
valueAtHit
);
ray.tmin = thit;
return hit;
}
void get_participating_media(out float sigmaS, out float sigmaE, float3 pos, inout pnanovdb_readaccessor_t acc)
{
sigmaS = get_value_coord(acc, pos) * _DensityScale;
sigmaE = max(0.000001, sigmaS);
}
// No inout to avoid breaking cache for main ray (Gaida 2022)
float volumetric_shadow(float3 pos, pnanovdb_readaccessor_t acc)
{
if (_LightSamples < 1) { return 0; }
float light_dir = -(_LightDir.xyz);
float shadow = 1;
float sigmaS = 0.0;
float sigmaE = 0.0;
int step = 0;
int steps = 10;
float step_size = 1;
while (step < steps)
{
float3 sample_pos = pos + step_size * light_dir;
get_participating_media(sigmaS, sigmaE, sample_pos, acc);
shadow *= exp(-sigmaE * step_size);
if (shadow < MIN_TRANSMITTANCE)
{
shadow = 0;
break;
}
step_size *= 2;
step++;
}
return shadow;
}
// Exp step with jitter
float volumetric_shadow_2(float3 pos, pnanovdb_readaccessor_t acc, float3 view_dir)
{
if (_LightSamples < 1) { return 0; }
float light_dir = -(_LightDir.xyz);
float shadow = 1.0;
float sigmaS = 0.0;
float sigmaE = 0.0;
float step_size = 1.0;
float jitter = 0;
int step = 0;
int steps = 10;
while (step < steps)
{
float3 sample_pos = pos + (jitter + step_size) * light_dir;
get_participating_media(sigmaS, sigmaE, sample_pos, acc);
sigmaE *= 0.3;
shadow *= exp(-sigmaE * step_size);
step++;
step_size *= (2 + random_float(sample_pos));
}
return shadow;
}
float phase_function()
{
return 1.0;///(4.0*3.14);
}
float4 raymarch_volume(Ray ray, inout NanoVolume volume, float step_size)
{
float transmittance = 1.0;
float sigmaS = 0.0;
float sigmaE = 0.0;
float acc_density = 0.0;
float3 direct_light = 0.0;
float3 ambient_light = 0.005;
float not_used;
bool hit = get_hdda_hit(volume.acc, ray, not_used);
if (!hit) { return COLOR_NONE; }
int step = 0;
float skip = 0;
while (step < _RayMarchSamples)
{
if (ray.tmin >= ray.tmax)
{
break;
}
// read density from ray position
float3 pos = ray.origin + ray.direction * ray.tmin;
get_participating_media(sigmaS, sigmaE, pos, volume.acc);
// Skip empty space.
uint dim = get_dim_coord(volume.acc, pos);
if (dim > 1)
{
step++;
float skip_step = 15;
ray.tmin += skip_step;
skip = skip_step;
continue;
}
if (sigmaS < MIN_DENSITY)
{
step++;
float skip_step = 5;
ray.tmin += skip_step;
skip = skip_step;
continue;
}
if (skip > 0) {
// backtrack a little bit
ray.tmin -= skip * 0.8;
pos = ray.origin + ray.direction * ray.tmin;
skip = 0;
}
acc_density += sigmaS;
// float3 S = sigmaS * phase_function() * volumetric_shadow_2(pos, volume.acc);
float3 S = sigmaS * phase_function() * volumetric_shadow_2(pos, volume.acc, ray.direction);
float3 Sint = (S - S * exp(-sigmaE * step_size)) / sigmaE;
direct_light += transmittance * Sint;
transmittance *= exp(-sigmaE * step_size);
if (acc_density > 1.0)
{
break;
}
// Early out if no more light is reaching this point
if (transmittance < MIN_TRANSMITTANCE)
{
transmittance = 0;
break;
}
step++;
ray.tmin += step_size;
}
// Low step count will be blue, high red.
if (_VisualizeSteps == 1)
{
float t = float(step) / float(_RayMarchSamples);
if (step <= 0)
{
return COLOR_NONE;
}
float3 final_color = lerp(COLOR_BLUE, COLOR_RED, t);
return float4(final_color, 1);
}
float3 final_color = (direct_light + ambient_light) * acc_density;
final_color = pow(final_color, 1.0 / 2.2);
return float4(final_color, acc_density);
}
float4 NanoVolumePass(float3 origin, float3 direction)
{
NanoVolume volume; initVolume(volume);
Ray ray;
ray.origin = origin;
ray.direction = direction;
ray.tmin = _ClipPlaneMin;
ray.tmax = _ClipPlaneMax;
float step_size = 0.57;
float4 final_color = raymarch_volume(ray, volume, step_size);
return final_color;
}
#endif // NANO_VOLUME_PASS