Update project structure and improve performance

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.
This commit is contained in:
2025-04-21 15:56:19 +09:00
parent 1162575545
commit 6800810369
36 changed files with 1590 additions and 579 deletions

View File

@@ -1,68 +1,24 @@
#ifndef BSDF_H
#define BSDF_H
#include "Algorithm/Sobol.h"
#include "cglm/struct/vec3.h"
inline float power_heuristic(float pdf_a, float pdf_b)
{
float a2 = pdf_a * pdf_a;
float b2 = pdf_b * pdf_b;
return a2 / (a2 + b2);
}
float power_heuristic(float pdf_a, float pdf_b);
float roughness_to_blinn_phong_specular_exponent(float roughness);
vec3s fresnel_schlick_vec3(vec3s f0, float cos_theta);
inline float roughness_to_blinn_phong_specular_exponent(float roughness)
{
return glm_clamp(2.0f * 1.0f / (fmaxf(roughness * roughness, FLT_EPSILON)) - 2.0f, FLT_EPSILON, 1.0f / FLT_EPSILON);
}
// BSDF sampling functions
float pdf_cosine_weighted_hemisphere(vec3s normal, vec3s wi);
float pdf_blinn_phong_lobe(vec3s normal, vec3s wi, vec3s wo, float roughness);
inline vec3s fresnel_schlick_vec3(vec3s f0, float cos_theta)
{
float x = 1.0f - cos_theta;
float x5 = x * x * x * x * x;
return glms_vec3_adds(glms_vec3_scale(f0, (1.0f - x5)), x5);
}
vec3s sample_cosine_weighted_hemisphere_z_angular(float angular, uint32_t index, uint32_t d1, uint32_t d2);
vec3s sample_cosine_weighted_hemisphere_z(uint32_t index, uint32_t d1, uint32_t d2);
void create_orthonormal_basis(vec3s direction, vec3s* u, vec3s* v);
vec3s random_cosine_direction_angular(vec3s direction, float angular, uint32_t index, uint32_t d1, uint32_t d2);
vec3s random_cosine_direction(vec3s direction, uint32_t index, uint32_t d1, uint32_t d2);
inline float pdf_cosine_weighted_hemisphere(vec3s normal, vec3s wi)
{
return fmaxf(glms_vec3_dot(wi, normal), 0.0f) / (float)M_PI;
}
inline float pdf_blinn_phong_lobe(vec3s normal, vec3s wi, vec3s wo, float roughness)
{
// Check if wo and wi are on the same side of the surface normal geometry
if (glms_vec3_dot(wo, normal) <= 0.0f || glms_vec3_dot(wi, normal) <= 0.0f)
{
return 0.0f; // Cannot scatter from below horizon to above, or vice versa
}
// Calculate the half-vector h based on input wo and wi
vec3s wo_n = glms_vec3_normalize(wo); // Ensure normalized inputs if not guaranteed
vec3s wi_n = glms_vec3_normalize(wi);
vec3s h = glms_vec3_add(wo_n, wi_n);
float h_len_sq = glms_vec3_norm2(h);
if (h_len_sq < FLT_EPSILON)
{
return 0.0f; // wo and wi are opposite, highly unlikely for reflection
}
h = glms_vec3_scale(h, 1.0f / sqrtf(h_len_sq)); // Normalize h
// Calculate Blinn-Phong specular exponent
float specular_exponent = roughness_to_blinn_phong_specular_exponent(roughness);
// PDF of sampling h (Blinn-Phong distribution)
// D(h) = (specular_exponent + 1) / (2 * PI) * pow(max(0, dot(n, h)), specular_exponent)
float n_dot_h = fmaxf(0.0f, glms_vec3_dot(normal, h));
float pdf_h = (specular_exponent + 1.0f) / (2.0f * (float)M_PI) * powf(n_dot_h, specular_exponent);
// Jacobian of the transformation from h to wi
// jacobian = 1 / (4 * dot(wo, h))
float wo_dot_h = fmaxf(FLT_EPSILON, glms_vec3_dot(wo_n, h)); // Use normalized wo, ensure > 0
float jacobian = 1.0f / (4.0f * wo_dot_h);
// PDF of sampling wi is pdf(h) * jacobian
float pdf_spec = pdf_h * jacobian;
return pdf_spec;
}
vec3s random_uniform_cdf_direction(vec3s direction, uint32_t index, uint32_t d1, uint32_t d2);
vec3s random_uniform_cdf_direction_angular(vec3s direction, uint32_t index, float angular, uint32_t d1, uint32_t d2);
#endif // BSDF_H

