I have a forward path tracer. Essentially, it looks flat:
This is the backward path tracer in comparison:
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.
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. $\endgroup$