<p>I swear that I've been struggling with this code for months, and after a hiatus, I have now mostly solved the problem. I just need to add in reflections and refractions.</p>
<p>Instead of getting line of sight between the eye and some points, I am now getting the line of sight with the first hitpos generated by tracing the ray from the eye into the scene. I am also not overwriting the directions taken by the rays. Thank you so much Enigmatisms!</p>
<p><a href="https://i.sstatic.net/7Dc9K.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/7Dc9K.png" alt="enter image description here" /></a></p>
<pre><code>float trace_path_forward2(const vec3 eye, const vec3 direction, const float hue, const float eta)
{
    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    const vec3 light_o = get_random_light_pos(50, eye, direction, hue, eta);
    const vec3 light_d = RandomUnitVector(prng_state);

    const float energy = 1.0;

    int step_count = 0;
    vec3 step_locations[max_bounces + 3];
    vec3 step_directions[max_bounces + 3];

    traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, eye, 0.001, direction, 10000.0, 0);

    if(rayPayload.dist == -1)
        return 0.0;

    const vec3 first_hit_pos = eye + direction * rayPayload.dist;

    step_locations[step_count] = light_o;
    step_directions[step_count] = light_d;
    step_count++;

    if(false == is_clear_line_of_sight(first_hit_pos, light_o))
    {   
        vec3 o = light_o;
        vec3 d = light_d;

        for(int i = 0; i &lt; max_bounces; i++)
        {
            traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, 0.001, d, 10000.0, 0);

            if(rayPayload.dist == -1)
                return 0.0;

            const vec3 hitPos = o + d * rayPayload.dist;

            o = hitPos + rayPayload.normal * 0.01;
            d = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);
            
            step_locations[step_count] = o;
            step_directions[step_count] = d;
            step_count++;

            if(true == is_clear_line_of_sight(first_hit_pos, step_locations[step_count - 1]))
                break;
        }
    }




    step_locations[step_count] = first_hit_pos;
    step_directions[step_count] = light_d;
    step_count++;

    step_locations[step_count] = eye;
    step_directions[step_count] = direction;
    step_count++;






    // Reverse the path
    uint start = 0;
    uint end = step_count - 1;

    while(start &lt; end) 
    { 
        vec3 temp = step_locations[start];  
        step_locations[start] = step_locations[end]; 
        step_locations[end] = temp; 

        temp = step_directions[start];
        step_directions[start] = step_directions[end]; 
        step_directions[end] = temp; 

        start++;
        end--; 
    }


    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    for(int i = 0; i &lt; step_count - 1; i++)
    {
        vec3 step_o = step_locations[i];
        vec3 step_d = step_directions[i];

        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, step_o, 0.001, step_d, 10000.0, 0);

        local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(i == step_count - 2 || 
        (rayPayload.color.r &gt; 1.0 || 
        rayPayload.color.g &gt; 1.0 || 
        rayPayload.color.b &gt; 1.0))
        {
            ret_colour += local_colour;
            break;
        }
    }

    return ret_colour / total;
}
</code></pre>

