Added new files for BVH, AABB, and Debug functionalities. Added new utility functions in Common.h. Added gamma correction function in PostProcessing.h. Changed the return type of path_trace to vec4s for alpha blending. Changed BSDF function signatures to include sample index and bounce. Changed the BSDF.h to replace inline functions with declarations. Changed the Light and SkyLight evaluation functions to include throughput and sample index. Changed the sphere creation function in GeometryUtilities.h for better quality. Changed the scene structure to include a BVH tree for improved ray intersection. Changed the scene initialization parameters for better performance. Created new Debug functions for ray intersection counting. Created new functions for triangle collection management in Triangle.c. Improved pixel updating logic in Window.c. Improved ray intersection performance with new BVH implementation. Removed unused includes from Common.h. Removed old library linking methods in CMakeLists.txt.
162 lines
7.1 KiB
C
162 lines
7.1 KiB
C
#include "Material/SimpleLit.h"
|
|
#include "Algorithm/BSDF.h"
|
|
#include "Algorithm/Sobol.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%)
|
|
|
|
// Simple lit, but keep it unbiased as much as possible
|
|
vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out)
|
|
{
|
|
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
|
|
|
//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(normal, wo), 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 total_prob = prob_diffuse + prob_specular;
|
|
if (total_prob < FLT_EPSILON)
|
|
{
|
|
*pdf_out = 0.0f;
|
|
return glms_vec3_zero();
|
|
}
|
|
|
|
// Normalize probabilities
|
|
// total_prob should be 1.0f, worth it? Maybe still need to avoid floating point errors
|
|
prob_diffuse /= total_prob;
|
|
prob_specular /= total_prob;
|
|
|
|
vec3s wi;
|
|
float lob_sample = sobol_sample(sample_index, sobol_get_dimension(bounce, PRNG_BSDF));
|
|
uint16_t d1 = sobol_get_dimension(bounce, PRNG_BSDF_U);
|
|
uint16_t d2 = sobol_get_dimension(bounce, PRNG_BSDF_V);
|
|
|
|
if (lob_sample < prob_diffuse) // Diffuse Lobe
|
|
{
|
|
wi = random_cosine_direction(normal, sample_index, d1, d2);
|
|
}
|
|
else // Specular Lobe
|
|
{
|
|
// For simplification we use blinn-phong lobe distribution, we will implement GGX for standard lit later
|
|
// 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 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);
|
|
// float theta = acosf(powf(random_float(), 1.0f / (specular_exponent + 1.0f)));
|
|
// float phi = 2.0f * (float)M_PI * random_float();
|
|
vec3s h_ts = (vec3s)
|
|
{
|
|
sinf(theta) * cosf(phi),
|
|
sinf(theta) * sinf(phi),
|
|
cosf(theta)
|
|
};
|
|
|
|
vec3s tangent_u; // World-space tangent (U)
|
|
vec3s bitangent_v; // World-space bitangent (V)
|
|
create_orthonormal_basis(normal, &tangent_u, &bitangent_v);
|
|
|
|
vec3s scaled_u = glms_vec3_scale(tangent_u, h_ts.x);
|
|
vec3s scaled_v = glms_vec3_scale(bitangent_v, h_ts.y);
|
|
vec3s scaled_n = glms_vec3_scale(normal, h_ts.z);
|
|
|
|
// Transform h from tangent space to world space
|
|
vec3s h_ws;
|
|
h_ws = glms_vec3_add(scaled_u, scaled_v);
|
|
h_ws = glms_vec3_add(h_ws, scaled_n);
|
|
h_ws = glms_vec3_normalize(h_ws);
|
|
|
|
// wi is simple now, just reflect wo around normal
|
|
wi = glms_vec3_reflect(glms_vec3_negate(wo), h_ws);
|
|
}
|
|
|
|
// Final check to ensure wi is in the correct hemisphere
|
|
if (glms_vec3_dot(wi, normal) < 0.0f)
|
|
{
|
|
*pdf_out = 0.0f;
|
|
return glms_vec3_zero();
|
|
}
|
|
|
|
float pdf_diffuse = pdf_cosine_weighted_hemisphere(normal, wi);
|
|
float pdf_specular = pdf_blinn_phong_lobe(normal, wi, wo, shading_data.roughness);
|
|
*pdf_out = prob_diffuse * pdf_diffuse + prob_specular * pdf_specular;
|
|
|
|
return wi;
|
|
}
|
|
|
|
//TODO: Most of the calculation here is same as in sample_bsdf_simple_lit, we can optimize this by using a bsdf data struct to avoid recomputing the same thing in both sample and evaluate
|
|
float sample_bsdf_pdf_simple_lit(const void* data, vec3s normal, vec3s wo, vec3s wi)
|
|
{
|
|
// If wi is below the horizon relative to the normal, PDF must be 0
|
|
if (glms_vec3_dot(normal, wi) <= 0.0f) // Use <= to be safe
|
|
{
|
|
return 0.0f;
|
|
}
|
|
|
|
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
|
|
|
// 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(normal, wo), 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 total_prob = prob_diffuse + prob_specular;
|
|
|
|
if (total_prob < FLT_EPSILON)
|
|
{
|
|
return 0.0f; // No probability of scattering
|
|
}
|
|
prob_diffuse /= total_prob;
|
|
prob_specular /= total_prob;
|
|
|
|
float pdf_diff = pdf_cosine_weighted_hemisphere(normal, wi);
|
|
float diffuse_pdf_component = prob_diffuse * pdf_diff;
|
|
|
|
float pdf_spec = pdf_blinn_phong_lobe(normal, wo, wi, shading_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* data)
|
|
{
|
|
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
|
shading_context_t shading_context = *context;
|
|
|
|
vec3s h = glms_vec3_normalize(glms_vec3_add(shading_context.wi, shading_context.wo));
|
|
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(shading_context.normal, shading_context.wi));
|
|
float n_dot_v = fmaxf(FLT_EPSILON, glms_vec3_dot(shading_context.normal, shading_context.wo));
|
|
float n_dot_h = glms_vec3_dot(shading_context.normal, h);
|
|
float v_dot_h = glms_vec3_dot(shading_context.wo, h);
|
|
|
|
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
|
|
|
|
vec3s diffuse_color = glms_vec3_scale(shading_data.albedo, 1.0f - shading_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);
|
|
// 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);
|
|
vec3s F = fresnel_schlick_vec3(f0, v_dot_h);
|
|
|
|
float denominator = 4.0f * n_dot_l * n_dot_v;
|
|
if (denominator < FLT_EPSILON)
|
|
{
|
|
return diffuse_term;
|
|
}
|
|
|
|
vec3s specular_term = glms_vec3_scale(F, D / denominator); // Specular term (Blinn-Phong), we assume that G = 1.0f for simplicity
|
|
|
|
return glms_vec3_add(diffuse_term, specular_term);
|
|
}
|