Fixed cdf and added Standard Lit
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
#include "ALgorithm/BSDF.h"
|
||||
#include "cglm/util.h"
|
||||
#include "cglm/struct/mat3.h"
|
||||
#include "cglm/util.h"
|
||||
|
||||
float power_heuristic(float pdf_a, float pdf_b)
|
||||
{
|
||||
@@ -39,23 +39,39 @@ vec3s normal_unpack(vec3s normal)
|
||||
|
||||
vec3s normal_ts_to_ws(vec3s normal, vec3s geo_normal, vec3s tangent)
|
||||
{
|
||||
// 1. Sanitize inputs
|
||||
tangent = glms_vec3_normalize(tangent);
|
||||
vec3s bitangent = glms_vec3_cross(geo_normal, tangent);
|
||||
|
||||
float w = (glms_vec3_dot(glms_vec3_cross(geo_normal, tangent), bitangent) < 0.0f) ? -1.0f : +1.0f;
|
||||
geo_normal = glms_vec3_normalize(geo_normal);
|
||||
|
||||
// 2. Gram-Schmidt with safety check
|
||||
float proj = glms_vec3_dot(geo_normal, tangent);
|
||||
vec3s t_prime = glms_vec3_normalize(glms_vec3_sub(tangent, glms_vec3_scale(geo_normal, proj)));
|
||||
vec3s b_prime = glms_vec3_scale(glms_vec3_cross(geo_normal, t_prime), w);
|
||||
vec3s t_prime_unorm = glms_vec3_sub(tangent, glms_vec3_scale(geo_normal, proj));
|
||||
|
||||
// Matrix in cglm is column-major, not row-major
|
||||
mat3s tbn =
|
||||
// SAFETY: If tangent is parallel to normal, t_prime is zero.
|
||||
// Fallback to original tangent or arbitrary axis to avoid NaN.
|
||||
if (glms_vec3_norm2(t_prime_unorm) < FLT_EPSILON)
|
||||
{
|
||||
t_prime.x, t_prime.y, t_prime.z,
|
||||
b_prime.x, b_prime.y, b_prime.z,
|
||||
geo_normal.x, geo_normal.y, geo_normal.z
|
||||
};
|
||||
t_prime_unorm = tangent;
|
||||
// If tangent was also bad, pick an arbitrary axis
|
||||
if (glms_vec3_norm2(t_prime_unorm) < FLT_EPSILON)
|
||||
{
|
||||
create_orthonormal_basis(geo_normal, &t_prime_unorm, &tangent); // Recycle variable
|
||||
}
|
||||
}
|
||||
vec3s t_prime = glms_vec3_normalize(t_prime_unorm);
|
||||
|
||||
// 3. Calculate Bitangent
|
||||
vec3s b_prime = glms_vec3_cross(geo_normal, t_prime);
|
||||
|
||||
// 4. Apply Tangent Handedness (w)
|
||||
// NOTE: Check if tangent W component is stored/passed correctly.
|
||||
// If not sure, assuming 1.0 is safer than calculating it from cross products of unnormalized vectors.
|
||||
float w = (glms_vec3_dot(glms_vec3_cross(geo_normal, tangent), b_prime) < 0.0f) ? -1.0f : 1.0f;
|
||||
b_prime = glms_vec3_scale(b_prime, w);
|
||||
|
||||
mat3s tbn = {t_prime.x, t_prime.y, t_prime.z, b_prime.x, b_prime.y, b_prime.z, geo_normal.x, geo_normal.y, geo_normal.z};
|
||||
|
||||
// 5. Transform and Re-normalize
|
||||
return glms_vec3_normalize(glms_mat3_mulv(tbn, normal));
|
||||
}
|
||||
|
||||
@@ -104,10 +120,10 @@ float pdf_blinn_phong_lobe(vec3s normal, vec3s wi, vec3s wo, float roughness)
|
||||
return pdf_spec;
|
||||
}
|
||||
|
||||
vec3s sample_cosine_weighted_hemisphere_z_angular(float angular, uint32_t index, uint32_t d1, uint32_t d2)
|
||||
vec3s sample_cosine_weighted_hemisphere_z_angular(float angular, uint32_t index, uint16_t d1, uint16_t d2, uint32_t scramble)
|
||||
{
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
float r1 = sobol_sample_scrambled(index, d1, scramble);
|
||||
float r2 = sobol_sample_scrambled(index, d2, scramble);
|
||||
|
||||
float phi = 2.0f * PI * r1;
|
||||
|
||||
@@ -128,10 +144,10 @@ vec3s sample_cosine_weighted_hemisphere_z_angular(float angular, uint32_t index,
|
||||
|
||||
// Function to generate a direction with cosine weighting around (0, 0, 1)
|
||||
// This is the local coordinate sample.
|
||||
vec3s sample_cosine_weighted_hemisphere_z(uint32_t index, uint32_t d1, uint32_t d2)
|
||||
vec3s sample_cosine_weighted_hemisphere_z(uint32_t index, uint16_t d1, uint16_t d2, uint32_t scramble)
|
||||
{
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
float r1 = sobol_sample_scrambled(index, d1, scramble);
|
||||
float r2 = sobol_sample_scrambled(index, d2, scramble);
|
||||
|
||||
float r = sqrtf(r1);
|
||||
float phi = 2.0f * PI * r2;
|
||||
@@ -155,20 +171,20 @@ void create_orthonormal_basis(vec3s direction, vec3s* u, vec3s* v)
|
||||
vec3s a;
|
||||
if (fabsf(direction.x) > 0.9f)
|
||||
{
|
||||
a = (vec3s){{0.0f, 1.0f, 0.0f}}; // Use y-axis
|
||||
a = (vec3s){0.0f, 1.0f, 0.0f}; // Use y-axis
|
||||
}
|
||||
else
|
||||
{
|
||||
a = (vec3s){{1.0f, 0.0f, 0.0f}}; // Use x-axis
|
||||
a = (vec3s){1.0f, 0.0f, 0.0f}; // Use x-axis
|
||||
}
|
||||
|
||||
*u = glms_vec3_normalize(glms_vec3_cross(a, direction));
|
||||
*v = glms_vec3_normalize(glms_vec3_cross(direction, *u));
|
||||
}
|
||||
|
||||
vec3s random_cosine_direction_angular(vec3s direction, float angular, uint32_t index, uint32_t d1, uint32_t d2)
|
||||
vec3s random_cosine_direction_angular(vec3s direction, float angular, uint32_t index, uint32_t d1, uint32_t d2, uint32_t scramble)
|
||||
{
|
||||
vec3s local_dir = sample_cosine_weighted_hemisphere_z_angular(angular, index, d1, d2);
|
||||
vec3s local_dir = sample_cosine_weighted_hemisphere_z_angular(angular, index, d1, d2, scramble);
|
||||
|
||||
vec3s u, v;
|
||||
create_orthonormal_basis(direction, &u, &v);
|
||||
@@ -184,9 +200,9 @@ vec3s random_cosine_direction_angular(vec3s direction, float angular, uint32_t i
|
||||
|
||||
// Samples a direction from the hemisphere oriented along 'normal'
|
||||
// with a cosine-weighted distribution.
|
||||
vec3s random_cosine_direction(vec3s direction, uint32_t index, uint32_t d1, uint32_t d2)
|
||||
vec3s random_cosine_direction(vec3s direction, uint32_t index, uint32_t d1, uint32_t d2, uint32_t scramble)
|
||||
{
|
||||
vec3s local_dir = sample_cosine_weighted_hemisphere_z(index, d1, d2);
|
||||
vec3s local_dir = sample_cosine_weighted_hemisphere_z(index, d1, d2, scramble);
|
||||
|
||||
vec3s u, v;
|
||||
create_orthonormal_basis(direction, &u, &v);
|
||||
@@ -199,10 +215,10 @@ vec3s random_cosine_direction(vec3s direction, uint32_t index, uint32_t d1, uint
|
||||
return world_dir;
|
||||
}
|
||||
|
||||
vec3s random_uniform_cdf_direction(vec3s direction, uint32_t index, uint32_t d1, uint32_t d2)
|
||||
vec3s random_uniform_cdf_direction(vec3s direction, uint32_t index, uint16_t d1, uint16_t d2, uint32_t scramble)
|
||||
{
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
float r1 = sobol_sample_scrambled(index, d1, scramble);
|
||||
float r2 = sobol_sample_scrambled(index, d2, scramble);
|
||||
|
||||
float phi = 2.0f * PI * r1;
|
||||
float cos_theta = 1.0f - r2 * 2.0f;
|
||||
@@ -223,10 +239,10 @@ vec3s random_uniform_cdf_direction(vec3s direction, uint32_t index, uint32_t d1,
|
||||
return world_dir;
|
||||
}
|
||||
|
||||
vec3s random_uniform_cdf_direction_angular(vec3s direction, uint32_t index, float angular, uint32_t d1, uint32_t d2)
|
||||
vec3s random_uniform_cdf_direction_angular(vec3s direction, uint32_t index, float angular, uint16_t d1, uint16_t d2, uint32_t scramble)
|
||||
{
|
||||
float r1 = sobol_sample(index, d1);
|
||||
float r2 = sobol_sample(index, d2);
|
||||
float r1 = sobol_sample_scrambled(index, d1, scramble);
|
||||
float r2 = sobol_sample_scrambled(index, d2, scramble);
|
||||
|
||||
float cos_alpha = cosf(angular);
|
||||
float cos_theta = 1.0f - r1 * (1.0f - cos_alpha);
|
||||
@@ -249,11 +265,10 @@ vec3s random_uniform_cdf_direction_angular(vec3s direction, uint32_t index, floa
|
||||
return world_dir;
|
||||
}
|
||||
|
||||
|
||||
// Must use this function to weight any nee light contribution before accumulate.
|
||||
vec3s weight_nee_light(vec3s bsdf, vec3s light, float pdf_bsdf, float pdf_sky)
|
||||
{
|
||||
light = glms_vec3_mul(bsdf, light);
|
||||
float weight = power_heuristic(pdf_sky, pdf_bsdf);
|
||||
return glms_vec3_scale(light, weight);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#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)
|
||||
@@ -9,7 +10,9 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
vec3s throughput = glms_vec3_one();
|
||||
|
||||
ray_t active_ray = ray;
|
||||
float pdf_bsdf = 1.0f;
|
||||
|
||||
// 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)
|
||||
@@ -25,11 +28,26 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
.textures = &scene->textures,
|
||||
};
|
||||
path_output sky_output = evaluate_bsdf_sky(&scene->lights, &light_context, throughput, sample_index);
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_output.direct_lighting, 0.0f));
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
const material_t* hit_material = &scene->materials.buffer[scene->triangles.buffer[closest_hit.triangle_id].material_id];
|
||||
uint8_t material_id = scene->triangles.buffer[closest_hit.triangle_id].material_id;
|
||||
const material_t* hit_material = &scene->materials.buffer[material_id];
|
||||
shading_context_t shading_context =
|
||||
{
|
||||
.camera_position = scene->camera.position,
|
||||
@@ -52,10 +70,25 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
};
|
||||
|
||||
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));
|
||||
pdf_bsdf = material_output.pdf;
|
||||
|
||||
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)
|
||||
|
||||
@@ -52,7 +52,7 @@ static inline float sobol_uint_to_float(uint32_t x)
|
||||
return x * 2.3283064365386963e-10f; // 1/2^32
|
||||
}
|
||||
|
||||
float sobol_sample(uint32_t index, uint32_t dimension)
|
||||
float sobol_sample(uint32_t index, uint16_t dimension)
|
||||
{
|
||||
// return random_float();
|
||||
if (dimension >= SOBOL_DIMENSIONS)
|
||||
@@ -71,3 +71,25 @@ float sobol_sample(uint32_t index, uint32_t dimension)
|
||||
|
||||
return sobol_uint_to_float(result);
|
||||
}
|
||||
|
||||
float sobol_sample_scrambled(uint32_t index, uint16_t dimension, uint32_t scramble)
|
||||
{
|
||||
if (dimension >= SOBOL_DIMENSIONS)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
uint32_t result = 0;
|
||||
for (int i = 0; i < SOBOL_BITS; i++)
|
||||
{
|
||||
if (sobol_get_bit(index, i))
|
||||
{
|
||||
result ^= sobol_direction_vectors[dimension][i];
|
||||
}
|
||||
}
|
||||
|
||||
// Apply XOR scrambling to decorrelate pixels
|
||||
result ^= scramble;
|
||||
|
||||
return sobol_uint_to_float(result);
|
||||
}
|
||||
Reference in New Issue
Block a user