CC BY-SA 4.0
2024-03-08T02:14:33.843Z
You see, the global illumination is not correct. Maybe it's because that you break too early from the ray bouncing (first for loop). Can you explain what does the function `is_clear_line_of_sight` do in your code, in detail? I am confused by the logic here.
Enigmatisms
CC BY-SA 4.0
2024-03-08T16:34:21.327Z
You're right. I was mislead into thinking that it's working in general. I appreciate your efforts. The function traces a ray from point a to point b, to determine if there is anything occluding b from a.
shawn_halayka
CC BY-SA 4.0
2024-03-08T16:35:04.327Z
... but it goes to show that splatting is absolutely unnecessary! That's neat.
shawn_halayka
CC BY-SA 4.0
2024-03-09T02:34:56.657Z
I don't think splatting is unnecessary. It bares the same idea of next event estimation: if the light source is small, we make a shadow connection to a point on the light source. Now in this reversed settings, camera is indeed small and for non-lens-based cameras, you will have zero probability for a ray to hit the camera. So for each vertex on the traced path, a shadow connection to the camera should be made and evaluate this direct component (how much radiance can the camera receive through this shadow connection, if the connection is not occluded).
Enigmatisms
CC BY-SA 4.0
2024-03-07T19:23:42.343Z
2024-03-07T19:23:42.343Z
shawn_halayka
<p>I have a forward path tracer. Essentially, it looks flat:</p>
<p><a href="https://i.sstatic.net/gZNWq.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/gZNWq.png" alt="enter image description here" /></a></p>
<p>This is the backward path tracer in comparison:</p>
<p><a href="https://i.sstatic.net/2ggqR.jpg" rel="nofollow noreferrer"><img src="https://i.sstatic.net/2ggqR.jpg" alt="enter image description here" /></a></p>
<p>My question is: How do I get pseudorandom lighting like with the backward path tracer, when the eye ray always hits a light in forward path tracing?</p>
<p>Here is my code for the forward path tracer:</p>
<pre><code>float trace_path_forward2(const vec3 eye, const vec3 direction, const float hue, const float eta)
{
    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    const vec3 light_o = get_random_light_pos(50, eye, direction, hue, eta);//getRandomPointOnTriangle(A.pos, B.pos, C.pos);
    const vec3 light_d = RandomUnitVector(prng_state);//vec3(0);//cosWeightedRandomHemisphereDirection(get_normal_by_light_index(tri_index), prng_state);


    const float energy = 1.0;

    int step_count = 0;
    vec3 step_locations[max_bounces + 2];
    vec3 step_directions[max_bounces + 2];

    step_locations[step_count] = light_o;
    step_directions[step_count] = light_d;
    step_count++;

    if(false == is_clear_line_of_sight(eye, light_o))
    {   
        vec3 o = light_o;
        vec3 d = light_d;

        for(int i = 0; i &lt; max_bounces; i++)
        {
            traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, 0.001, d, 10000.0, 0);

            if(rayPayload.dist == -1)
                return 0.0;

            const vec3 hitPos = o + d * rayPayload.dist;

            o = hitPos + rayPayload.normal * 0.01;
            d = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);
            
            step_locations[step_count] = o;
            step_directions[step_count] = d;
            step_count++;

            if(true == is_clear_line_of_sight(eye, step_locations[step_count - 1]))
                break;
        }
    }

    step_locations[step_count] = eye;
    step_directions[step_count] = direction;
    step_count++;

    

    // Reverse the path
    uint start = 0;
    uint end = step_count - 1;

    while(start &lt; end) 
    { 
        vec3 temp = step_locations[start];  
        step_locations[start] = step_locations[end]; 
        step_locations[end] = temp; 

        temp = step_directions[start];  
        step_directions[start] = step_directions[end]; 
        step_directions[end] = temp; 

        start++;
        end--; 
    }

    
    for(int i = 1; i &lt; step_count - 1; i++)
    {
        step_directions[i] = (step_locations[i + 1] - step_locations[i]);
    }
    



    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    for(int i = 0; i &lt; step_count - 1; i++)
    {
        vec3 step_o = step_locations[i];
        vec3 step_d = step_directions[i];



        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, step_o, 0.001, step_d, 10000.0, 0);

        local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(i == step_count - 2 || (rayPayload.color.r &gt; 1.0 || rayPayload.color.g &gt; 1.0 || rayPayload.color.b &gt; 1.0))
        {
            ret_colour += local_colour;
            break;
        }
    }

    return ret_colour / total;
}
</code></pre>
<p>The code for the backward path tracer is:</p>
<pre><code>float trace_path_backward(const int steps, const vec3 origin, const vec3 direction, const float hue, const float eta)
{
    vec3 o = origin;
    vec3 d = direction;

    const float energy = 1;
    const float caustic_energy = 1;//energy;

    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    bool doing_refraction_caustic = false;

    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    for(int i = 0; i &lt; steps; i++)
    {
        const float tmin = 0.001;
        const float tmax = 10000.0;

        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, tmin, d, tmax, 0);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(doing_refraction_caustic)
        {
            local_colour += caustic_energy*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            doing_refraction_caustic = false;
        }
        else
        {
            local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
        }

        // If hit the sky
        if(rayPayload.dist == -1.0)
        {
            ret_colour += local_colour;
            break;
        }

        // If this is simply the final step
        // then don't throw away perfectly 
        // good data
        if(i == steps - 1)
        {
            ret_colour += local_colour;
            break;
        }

        // Hit a light
        if(rayPayload.color.r &gt; 1
        || rayPayload.color.g &gt; 1
        || rayPayload.color.b &gt; 1)
        {
            ret_colour += local_colour;
            break;
        }

        vec3 hitPos = o + d * rayPayload.dist;

        if(stepAndOutputRNGFloat(prng_state) &lt;= rayPayload.opacity)
        {
            RayPayload r = rayPayload;
            
            if(rayPayload.subsurface &gt; 0)
            {
                vec3 o_subsurface = hitPos - rayPayload.normal * 0.001 + RandomUnitVector(prng_state)*0.001;
                vec3 d_subsurface = RandomUnitVector(prng_state);

                traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o_subsurface, tmin, d_subsurface, tmax, 0);

                total += mask.r;
                total += mask.g;
                total += mask.b;

                local_colour += rayPayload.subsurface*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            }

            if(rayPayload.reflector &gt; 0)
            {
                vec3 o_reflect = hitPos + rayPayload.normal * 0.01;
                vec3 d_reflect = reflect(d, rayPayload.normal);

                traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o_reflect, tmin, d_reflect, tmax, 0);

                total += mask.r;
                total += mask.g;
                total += mask.b;

                local_colour += rayPayload.reflector*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            }

            rayPayload = r;

            vec3 o_scatter = hitPos + rayPayload.normal * 0.01;
            vec3 d_scatter = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);

            o = o_scatter;
            d = d_scatter;
        }
        else
        {
            doing_refraction_caustic = true;

            vec3 o_transparent = vec3(0.0);
            vec3 d_transparent = vec3(0.0);

            // Incoming
            if(dot(d, rayPayload.normal) &lt;= 0.0)
            {
                o_transparent = hitPos.xyz - rayPayload.normal * 0.01f;
                d_transparent = refract(d, rayPayload.normal, eta);
            }
            else // Outgoing
            {
                vec3 temp_dir = refract(d, -rayPayload.normal, 1.0 / eta);

                if(temp_dir != vec3(0.0))
                {
                    o_transparent = hitPos.xyz + rayPayload.normal * 0.01f;
                    d_transparent = mix(temp_dir, RandomUnitVector(prng_state), rayPayload.subsurface);//   temp_dir;
                }
                else
                {
                    // Total internal reflection
                    o_transparent = hitPos.xyz - rayPayload.normal * 0.01f;
                    d_transparent = mix(reflect(d, -rayPayload.normal), RandomUnitVector(prng_state), rayPayload.subsurface);//reflect(d, -rayPayload.normal);
                }
            }

            o = o_transparent;
            d = normalize(d_transparent);
        }
    }

    return ret_colour / total;
}
</code></pre>
<p>Any comments or questions are appreciated. I think that I'm missing something that might be obvious to another person. If there's anything that I can add to this question, please let me know.</p>

