Refactor path tracing and material rendering system
Added a new enum `path_state` and a struct `path_output` in `Common.h` to manage path tracing states and outputs. Added a new `simple_lit_render_loop` function in `SimpleLit.c` to handle rendering for simple lit materials, integrating light contributions and BSDF evaluations. Changed the function signatures in `Light.h` to use `path_output` instead of `vec3s` for BSDF evaluations. Changed the `evaluate_bsdf_directional` function in `LightEvaluation.h` to return `path_output` instead of `vec3s`. Changed the `evaluate_bsdf_sky` function in `LightEvaluation.h` to return `path_output` and updated its implementation accordingly. Changed the function signature of `evaluate_bsdf_const_sky` in `SkyLight.h` to return `path_output` and modified its implementation to match. Removed several old BSDF-related functions in `Material.c` that are no longer needed due to the new structure. Updated the `material_create` function in `Material.c` to accept the new render loop function pointer. Updated the `path_trace` function in `PathTracing.c` to utilize the new `path_output` structure for managing light contributions and state transitions during path tracing. Updated the `evaluate_bsdf_directional` function in `LightEvaluation.c` to return `path_output` and adjusted its internal logic to populate the output structure. Updated the `evaluate_bsdf_const_sky` function in `SkyLight.c` to return `path_output` and modified its logic to handle light contributions correctly. Updated the intensity of the sky light to `0.0f` during scene setup in `main.c`, effectively disabling it.
This commit is contained in:
@@ -3,7 +3,6 @@
|
||||
#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)
|
||||
{
|
||||
@@ -21,91 +20,75 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
|
||||
if (!closest_hit.hit)
|
||||
{
|
||||
vec3s sky_light = evaluate_bsdf_sky(scene, NULL, throughput, sample_index);
|
||||
vec3s sky_light = evaluate_bsdf_sky(&scene->lights, NULL, throughput, sample_index).direct_lighting;
|
||||
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 =
|
||||
{
|
||||
.hit_point = closest_hit.point,
|
||||
.normal = closest_hit.normal,
|
||||
.tangent = closest_hit.tangent,
|
||||
.uv = closest_hit.uv,
|
||||
.wo = active_ray.direction,
|
||||
|
||||
.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 =
|
||||
{
|
||||
.position = closest_hit.point,
|
||||
.normal = closest_hit.normal,
|
||||
.tangent = closest_hit.tangent,
|
||||
.uv = closest_hit.uv,
|
||||
.wo = glms_vec3_negate(active_ray.direction),
|
||||
.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,
|
||||
};
|
||||
|
||||
vec3s wi = sample_material_bsdf(hit_material, &shading_context, sample_index, depth, &pdf_bsdf);
|
||||
shading_context.wi = wi;
|
||||
if (pdf_bsdf <= 0.0f)
|
||||
path_output material_output = render_material(hit_material, &shading_context);
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(material_output.direct_lighting, 0.0f));
|
||||
|
||||
float cos_theta = fmaxf(0.0f, glms_vec3_dot(material_output.wi, closest_hit.normal));
|
||||
throughput = glms_vec3_mul(throughput, material_output.bsdf);
|
||||
|
||||
switch (material_output.state)
|
||||
{
|
||||
break;
|
||||
case TERMINATE:
|
||||
goto end_path_trace;
|
||||
case PATH_THROUGH:
|
||||
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, glms_vec3_negate(closest_hit.normal)), active_ray.direction);
|
||||
continue;
|
||||
default:
|
||||
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));
|
||||
if (rr_sample > q)
|
||||
float rr = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
|
||||
if (rr > q)
|
||||
{
|
||||
break; // Terminate the path
|
||||
goto end_path_trace;
|
||||
}
|
||||
// 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);
|
||||
active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, closest_hit.normal), material_output.wi);
|
||||
prev_normal = closest_hit.normal;
|
||||
pdf_bsdf = material_output.pdf;
|
||||
|
||||
depth++;
|
||||
}
|
||||
|
||||
end_path_trace:
|
||||
return accumulated_color;
|
||||
}
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
|
||||
vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||
path_output evaluate_bsdf_directional(directional_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||
{
|
||||
path_output output = {0.0f};
|
||||
output.state = TERMINATE;
|
||||
|
||||
float angular_radius = glm_rad(light.angular_diameter / 2.0f);
|
||||
|
||||
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
|
||||
@@ -14,7 +17,7 @@ vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_c
|
||||
float n_dot_l = glms_vec3_dot(context->normal, wi);
|
||||
if (n_dot_l <= 0.0f)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
return output;
|
||||
}
|
||||
|
||||
ray_t shadow_ray = ray_create(BIAS_RAY_ORIGION(context->hit_point, context->normal), wi);
|
||||
@@ -25,20 +28,14 @@ vec3s evaluate_bsdf_directional(directional_light_t light, const light_shading_c
|
||||
&closest, &shadow_hit);
|
||||
if (shadow_hit.hit)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
return output;
|
||||
}
|
||||
|
||||
shading_context_t shading_context = {
|
||||
.normal = context->normal,
|
||||
.wi = wi,
|
||||
.wo = glms_vec3_negate(context->wo),
|
||||
.uv = context->uv,
|
||||
|
||||
.textures = context->textures,
|
||||
};
|
||||
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
|
||||
|
||||
vec3s light_radiance = glms_vec3_scale(light.color, light.intensity);
|
||||
vec3s light_contribute = glms_vec3_scale(glms_vec3_mul(throughput, bsdf), fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for directional light
|
||||
return glms_vec3_mul(light_radiance, light_contribute);
|
||||
vec3s light_contribute = glms_vec3_scale( throughput, fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for directional light
|
||||
|
||||
output.direct_lighting = glms_vec3_mul(light_radiance, light_contribute);
|
||||
output.wi = wi;
|
||||
output.state = NORMAL;
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
#include "Lighting/SkyLight.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
#include "Material/Material.h"
|
||||
|
||||
vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||
path_output evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||
{
|
||||
constant_sky_data_t sky_data = *(const constant_sky_data_t*)data;
|
||||
vec3s sky_color = glms_vec3_scale(sky_data.color, sky_data.intensity);
|
||||
|
||||
path_output output = {0.0f};
|
||||
output.state = TERMINATE;
|
||||
|
||||
if (context == NULL)
|
||||
{
|
||||
return glms_vec3_mul(sky_color, throughput);
|
||||
output.direct_lighting = glms_vec3_mul(sky_color, throughput);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
|
||||
@@ -26,25 +28,15 @@ vec3s evaluate_bsdf_const_sky(const void* data, const light_shading_context_t* c
|
||||
ray_intersect_bvh(&shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0, &closest, &shadow_hit);
|
||||
if (shadow_hit.hit)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
return output;
|
||||
}
|
||||
|
||||
shading_context_t shading_context = {
|
||||
.normal = context->normal,
|
||||
.wi = wi,
|
||||
.wo = glms_vec3_negate(context->wo),
|
||||
.uv = context->uv,
|
||||
|
||||
.textures = context->textures,
|
||||
};
|
||||
|
||||
vec3s bsdf = evaluate_material_bsdf(context->material, &shading_context);
|
||||
bsdf = glms_vec3_mul(bsdf, sky_color);
|
||||
float cos_theta = fmaxf(glms_vec3_dot(wi, context->normal), 0.0f);
|
||||
output.direct_lighting = glms_vec3_scale(throughput, cos_theta / pdf);
|
||||
|
||||
float pdf_bsdf = sample_material_bsdf_pdf(context->material, &shading_context);
|
||||
float weight = power_heuristic(pdf, pdf_bsdf);
|
||||
|
||||
vec3s env_contrib = glms_vec3_scale(glms_vec3_mul(throughput, bsdf), cos_theta / pdf);
|
||||
return glms_vec3_scale(env_contrib, weight);
|
||||
output.wi = wi;
|
||||
output.pdf = pdf;
|
||||
output.state = NORMAL;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ void material_collection_free(material_collection_t* materials)
|
||||
}
|
||||
}
|
||||
|
||||
material_entity_t material_create(const void* properties, size_t properties_size, compute_surface_data_f surface_data, sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, material_collection_t* collection)
|
||||
material_entity_t material_create(const void* properties, size_t properties_size, material_render_loop_f render_loop, material_collection_t* collection)
|
||||
{
|
||||
material_t material = {0};
|
||||
|
||||
@@ -61,10 +61,7 @@ material_entity_t material_create(const void* properties, size_t properties_size
|
||||
}
|
||||
|
||||
memcpy(material.properties, properties, properties_size);
|
||||
material.compute_surface_data = surface_data;
|
||||
material.sample_bsdf = sample;
|
||||
material.sample_bsdf_pdf = sample_pdf;
|
||||
material.evaluate_bsdf = evaluate;
|
||||
material.render_loop = render_loop;
|
||||
|
||||
material_entity_t entity = {.id = collection->count};
|
||||
|
||||
@@ -73,36 +70,3 @@ material_entity_t material_create(const void* properties, size_t properties_size
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
vec3s sample_material_bsdf(const material_t* material, const shading_context_t* context, uint32_t sample_index, uint32_t bounce, float* pdf_out)
|
||||
{
|
||||
vec3s wi = glms_vec3_zero();
|
||||
if (material->compute_surface_data != NULL && material->sample_bsdf != NULL)
|
||||
{
|
||||
wi = material->sample_bsdf(context, material->properties, material->compute_surface_data, sample_index, bounce, pdf_out);
|
||||
}
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
float sample_material_bsdf_pdf(const material_t* material, const shading_context_t* context)
|
||||
{
|
||||
float pdf = 0.0f;
|
||||
if (material->compute_surface_data != NULL && material->sample_bsdf_pdf != NULL)
|
||||
{
|
||||
pdf = material->sample_bsdf_pdf(context, material->properties, material->compute_surface_data);
|
||||
}
|
||||
|
||||
return pdf;
|
||||
}
|
||||
|
||||
vec3s evaluate_material_bsdf(const material_t* material, const shading_context_t* context)
|
||||
{
|
||||
vec3s bsdf_color = glms_vec3_zero();
|
||||
if (material->compute_surface_data != NULL && material->evaluate_bsdf != NULL)
|
||||
{
|
||||
bsdf_color = material->evaluate_bsdf(context, material->properties, material->compute_surface_data);
|
||||
}
|
||||
|
||||
return bsdf_color;
|
||||
}
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
#include "Material/SimpleLit.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/Sobol.h"
|
||||
#include "Lighting/LightEvaluation.h"
|
||||
#include "Lighting/SkyLight.h"
|
||||
|
||||
#include <float.h>
|
||||
|
||||
static float DIELECTRIC_REFLECTIVE_F0 = 0.04f; // Standard dielectric reflectivity coef at incident angle (= 4%)
|
||||
static vec3s DIELECTRIC_REFLECTIVE = {0.04f, 0.04f, 0.04f}; // Standard dielectric reflectivity coef at incident angle (= 4%)
|
||||
|
||||
void simple_lit_data_default(const shading_context_t* context, const void* properties, void* data_out)
|
||||
static void get_surface_data(const shading_context_t* context, const void* properties, surface_data_t* data_out)
|
||||
{
|
||||
const simple_lit_properties_t* prop = (simple_lit_properties_t*)properties;
|
||||
|
||||
simple_lit_data_t* data = (simple_lit_data_t*)data_out;
|
||||
|
||||
data->albedo = prop->albedo;
|
||||
data_out->albedo = prop->albedo;
|
||||
const texture_t* albedo_texture = get_texture(context->textures, prop->albedo_texture);
|
||||
if (albedo_texture != NULL && albedo_texture->data != NULL)
|
||||
{
|
||||
data->albedo = glms_vec3_mul(data->albedo, glms_vec3(texture_sample(albedo_texture, context->uv.x, context->uv.y)));
|
||||
data_out->albedo = glms_vec3_mul(data_out->albedo, glms_vec3(texture_sample(albedo_texture, context->uv.x, context->uv.y)));
|
||||
}
|
||||
|
||||
const texture_t* normal_texture = get_texture(context->textures, prop->normal_texture);
|
||||
@@ -24,42 +25,41 @@ void simple_lit_data_default(const shading_context_t* context, const void* prope
|
||||
{
|
||||
vec3s normal_sample = glms_vec3(texture_sample(normal_texture, context->uv.x, context->uv.y));
|
||||
normal_sample = normal_unpack(normal_sample);
|
||||
data->normal = normal_ts_to_ws(normal_sample, context->tangent);
|
||||
data_out->normal = normal_ts_to_ws(normal_sample, context->tangent);
|
||||
}
|
||||
else
|
||||
{
|
||||
data->normal = context->normal;
|
||||
data_out->normal = context->normal;
|
||||
}
|
||||
|
||||
data->roughness = prop->roughness;
|
||||
data_out->roughness = prop->roughness;
|
||||
const texture_t* roughness_texture = get_texture(context->textures, prop->roughness_texture);
|
||||
if (roughness_texture != NULL && roughness_texture->data != NULL)
|
||||
{
|
||||
data->roughness = data->roughness * texture_sample(roughness_texture, context->uv.x, context->uv.y).x;
|
||||
data_out->roughness = data_out->roughness * texture_sample(roughness_texture, context->uv.x, context->uv.y).x;
|
||||
}
|
||||
|
||||
data->metallic = prop->metallic;
|
||||
data_out->metallic = prop->metallic;
|
||||
const texture_t* metallic_texture = get_texture(context->textures, prop->metallic_texture);
|
||||
if (metallic_texture != NULL && metallic_texture->data != NULL)
|
||||
{
|
||||
data->metallic = data->metallic * texture_sample(metallic_texture, context->uv.x, context->uv.y).x;
|
||||
data_out->metallic = data_out->metallic * texture_sample(metallic_texture, context->uv.x, context->uv.y).x;
|
||||
}
|
||||
}
|
||||
|
||||
// Simple lit, but keep it unbiased as much as possible
|
||||
vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data, uint32_t sample_index, uint32_t bounce, float* pdf_out)
|
||||
static vec3s sample_bsdf_simple_lit(const shading_context_t* context, const surface_data_t* surface_data, uint32_t sample_index, uint32_t bounce, float* pdf_out)
|
||||
{
|
||||
simple_lit_data_t shading_data;
|
||||
compute_surface_data(context, properties, &shading_data);
|
||||
vec3s L = glms_vec3_negate(context->wo);
|
||||
|
||||
//TODO: having a bsdf data struct to avoid recomputing the same thing in both sample and evaluate
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
|
||||
float cos_theta_0 = fmaxf(glms_vec3_dot(context->normal, context->wo), 0.0f);
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, surface_data->albedo, surface_data->metallic);
|
||||
float cos_theta_0 = fmaxf(glms_vec3_dot(context->normal, L), 0.0f);
|
||||
vec3s f = fresnel_schlick_vec3(f0, cos_theta_0);
|
||||
float lum_f = (f.x + f.y + f.z) / 3.0f;
|
||||
|
||||
float prob_specular = glm_lerp(lum_f, 1.0f, shading_data.metallic);
|
||||
float prob_diffuse = (1.0f - shading_data.metallic) * (1.0f - lum_f); // Diffuse only for non-metals, reduced by reflection
|
||||
float prob_specular = glm_lerp(lum_f, 1.0f, surface_data->metallic);
|
||||
float prob_diffuse = (1.0f - surface_data->metallic) * (1.0f - lum_f); // Diffuse only for non-metals, reduced by reflection
|
||||
float total_prob = prob_diffuse + prob_specular;
|
||||
if (total_prob < FLT_EPSILON)
|
||||
{
|
||||
@@ -87,7 +87,7 @@ vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* prope
|
||||
// When talking about simplification, wen even can use a simple interpolation bwtween roughness and wi, but it's too biased.
|
||||
// A common simplification involves sampling spherical coordinates(theta and phi angles) related to normal such that cose(theta) is distributed according to the Blinn-Phong distribution
|
||||
// We can use a inversion sampling where cos(theta) = powf(random_float(), 1.0f / (specular_exponent + 1.0f)) and phi = 2 * PI * random_float()
|
||||
float specular_exponent = roughness_to_blinn_phong_specular_exponent(shading_data.roughness);
|
||||
float specular_exponent = roughness_to_blinn_phong_specular_exponent(surface_data->roughness);
|
||||
|
||||
float theta = acosf(powf(sobol_sample(sample_index, d1), 1.0f / (specular_exponent + 1.0f)));
|
||||
float phi = 2.0f * (float)M_PI * sobol_sample(sample_index, d2);
|
||||
@@ -115,7 +115,7 @@ vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* prope
|
||||
h_ws = glms_vec3_normalize(h_ws);
|
||||
|
||||
// wi is simple now, just reflect wo around normal
|
||||
wi = glms_vec3_reflect(glms_vec3_negate(context->wo), h_ws);
|
||||
wi = glms_vec3_reflect(glms_vec3_negate(L), h_ws);
|
||||
}
|
||||
|
||||
// Final check to ensure wi is in the correct hemisphere
|
||||
@@ -126,30 +126,28 @@ vec3s sample_bsdf_simple_lit(const shading_context_t* context, const void* prope
|
||||
}
|
||||
|
||||
float pdf_diffuse = pdf_cosine_weighted_hemisphere(context->normal, wi);
|
||||
float pdf_specular = pdf_blinn_phong_lobe(context->normal, wi, context->wo, shading_data.roughness);
|
||||
float pdf_specular = pdf_blinn_phong_lobe(context->normal, wi, L, surface_data->roughness);
|
||||
*pdf_out = prob_diffuse * pdf_diffuse + prob_specular * pdf_specular;
|
||||
|
||||
return wi;
|
||||
}
|
||||
|
||||
float sample_bsdf_pdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data)
|
||||
static float sample_bsdf_pdf_simple_lit(const shading_context_t* context, const surface_data_t* surface_data, vec3s wi)
|
||||
{
|
||||
// If wi is below the horizon relative to the normal, PDF must be 0
|
||||
if (glms_vec3_dot(context->normal, context->wi) <= 0.0f) // Use <= to be safe
|
||||
if (glms_vec3_dot(context->normal, wi) <= 0.0f) // Use <= to be safe
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
simple_lit_data_t shading_data;
|
||||
compute_surface_data(context, properties, &shading_data);
|
||||
vec3s L = glms_vec3_negate(context->wo);
|
||||
|
||||
// Again, we need bsdf data;
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
|
||||
float cos_theta_o = fmaxf(glms_vec3_dot(context->normal, context->wo), 0.0f); // Use 'o' for outgoing (wo)
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, surface_data->albedo, surface_data->metallic);
|
||||
float cos_theta_o = fmaxf(glms_vec3_dot(context->normal, L), 0.0f); // Use 'o' for outgoing (wo)
|
||||
float F = glms_vec3_max(fresnel_schlick_vec3(f0, cos_theta_o));
|
||||
|
||||
float prob_specular = glm_lerp(F, 1.0f, shading_data.metallic);
|
||||
float prob_diffuse = (1.0f - shading_data.metallic) * (1.0f - F);
|
||||
float prob_specular = glm_lerp(F, 1.0f, surface_data->metallic);
|
||||
float prob_diffuse = (1.0f - surface_data->metallic) * (1.0f - F);
|
||||
float total_prob = prob_diffuse + prob_specular;
|
||||
|
||||
if (total_prob < FLT_EPSILON)
|
||||
@@ -159,32 +157,32 @@ float sample_bsdf_pdf_simple_lit(const shading_context_t* context, const void* p
|
||||
prob_diffuse /= total_prob;
|
||||
prob_specular /= total_prob;
|
||||
|
||||
float pdf_diff = pdf_cosine_weighted_hemisphere(context->normal, context->wi);
|
||||
float pdf_diff = pdf_cosine_weighted_hemisphere(context->normal, wi);
|
||||
float diffuse_pdf_component = prob_diffuse * pdf_diff;
|
||||
|
||||
float pdf_spec = pdf_blinn_phong_lobe(context->normal, context->wo, context->wi, shading_data.roughness);
|
||||
// HACK: Check this.
|
||||
float pdf_spec = pdf_blinn_phong_lobe(context->normal, L, wi, surface_data->roughness);
|
||||
float specular_pdf_component = prob_specular * pdf_spec;
|
||||
|
||||
return diffuse_pdf_component + specular_pdf_component;
|
||||
}
|
||||
|
||||
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* properties, const compute_surface_data_f compute_surface_data)
|
||||
static vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const surface_data_t* surface_data, vec3s wi)
|
||||
{
|
||||
simple_lit_data_t shading_data;
|
||||
compute_surface_data(context, properties, &shading_data);
|
||||
vec3s L = glms_vec3_negate(context->wo);
|
||||
|
||||
vec3s h = glms_vec3_normalize(glms_vec3_add(context->wi, context->wo));
|
||||
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, context->wi));
|
||||
float n_dot_v = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, context->wo));
|
||||
vec3s h = glms_vec3_normalize(glms_vec3_add(wi, L));
|
||||
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, wi));
|
||||
float n_dot_v = fmaxf(FLT_EPSILON, glms_vec3_dot(context->normal, L));
|
||||
float n_dot_h = glms_vec3_dot(context->normal, h);
|
||||
float v_dot_h = glms_vec3_dot(context->wo, h);
|
||||
float v_dot_h = glms_vec3_dot(L, h);
|
||||
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, surface_data->albedo, surface_data->metallic);
|
||||
|
||||
vec3s diffuse_color = glms_vec3_scale(shading_data.albedo, 1.0f - shading_data.metallic);
|
||||
vec3s diffuse_color = glms_vec3_scale(surface_data->albedo, 1.0f - surface_data->metallic);
|
||||
vec3s diffuse_term = glms_vec3_scale(diffuse_color, (float)M_1_PI);
|
||||
|
||||
float specular_exponent = roughness_to_blinn_phong_specular_exponent(shading_data.roughness);
|
||||
float specular_exponent = roughness_to_blinn_phong_specular_exponent(surface_data->roughness);
|
||||
// Normalization factor D (Blinn-Phong distribution)
|
||||
float D_norm = (specular_exponent + 2.0f) / (2.0f * (float)M_PI); // Common normalization
|
||||
float D = D_norm * powf(n_dot_h, specular_exponent);
|
||||
@@ -200,3 +198,68 @@ vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* pro
|
||||
|
||||
return glms_vec3_add(diffuse_term, specular_term);
|
||||
}
|
||||
|
||||
path_output simple_lit_render_loop(const void* properties, const shading_context_t* context)
|
||||
{
|
||||
surface_data_t surface_data = {0};
|
||||
get_surface_data(context, properties, &surface_data);
|
||||
|
||||
// TODO: Transparent handling
|
||||
// - if α == 0.0: pure pass-through; just offset the ray and continue.
|
||||
// - else if 0 < α < 1: treat α as scatter probability:
|
||||
// • with prob. α: scale throughput up by 1/α, accumulate direct lighting & BSDF.
|
||||
// • with prob. 1−α: pass-through the ray unmodified (throughput unchanged).
|
||||
|
||||
path_output output = {0.0f};
|
||||
|
||||
light_shading_context_t light_context =
|
||||
{
|
||||
.hit_point = context->position,
|
||||
.normal = context->normal,
|
||||
.tangent = context->tangent,
|
||||
.uv = context->uv,
|
||||
.wo = context->wo,
|
||||
|
||||
.bounce_depth = context->bounce_depth,
|
||||
|
||||
.bvh_tree = context->bvh_tree,
|
||||
.textures = context->textures,
|
||||
};
|
||||
|
||||
// Running the light loop.
|
||||
// TODO: Implementing other light types.
|
||||
for (uint32_t i = 0; i < context->lights->directional_light_count; i++)
|
||||
{
|
||||
path_output light_output = evaluate_bsdf_directional(context->lights->directional_lights[i], &light_context, context->throughput, context->sample_index);
|
||||
if (light_output.state == NORMAL)
|
||||
{
|
||||
vec3s bsdf_dir_light = evaluate_bsdf_simple_lit(context, &surface_data, light_output.wi);
|
||||
vec3s light_contribute = glms_vec3_mul(light_output.direct_lighting, bsdf_dir_light);
|
||||
output.direct_lighting = glms_vec3_add(output.direct_lighting, light_contribute);
|
||||
}
|
||||
}
|
||||
|
||||
// Sky
|
||||
path_output sky_output = evaluate_bsdf_sky(context->lights, &light_context, context->throughput, context->sample_index);
|
||||
if (sky_output.state == NORMAL)
|
||||
{
|
||||
vec3s bsdf_sky_light = evaluate_bsdf_simple_lit(context, &surface_data, sky_output.wi);
|
||||
float pdf_bsdf = sample_bsdf_pdf_simple_lit(context, &surface_data, sky_output.wi);
|
||||
vec3s sky_light = weight_sky_light(bsdf_sky_light, sky_output.direct_lighting, pdf_bsdf, sky_output.pdf);
|
||||
output.direct_lighting = glms_vec3_add(output.direct_lighting, sky_light);
|
||||
}
|
||||
|
||||
output.wi = sample_bsdf_simple_lit(context, &surface_data, context->sample_index, context->bounce_depth, &output.pdf);
|
||||
if (output.pdf <= 0.0f)
|
||||
{
|
||||
output.state = TERMINATE;
|
||||
return output;
|
||||
}
|
||||
|
||||
vec3s bsdf = evaluate_bsdf_simple_lit(context, &surface_data, output.wi);
|
||||
float cos_theta = fmaxf(0.0f, glms_vec3_dot(output.wi, context->normal));
|
||||
|
||||
output.bsdf = glms_vec3_scale(bsdf, cos_theta / output.pdf);
|
||||
output.state = NORMAL;
|
||||
return output;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user