Volume Rendering

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--;
            }
        }
    }