This is for sure took a long time to wrap my head around. Living on the country side there are countless occations, especially in autem, where I rode my bicyle through the dense mist. So dense that drops continously were running down my glasses. Grey color everywhere.
float scatter_at_distance = -(1.0 / (materials.m[medium_mat_id].medium_density)) * log(rnd(prd.seed)); // log between 0..1 returns negativ number. thats why formular negates at the end
if (scatter_at_distance < gl_HitTEXT - 0.01)
{
for (int it = 0; it < 2; it++)
{
vec3 scatter_point = prd.rayOrigin + prd.rayDirection * scatter_at_distance;
prd.rayOrigin = scatter_point;
prd.path_length += scatter_at_distance;
// DO !!!!! NOT !!!! HARDCODE THIS DIRECT LIGHT ACCESSES THIS VAR TOO
//Do FOR LOOP here and advance the scatterpoint and use float for raydistance advance as long in for as substeps exceed or we hit medium.
//Maybe calc here again a scatter at distance second one for each step we take and drop out if higher than distance from scatterpoint to end.
prd.hit_medium_density = clamp(fognoise(scatter_point * 8.0) * 3.0f, 0.0f, 1.0f);
if (prd.hit_medium_density > rnd(prd.seed))
{
// Less dense volume particles will not be affected by light as much as dense.
// This value here is used in lighting calculations for NEE. Where strength and falloff
// need to be computed manualy.
// Scattering the ray in the medium in any random direction resambles total lambertion reflection.
// It is responsible for having shading of light sources and contact shadows in narrow areas.
vec3 refract_tangent;
vec3 refract_bitangent;
createCoordinateSystem(prd.rayDirection, refract_tangent, refract_bitangent, prd.seed);
prd.rayDirection = samplingHemisphere(prd.seed, refract_tangent, refract_bitangent, prd.rayDirection, materials.m[medium_mat_id].medium_scatter * prd.hit_medium_density);
prd.hitNormal = randomVecOnUnitSphere(prd.seed);
if (materials.m[medium_mat_id].medium_lambertian_scatter > rnd(prd.seed))
{
prd.rayDirection = randomVecOnUnitSphere(prd.seed);
prd.bdrf_center_dir_and_bound = vec4(prd.rayDirection, 1.0);
}
else
{
prd.bdrf_center_dir_and_bound = vec4(prd.rayDirection, materials.m[medium_mat_id].medium_scatter * prd.hit_medium_density);
// Backscattering if a random number is below the anisotropy level. 0.5 is balanced. So its isotropic.
// same chance for light to back or front scatter. If the value gets smaller chance to back scatter gets smaller.
if (rnd(prd.seed) < materials.m[medium_mat_id].medium_scatter_anisotropy)
{
prd.rayDirection = -prd.rayDirection;
}
// Only diverting the ray in a range from its given path (forward or backward) results in absorbtion and diffusion of the medium.
}
//prd.path_roughness += non_uniform_density;
hit_surface_brdf = materials.m[medium_mat_id].medium_color;
hit_surface_normal = prd.hitNormal;
hit_surface_depth = length(scatter_point - prd.path_origin.xyz);
hit_surface_position = scatter_point.xyz;
hit_surface_emission = vec3(0);
hit_write_data = true;
// TODO: Do we need to adjust here for solid angle?
prd.pdf_surface = 1.0 / (2.0 * EN_PI);
break;
}
else
{
float tt = (prd.depth / prd.max_depth);
scatter_at_distance = -(1.0 / (materials.m[medium_mat_id].medium_density)) * log(rnd(prd.seed)); // log between 0..1 returns negativ number. thats why formular negates at the end
// BIG TODO: CHECK SOMHOW A PATH LENGTH AND LET GO DROP OUT OF VOLUME CALCS IF PATH LENGTH IS APROACHING THAT SAID LENGTH.
// MAYBE PER MATERIAL WHERE MATERIAL HAS MAX RANDOM SCATTERING PATH LENGTH :D
// Density propability was so low we see this hit point as non existing (virtual volume particle)
// the result of this is just extending the ray further.
// No density results in no absorbtion so we transport the full ray energy.
// NEE Weight for this virtual particle needs to be zero to not introduce artifacts. a particle that
// was just used for ray extention is not to be concidered in any lighting calculation.
prd.bdrf_center_dir_and_bound = vec4(prd.rayDirection, -1.0);
// If a virtual particle with no density is hit we roll back the current trace depth.
// this ensures we will exit the medium bounds definded by a cage mesh eventually.
// BIG TODO. THIS IS NICE FOR REMOVING BLACK BUT IF ACTIVE ON LONG FOG MAKES BLACK WORTH
// if (tt < rnd(prd.seed))
// {
// prd.depth = max(1, prd.depth-1);
// }
prd.hit_medium_density = 0.0f;
prd.hitNormal = prd.rayDirection;
hit_surface_brdf = vec3(1);
prd.pdf_surface = 1.0;
prd.light_pdf = 1.0;
if (scatter_at_distance > length(scatter_point - world_position.xyz) - 0.01)
{
break;
}
it--;
}
}
}