174 lines
6.3 KiB
C
174 lines
6.3 KiB
C
#include "Algorithm/PathTracing.h"
|
|
#include "Algorithm/RayIntersection.h"
|
|
#include "Lighting/LightEvaluation.h"
|
|
#include "Algorithm/BSDF.h"
|
|
|
|
// TODO: Split the diffuse and specular into different Monte Carlo, so we can decide the sample count for each one
|
|
vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth)
|
|
{
|
|
vec4s accumulated_color = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
|
vec3s throughput = glms_vec3_one();
|
|
|
|
ray_t active_ray = ray;
|
|
|
|
// PDF of the direction that generated the current ray segment (used for MIS on env hits).
|
|
float last_bsdf_pdf = 0.0f;
|
|
|
|
uint16_t depth = 0;
|
|
while (depth < max_depth)
|
|
{
|
|
hit_result_t closest_hit = ray_intersect_scene_closest(&active_ray, scene);
|
|
|
|
if (!closest_hit.hit)
|
|
{
|
|
// Set bvh to null indicate that the ray is not hit anything
|
|
light_shading_context_t light_context =
|
|
{
|
|
.wo = active_ray.direction,
|
|
.textures = &scene->textures,
|
|
.spread_angle = active_ray.spread_angle,
|
|
};
|
|
path_output sky_output = evaluate_bsdf_sky(&scene->lights, &light_context, throughput, sample_index);
|
|
|
|
// MIS for BSDF-sampled environment hit (for depth==0 camera ray, use weight 1).
|
|
float w = 1.0f;
|
|
if (depth > 0)
|
|
{
|
|
float pdf_env = sky_output.pdf;
|
|
float pdf_bsdf = last_bsdf_pdf;
|
|
if (pdf_env > 0.0f && pdf_bsdf > 0.0f)
|
|
{
|
|
w = power_heuristic(pdf_bsdf, pdf_env);
|
|
}
|
|
}
|
|
|
|
vec3s env_contrib = glms_vec3_scale(sky_output.direct_lighting, w);
|
|
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(env_contrib, 0.0f));
|
|
break;
|
|
}
|
|
|
|
uint8_t material_id = scene->triangles.buffer[closest_hit.triangle_id].material_id;
|
|
const material_t* hit_material = &scene->materials.buffer[material_id];
|
|
|
|
// Calculate ray cone width at the hit point
|
|
float current_cone_width = active_ray.width + closest_hit.distance * active_ray.spread_angle;
|
|
|
|
shading_context_t shading_context =
|
|
{
|
|
.camera_position = scene->camera.position,
|
|
.camera_direction = glms_vec3_normalize(glms_vec3_sub(closest_hit.point, scene->camera.position)),
|
|
|
|
.position = closest_hit.point,
|
|
.normal = closest_hit.normal,
|
|
.tangent = closest_hit.tangent,
|
|
.uv = closest_hit.uv,
|
|
.wo = active_ray.direction,
|
|
.throughput = throughput,
|
|
|
|
.sample_index = sample_index,
|
|
.bounce_depth = depth,
|
|
|
|
.bvh_tree = &scene->bvh_tree,
|
|
.triangles = &scene->triangles,
|
|
.lights = &scene->lights,
|
|
.textures = &scene->textures,
|
|
|
|
.triangle_id = closest_hit.triangle_id,
|
|
.cone_width = current_cone_width,
|
|
.spread_angle = active_ray.spread_angle,
|
|
};
|
|
|
|
path_output material_output = render_material(hit_material, &shading_context);
|
|
if (glms_vec3_isinf(material_output.direct_lighting) || glms_vec3_isnan(material_output.direct_lighting))
|
|
{
|
|
goto end_path_trace;
|
|
}
|
|
|
|
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(material_output.direct_lighting, 0.0f));
|
|
|
|
if (material_output.pdf < FLT_EPSILON)
|
|
{
|
|
goto end_path_trace;
|
|
}
|
|
|
|
last_bsdf_pdf = material_output.pdf;
|
|
|
|
throughput = glms_vec3_mul(throughput, material_output.bsdf);
|
|
if (glms_vec3_isinf(throughput) || glms_vec3_isnan(throughput))
|
|
{
|
|
goto end_path_trace;
|
|
}
|
|
|
|
// We do Russian roulette to decide whether to continue tracing or terminate the path
|
|
if (depth > 1)
|
|
{
|
|
float q = fminf(glms_vec3_max(throughput), 0.95f);
|
|
float rr = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
|
|
if (rr > q)
|
|
{
|
|
goto end_path_trace;
|
|
}
|
|
// Keep the energy of the path by scaling the throughput
|
|
throughput = glms_vec3_scale(throughput, 1.0f / q);
|
|
}
|
|
|
|
switch (material_output.state)
|
|
{
|
|
//case PATH_THROUGH:
|
|
// active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, glms_vec3_negate(closest_hit.normal)), active_ray.direction);
|
|
// continue;
|
|
case PS_SUCCESS:
|
|
vec3s origin = offset_ray_origin(closest_hit.point, closest_hit.normal, shading_context.wo);
|
|
active_ray = ray_create(origin, material_output.wi, current_cone_width, material_output.spread_angle);
|
|
depth++;
|
|
break;
|
|
default:
|
|
goto end_path_trace;
|
|
}
|
|
}
|
|
|
|
end_path_trace:
|
|
return accumulated_color;
|
|
}
|
|
|
|
// How to handle multi-bounced aov like indirect lighting?
|
|
// Maybe we should move aov to path_trace and split accumulated_color into direct/indirect diffuse/specular before returning.
|
|
void render_aov(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth, aov_output_t* aov_output)
|
|
{
|
|
hit_result_t closest_hit = ray_intersect_scene_closest(&ray, scene);
|
|
if (!closest_hit.hit)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const material_t* hit_material = &scene->materials.buffer[scene->triangles.buffer[closest_hit.triangle_id].material_id];
|
|
shading_context_t shading_context =
|
|
{
|
|
.camera_position = scene->camera.position,
|
|
.camera_direction = glms_vec3_normalize(glms_vec3_sub(closest_hit.point, scene->camera.position)),
|
|
|
|
.position = closest_hit.point,
|
|
.normal = closest_hit.normal,
|
|
.tangent = closest_hit.tangent,
|
|
.uv = closest_hit.uv,
|
|
.wo = ray.direction,
|
|
.throughput = 1.0f,
|
|
|
|
.sample_index = sample_index,
|
|
.bounce_depth = 0,
|
|
|
|
.bvh_tree = &scene->bvh_tree,
|
|
.triangles = &scene->triangles,
|
|
.lights = &scene->lights,
|
|
.textures = &scene->textures,
|
|
|
|
.triangle_id = closest_hit.triangle_id,
|
|
.cone_width = ray.width + closest_hit.distance * ray.spread_angle,
|
|
.spread_angle = ray.spread_angle,
|
|
};
|
|
|
|
render_material_aov(hit_material, &shading_context, aov_output);
|
|
aov_output->position = glms_vec4(closest_hit.point, 1.0f);
|
|
aov_output->depth = closest_hit.distance;
|
|
}
|