Files
SimpleRayTracing/source/Algorithm/PathTracing.c
Misaki 3c3168af7a Add texture handling and refactor material functions
Added a new function `blinn_phong_specular_exponent_to_roughness` in `BSDF.h` to convert a specular exponent to roughness.
Added a `textures` member to the `shading_context_t` structure in `Material.h` for passing texture information during shading.
Added a new member `textures` in the `light_shading_context_t` structure in `Light.h` to hold texture information.
Added inline functions in `Texture.h` for handling texture entities, including `invalid_texture_entity`, `is_texture_entity_valid`, and `get_texture`.
Changed the texture-related members in `simple_lit_properties_t` in `SimpleLit.h` from pointers to `texture_entity_t` to better manage texture entities.
Changed the `sample_material_bsdf` and `sample_material_bsdf_pdf` functions in `Material.h` to accept a `shading_context_t` instead of individual parameters.
Changed the `mesh_load` function in `Mesh.c` to use the new roughness calculation and texture entity handling.
Changed the `path_trace` function in `PathTracing.c` to use the new structure and functions for handling materials and textures.
Refactored the `sample_material_bsdf` and `sample_material_bsdf_pdf` functions in `Material.c` to utilize the new `shading_context_t` structure.
Updated the `material_collection_init` function in `Material.h` to reflect changes in the material sampling functions.
Updated the `evaluate_bsdf_directional` and `evaluate_bsdf_const_sky` functions to use the new shading context structure.
Adjusted the `simple_lit_data_default` function in `SimpleLit.c` to work with the new texture handling approach.
Added texture entity handling in `Texture.c` for managing invalid texture entities.
2025-04-29 13:29:29 +09:00

109 lines
4.2 KiB
C

#include "Algorithm/PathTracing.h"
#include "Algorithm/RayIntersection.h"
#include "Algorithm/BSDF.h"
#include "Lighting/LightEvaluation.h"
// TODO: Implement a faster methods like BVH, KD-Tree or uniform grid acceleration
// 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;
vec3s prev_normal = glms_vec3_zero();
float pdf_bsdf = 1.0f;
uint16_t depth = 0;
while (depth < max_depth)
{
hit_result_t closest_hit = ray_intersect_scene(&active_ray, scene);
if (!closest_hit.hit)
{
vec3s sky_light = evaluate_bsdf_sky(scene, NULL, throughput, sample_index);
if (depth > 0)
{
// Have to multiply the weight since we evaluate the sky at each bounce
float pdf_nee = pdf_cosine_weighted_hemisphere(prev_normal, active_ray.direction);
float weight = power_heuristic(pdf_bsdf, pdf_nee);
sky_light = glms_vec3_scale(sky_light, weight);
}
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f));
break;
}
// Add the emission of the hit material to the accumulated color
const material_t* hit_material = &scene->materials.buffer[scene->triangles.buffer[closest_hit.triangle_id].material_id];
vec3s emission = hit_material->emission;
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(glms_vec3_mul(throughput, emission), 0.0f));
light_shading_context_t light_context = {
.normal = closest_hit.normal,
.hit_point = closest_hit.point,
.wo = active_ray.direction,
.uv = closest_hit.uv,
.bounce_depth = depth,
.bvh_tree = &scene->bvh_tree,
.material = hit_material,
.textures = &scene->textures,
};
// Running the light loop.
// TODO: Implementing other light types.
for (uint32_t i = 0; i < scene->lights.directional_light_count; i++)
{
vec3s l = evaluate_bsdf_directional(scene->lights.directional_lights[i], &light_context, throughput, sample_index);
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(l, 0.0f));
}
vec3s sky_light = evaluate_bsdf_sky(scene, &light_context, throughput, sample_index);
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f));
// Bounce and prepare for the next iteration
shading_context_t shading_context = {
.normal = closest_hit.normal,
.position = closest_hit.point,
.wo = glms_vec3_negate(active_ray.direction),
.uv = closest_hit.uv,
.textures = &scene->textures,
};
vec3s wi = sample_material_bsdf(hit_material, &shading_context, sample_index, depth, &pdf_bsdf);
shading_context.wi = wi;
if (pdf_bsdf <= 0.0f)
{
break;
}
vec3s bsdf = evaluate_material_bsdf(hit_material, &shading_context);
float cos_theta = fmaxf(0.0f, glms_vec3_dot(wi, closest_hit.normal));
throughput = glms_vec3_mul(throughput, glms_vec3_scale(bsdf, cos_theta / pdf_bsdf));
// 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_sample = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
// float rr_sample = random_float();
if (rr_sample > q)
{
break; // Terminate the path
}
// Keep the energy of the path by scaling the throughput
throughput = glms_vec3_scale(throughput, 1.0f / q);
}
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal), wi);
prev_normal = closest_hit.normal;
depth++;
}
return accumulated_color;
}