34
header/Algorithm/BVH.h Normal file
View File

@@ -0,0 +1,34 @@
#ifndef BVH_H
#define BVH_H
#include "Geometry/AABB.h"
#include "Geometry/Triangle.h"
#define SAH_BIN_COUNT 8 // Number of bins for SAH
#define SAH_TRAVERSAL_COST 1.0f // Cost of traversing a node
#define SAH_INTERSECTION_COST 10.0f // Cost of intersecting a triangle
typedef struct
{
aabb_t bounds;
uint64_t start_index;
uint64_t primitive_count;
uint64_t left_child_offset;
uint64_t right_child_offset;
} bvh_node_t;
typedef struct
{
uint64_t node_count; // Current number of nodes used
uint64_t node_capacity; // Allocated capacity for nodes
uint64_t primitive_count; // Total number of primitives (should match triangles->count)
bvh_node_t* nodes;
uint64_t* primitive_indices; // Buffer of indices mapping to the original triangle_collection_t buffer
const triangle_collection_t* triangles; // Pointer to the original triangle data (read-only access)
} bvh_tree_t;
bool bvh_tree_init(bvh_tree_t* bvh_tree, const triangle_collection_t* triangles);
bool bvh_tree_build(bvh_tree_t* bvh_tree);
void bvh_tree_free(bvh_tree_t* bvh_tree);
#endif // BVH_H

View File

@@ -6,6 +6,6 @@
#include "Geometry/Triangle.h"
#include "Rendering/Scene.h"
vec3s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, int depth);
vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t depth);
#endif // PATH_TRACING_H

View File

@@ -3,6 +3,7 @@
#include "Common.h"
#include "Geometry/Triangle.h"
#include "Rendering/Scene.h"
typedef struct
{
@@ -19,8 +20,11 @@ typedef struct
bool hit;
} hit_result_t;
hit_result_t ray_intersect( triangle_t triangle, ray_t ray);
hit_result_t ray_intersect_closest(const triangle_collection_t* triangles, ray_t ray);
hit_result_t ray_intersect_any(const triangle_collection_t* triangles, ray_t ray);
hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle);
bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_out);
void ray_intersect_bvh(const ray_t ray, const bvh_node_t* bvh_nodes,
const uint64_t* primitive_indices, const triangle_collection_t* all_triangles, uint64_t node_index,
float* closest_out, hit_result_t* best_hit_out);
hit_result_t ray_intersect_scene(ray_t ray, const scene_t* scene);
#endif // RAY_INTERSECTION_H

View File