CC BY-SA 4.0
2024-03-07T17:22:41.007Z
I don't understand why in your forward path tracer that: you will not simulate light bouncing when your emitter is in the direct line of sight `if(false == is_clear_line_of_sight(eye, light_o)) ...`, and even if the light pos is not in the direct line of sight, you will stop the bounce once that position after bouncing is in the direct line of sight. Normally, a forward path tracer is implemented almost in the same way as the backward one: you simulate light bounces and splat the result (light rasterize them) on to the screen.
Enigmatisms
CC BY-SA 4.0
2024-03-07T18:49:53.440Z
Thank you for your input. This is what I meant. So, basically, I need to do ray-image plane intersection?
shawn_halayka
<raytracing><pathtracing><c++><vulkan>
Forward path tracing versus backward path tracing. How may I achieve realistic shading in forward path tracing?
shawn_halayka
# Forward path tracing versus backward path tracing. How may I achieve realistic shading in forward path tracing?

(Asked by: username_0 on 2024-03-07)

I have a forward path tracer. Essentially, it looks flat:

[![enter image description here](https://i.sstatic.net/gZNWq.png)](https://i.sstatic.net/gZNWq.png)

This is the backward path tracer in comparison:

[![enter image description here](https://i.sstatic.net/2ggqR.jpg)](https://i.sstatic.net/2ggqR.jpg)

My question is: How do I get pseudorandom lighting like with the backward path tracer, when the eye ray always hits a light in forward path tracing?

Here is my code for the forward path tracer:

```
float trace_path_forward2(const vec3 eye, const vec3 direction, const float hue, const float eta)
{
    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    const vec3 light_o = get_random_light_pos(50, eye, direction, hue, eta);//getRandomPointOnTriangle(A.pos, B.pos, C.pos);
    const vec3 light_d = RandomUnitVector(prng_state);//vec3(0);//cosWeightedRandomHemisphereDirection(get_normal_by_light_index(tri_index), prng_state);

    const float energy = 1.0;

    int step_count = 0;
    vec3 step_locations[max_bounces + 2];
    vec3 step_directions[max_bounces + 2];

    step_locations[step_count] = light_o;
    step_directions[step_count] = light_d;
    step_count++;

    if(false == is_clear_line_of_sight(eye, light_o))
    {   
        vec3 o = light_o;
        vec3 d = light_d;

        for(int i = 0; i < max_bounces; i++)
        {
            traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, 0.001, d, 10000.0, 0);

            if(rayPayload.dist == -1)
                return 0.0;

            const vec3 hitPos = o + d * rayPayload.dist;

            o = hitPos + rayPayload.normal * 0.01;
            d = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);
            
            step_locations[step_count] = o;
            step_directions[step_count] = d;
            step_count++;

            if(true == is_clear_line_of_sight(eye, step_locations[step_count - 1]))
                break;
        }
    }

    step_locations[step_count] = eye;
    step_directions[step_count] = direction;
    step_count++;

    

    // Reverse the path
    uint start = 0;
    uint end = step_count - 1;

    while(start < end) 
    { 
        vec3 temp = step_locations[start];  
        step_locations[start] = step_locations[end]; 
        step_locations[end] = temp; 

        temp = step_directions[start];  
        step_directions[start] = step_directions[end]; 
        step_directions[end] = temp; 

        start++;
        end--; 
    }

    
    for(int i = 1; i < step_count - 1; i++)
    {
        step_directions[i] = (step_locations[i + 1] - step_locations[i]);
    }
    

    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    for(int i = 0; i < step_count - 1; i++)
    {
        vec3 step_o = step_locations[i];
        vec3 step_d = step_directions[i];

        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, step_o, 0.001, step_d, 10000.0, 0);

        local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(i == step_count - 2 || (rayPayload.color.r > 1.0 || rayPayload.color.g > 1.0 || rayPayload.color.b > 1.0))
        {
            ret_colour += local_colour;
            break;
        }
    }

    return ret_colour / total;
}

```

The code for the backward path tracer is:

```
float trace_path_backward(const int steps, const vec3 origin, const vec3 direction, const float hue, const float eta)
{
    vec3 o = origin;
    vec3 d = direction;

    const float energy = 1;
    const float caustic_energy = 1;//energy;

    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    bool doing_refraction_caustic = false;

    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    for(int i = 0; i < steps; i++)
    {
        const float tmin = 0.001;
        const float tmax = 10000.0;

        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, tmin, d, tmax, 0);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(doing_refraction_caustic)
        {
            local_colour += caustic_energy*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            doing_refraction_caustic = false;
        }
        else
        {
            local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
        }

        // If hit the sky
        if(rayPayload.dist == -1.0)
        {
            ret_colour += local_colour;
            break;
        }

        // If this is simply the final step
        // then don't throw away perfectly 
        // good data
        if(i == steps - 1)
        {
            ret_colour += local_colour;
            break;
        }

        // Hit a light
        if(rayPayload.color.r > 1
        || rayPayload.color.g > 1
        || rayPayload.color.b > 1)
        {
            ret_colour += local_colour;
            break;
        }

        vec3 hitPos = o + d * rayPayload.dist;

        if(stepAndOutputRNGFloat(prng_state) <= rayPayload.opacity)
        {
            RayPayload r = rayPayload;
            
            if(rayPayload.subsurface > 0)
            {
                vec3 o_subsurface = hitPos - rayPayload.normal * 0.001 + RandomUnitVector(prng_state)*0.001;
                vec3 d_subsurface = RandomUnitVector(prng_state);

                traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o_subsurface, tmin, d_subsurface, tmax, 0);

                total += mask.r;
                total += mask.g;
                total += mask.b;

                local_colour += rayPayload.subsurface*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            }

            if(rayPayload.reflector > 0)
            {
                vec3 o_reflect = hitPos + rayPayload.normal * 0.01;
                vec3 d_reflect = reflect(d, rayPayload.normal);

                traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o_reflect, tmin, d_reflect, tmax, 0);

                total += mask.r;
                total += mask.g;
                total += mask.b;

                local_colour += rayPayload.reflector*(rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);
            }

            rayPayload = r;

            vec3 o_scatter = hitPos + rayPayload.normal * 0.01;
            vec3 d_scatter = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);

            o = o_scatter;
            d = d_scatter;
        }
        else
        {
            doing_refraction_caustic = true;

            vec3 o_transparent = vec3(0.0);
            vec3 d_transparent = vec3(0.0);

            // Incoming
            if(dot(d, rayPayload.normal) <= 0.0)
            {
                o_transparent = hitPos.xyz - rayPayload.normal * 0.01f;
                d_transparent = refract(d, rayPayload.normal, eta);
            }
            else // Outgoing
            {
                vec3 temp_dir = refract(d, -rayPayload.normal, 1.0 / eta);

                if(temp_dir != vec3(0.0))
                {
                    o_transparent = hitPos.xyz + rayPayload.normal * 0.01f;
                    d_transparent = mix(temp_dir, RandomUnitVector(prng_state), rayPayload.subsurface);//   temp_dir;
                }
                else
                {
                    // Total internal reflection
                    o_transparent = hitPos.xyz - rayPayload.normal * 0.01f;
                    d_transparent = mix(reflect(d, -rayPayload.normal), RandomUnitVector(prng_state), rayPayload.subsurface);//reflect(d, -rayPayload.normal);
                }
            }

            o = o_transparent;
            d = normalize(d_transparent);
        }
    }

    return ret_colour / total;
}

```

Any comments or questions are appreciated. I think that I'm missing something that might be obvious to another person. If there's anything that I can add to this question, please let me know.

**Tags:** raytracing, pathtracing, c++, vulkan

> **Comments:**
> **username_1**: I don't understand why in your forward path tracer that: you will not simulate light bouncing when your emitter is in the direct line of sight `if(false == is_clear_line_of_sight(eye, light_o)) ...`, and even if the light pos is not in the direct line of sight, you will stop the bounce once that position after bouncing is in the direct line of sight. Normally, a forward path tracer is implemented almost in the same way as the backward one: you simulate light bounces and splat the result (light rasterize them) on to the screen.  
> **username_0**: Thank you for your input. This is what I meant. So, basically, I need to do ray-image plane intersection?  

---

## 1 answers

### Answer by username_0 on 2024-03-07. Score: 0

I swear that I've been struggling with this code for months, and after a hiatus, I have now mostly solved the problem. I just need to add in reflections and refractions.

Instead of getting line of sight between the eye and some points, I am now getting the line of sight with the first hitpos generated by tracing the ray from the eye into the scene. I am also not overwriting the directions taken by the rays. Thank you so much username_1!

[![enter image description here](https://i.sstatic.net/7Dc9K.png)](https://i.sstatic.net/7Dc9K.png)

```
float trace_path_forward2(const vec3 eye, const vec3 direction, const float hue, const float eta)
{
    const vec3 mask = hsv2rgb(vec3(hue, 1.0, 1.0));

    const vec3 light_o = get_random_light_pos(50, eye, direction, hue, eta);
    const vec3 light_d = RandomUnitVector(prng_state);

    const float energy = 1.0;

    int step_count = 0;
    vec3 step_locations[max_bounces + 3];
    vec3 step_directions[max_bounces + 3];

    traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, eye, 0.001, direction, 10000.0, 0);

    if(rayPayload.dist == -1)
        return 0.0;

    const vec3 first_hit_pos = eye + direction * rayPayload.dist;

    step_locations[step_count] = light_o;
    step_directions[step_count] = light_d;
    step_count++;

    if(false == is_clear_line_of_sight(first_hit_pos, light_o))
    {   
        vec3 o = light_o;
        vec3 d = light_d;

        for(int i = 0; i < max_bounces; i++)
        {
            traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, o, 0.001, d, 10000.0, 0);

            if(rayPayload.dist == -1)
                return 0.0;

            const vec3 hitPos = o + d * rayPayload.dist;

            o = hitPos + rayPayload.normal * 0.01;
            d = cosWeightedRandomHemisphereDirection(rayPayload.normal, prng_state);
            
            step_locations[step_count] = o;
            step_directions[step_count] = d;
            step_count++;

            if(true == is_clear_line_of_sight(first_hit_pos, step_locations[step_count - 1]))
                break;
        }
    }

    step_locations[step_count] = first_hit_pos;
    step_directions[step_count] = light_d;
    step_count++;

    step_locations[step_count] = eye;
    step_directions[step_count] = direction;
    step_count++;

    // Reverse the path
    uint start = 0;
    uint end = step_count - 1;

    while(start < end) 
    { 
        vec3 temp = step_locations[start];  
        step_locations[start] = step_locations[end]; 
        step_locations[end] = temp; 

        temp = step_directions[start];
        step_directions[start] = step_directions[end]; 
        step_directions[end] = temp; 

        start++;
        end--; 
    }

    float ret_colour = 0;
    float local_colour = energy;
    float total = 0;

    for(int i = 0; i < step_count - 1; i++)
    {
        vec3 step_o = step_locations[i];
        vec3 step_d = step_directions[i];

        traceRayEXT(topLevelAS, gl_RayFlagsOpaqueEXT, 0xff, 0, 0, 0, step_o, 0.001, step_d, 10000.0, 0);

        local_colour *= (rayPayload.color.r*mask.r + rayPayload.color.g*mask.g + rayPayload.color.b*mask.b);

        total += mask.r;
        total += mask.g;
        total += mask.b;

        if(i == step_count - 2 || 
        (rayPayload.color.r > 1.0 || 
        rayPayload.color.g > 1.0 || 
        rayPayload.color.b > 1.0))
        {
            ret_colour += local_colour;
            break;
        }
    }

    return ret_colour / total;
}

```

> **Comments:**
> **username_1**: You see, the global illumination is not correct. Maybe it's because that you break too early from the ray bouncing (first for loop). Can you explain what does the function `is_clear_line_of_sight` do in your code, in detail? I am confused by the logic here.  
> **username_0**: You're right. I was mislead into thinking that it's working in general. I appreciate your efforts. The function traces a ray from point a to point b, to determine if there is anything occluding b from a.  
> **username_0**: ... but it goes to show that splatting is absolutely unnecessary! That's neat.  
> **username_1**: I don't think splatting is unnecessary. It bares the same idea of next event estimation: if the light source is small, we make a shadow connection to a point on the light source. Now in this reversed settings, camera is indeed small and for non-lens-based cameras, you will have zero probability for a ray to hit the camera. So for each vertex on the traced path, a shadow connection to the camera should be made and evaluate this direct component (how much radiance can the camera receive through this shadow connection, if the connection is not occluded).  

---