@@ -1,39 +1,82 @@
#ifndef SOBOL_H
#define SOBOL_H
#include "Common.h"
#include <stdint.h>
#define SOBOL_BITS 32
// NOTE: May need more dimensions for later
#define SOBOL_DIMENSIONS 8
// NOTE: May need more dimensions for later, most of commercial renderer use more than 1000 dimensions
#define SOBOL_DIMENSIONS 36
// #define COS_WEIGHTED_HEMISPHERE_R1_INDEX 0
// #define COS_WEIGHTED_HEMISPHERE_R2_INDEX 1
typedef enum
{
PRNG_FILTER_U = 0,
PRNG_FILTER_V = 1,
PRNG_LENS_U = 2,
PRNG_LENS_V = 3,
PRNG_BASE_NUM = 4,
PRNG_BSDF_U = 0,
PRNG_BSDF_V = 1,
PRNG_BSDF = 2,
PRNG_LIGHT = 3,
PRNG_LIGHT_U = 4,
PRNG_LIGHT_V = 5,
PRNG_LIGHT_F = 6,
PRNG_TERMINATE = 7,
PRNG_BOUNCE_NUM = 8
} sampling_dimension_t;
static uint32_t sobol_direction_vectors[SOBOL_DIMENSIONS][SOBOL_BITS];
// Precomputed table: s, a, and m_i's
// https://web.maths.unsw.edu.au/~fkuo/sobol/
static int s_vals[SOBOL_DIMENSIONS - 1] = {1, 2, 3, 3, 4, 4, 5};
static int a_vals[SOBOL_DIMENSIONS - 1] = {0, 1, 1, 2, 1, 4, 2};
static int m_vals[SOBOL_DIMENSIONS - 1][5] = {
{1}, // dim 2
{1, 3}, // dim 3
{1, 3, 1}, // dim 4
{1, 1, 1}, // dim 5
{1, 1, 3, 3}, // dim 6
{1 ,1, 5, 13}, // dim 7
{1, 1, 5, 5, 17} // dim 8
static int s_vals[SOBOL_DIMENSIONS - 1] = {1, 2, 3, 3, 4, 4, 5, 5, 5, 5, 5, 5, 6, 6, 6, 6,
6, 6, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7};
static uint32_t a_vals[SOBOL_DIMENSIONS - 1] = {0, 1, 1, 2, 1, 4, 2, 4, 7, 11, 13, 14, 1, 13, 16, 19,
22, 25, 1, 4, 7, 8, 14, 19, 21, 28, 31, 32, 37, 41, 42, 50, 55, 56, 59};
static uint32_t m_vals[SOBOL_DIMENSIONS - 1][7] = {
{1},
{1, 3},
{1, 3, 1},
{1, 1, 1},
{1, 1, 3, 3},
{1 ,1, 5, 13},
{1, 1, 5, 5, 17},
{1, 1, 5, 5, 5},
{1, 1, 7, 11, 19},
{1, 1, 5, 1, 1},
{1, 1, 1, 3, 11},
{1, 3, 5, 5, 31},
{1, 3, 3, 9, 7, 49},
{1, 1, 1, 15, 21, 21},
{1, 3, 1, 13, 27, 49},
{1, 1, 1, 15, 7, 5},
{1, 3, 1, 15, 13, 25},
{1, 1, 5, 5, 19, 61},
{1, 3, 7, 11, 23, 15, 103},
{1, 3, 7, 13, 13, 15, 69},
{1, 1, 3, 13, 7, 35, 63},
{1, 3, 5, 9, 1, 25, 53},
{1, 3, 1, 13, 9, 35, 107},
{1, 3, 1, 5, 27, 61, 31},
{1, 1, 5, 11, 19, 41, 61},
{1, 3, 5, 3, 3, 13, 69},
{1, 1, 7, 13, 1, 19, 1},
{1, 3, 7, 5, 13, 19, 59},
{1, 1, 3, 9, 25, 29, 41},
{1, 3, 5, 13, 23, 1, 55},
{1, 3, 7, 3, 13, 59, 17},
{1, 3, 1, 3, 5, 53, 69},
{1, 1, 5, 5, 23, 33, 13},
{1, 1, 7, 7, 1, 61, 123},
{1, 1, 7, 9, 13, 61, 49}
};
typedef struct
{
uint32_t index;
uint32_t dimension;
} sobol_state_t;
void sobol_init();
uint16_t sobol_get_dimension(uint16_t bounce, sampling_dimension_t operation_type);
float sobol_sample(uint32_t index, uint32_t dimension);
float sobol_next(sobol_state_t* state);
#endif // SOBOL_H