#ifndef COMMON_H #define COMMON_H #include "Algorithm/Sobol.h" #include "cglm/struct/vec4.h" #include "cglm/struct/vec3.h" #include #define COLOR_CLAMP(color) (unsigned char)fminf(fmaxf(color, 0.0f), 255.0f) inline float random_float() { return (float)rand() / (float)RAND_MAX; } inline vec3s sample_cosine_weighted_hemisphere_z_angular(sobol_state_t* sobol_state, float angular) { float r1 = sobol_next(sobol_state); float r2 = sobol_next(sobol_state); float phi = 2.0f * (float)M_PI * r1; float cos_theta = 1.0f - r2 * (1.0f - cosf(angular)); float sin_theta = sqrtf(1.0f - cos_theta * cos_theta); // Convert spherical to Cartesian coordinates (relative to +Z axis) float x = cosf(phi) * sin_theta; float y = sinf(phi) * sin_theta; float z = cos_theta; // Create the vector in local coordinates (Z is up) vec3s local_dir = {{x, y, z}}; return local_dir; } // Function to generate a direction with cosine weighting around (0, 0, 1) // This is the local coordinate sample. inline vec3s sample_cosine_weighted_hemisphere_z(sobol_state_t* sobol_state) { // float r1 = random_float(); // float r2 = random_float(); float r1 = sobol_next(sobol_state); float r2 = sobol_next(sobol_state); // Convert uniform random numbers to spherical coordinates for cosine weighting // phi = angle around Z-axis (azimuthal) // theta = angle from Z-axis (polar) float phi = 2.0f * (float)M_PI * r1; // cos(theta) = sqrt(r2), so z = sqrt(r2) // sin(theta) = sqrt(1 - cos^2(theta)) = sqrt(1 - r2) float sqrt_r2 = sqrtf(r2); // This is z float sqrt_1_minus_r2 = sqrtf(1.0f - r2); // This is sin(theta) // Convert spherical to Cartesian coordinates (relative to +Z axis) float x = cosf(phi) * sqrt_1_minus_r2; float y = sinf(phi) * sqrt_1_minus_r2; float z = sqrt_r2; // Direct mapping for cosine weighting // Create the vector in local coordinates (Z is up) vec3s local_dir = {{x, y, z}}; return local_dir; // Already normalized by construction } // Function to create an orthonormal basis (coordinate system) from a single vector (normal) // w will be aligned with normal, u and v will be perpendicular. inline void create_orthonormal_basis(const vec3s normal, vec3s* u, vec3s* v) { // Choose a vector 'a' that is not parallel to 'normal' vec3s a; if (fabsf(normal.x) > 0.9f) { // If w is close to x-axis a = (vec3s){{0.0f, 1.0f, 0.0f}}; // Use y-axis } else { a = (vec3s){{1.0f, 0.0f, 0.0f}}; // Use x-axis } // Calculate u = normalize(cross(a, w)) - this is the new 'x-axis' *u = glms_vec3_normalize(glms_vec3_cross(a, normal)); // Calculate v = cross(w, u) - this is the new 'y-axis' // Since w and u are orthonormal, v will also be normalized. *v = glms_vec3_cross(normal, *u); } inline vec3s random_cosine_direction_angular(const vec3s normal, float angular, sobol_state_t* sobol_state) { vec3s local_dir = sample_cosine_weighted_hemisphere_z_angular(sobol_state, angular); vec3s u, v; create_orthonormal_basis(normal, &u, &v); vec3s term_u = glms_vec3_scale(u, local_dir.x); vec3s term_v = glms_vec3_scale(v, local_dir.y); vec3s term_w = glms_vec3_scale(normal, local_dir.z); vec3s world_dir = glms_vec3_add(glms_vec3_add(term_u, term_v), term_w); return world_dir; } // Samples a direction from the hemisphere oriented along 'normal' // with a cosine-weighted distribution. inline vec3s random_cosine_direction(const vec3s normal, sobol_state_t* sobol_state) { // 1. Sample a direction in local coordinates (hemisphere oriented along +Z) vec3s local_dir = sample_cosine_weighted_hemisphere_z(sobol_state); // 2. Create an orthonormal basis aligned with the surface normal vec3s u, v; create_orthonormal_basis(normal, &u, &v); // 3. Transform the local direction to world coordinates using the basis // world_dir = u * local_dir.x + v * local_dir.y + w * local_dir.z vec3s term_u = glms_vec3_scale(u, local_dir.x); vec3s term_v = glms_vec3_scale(v, local_dir.y); vec3s term_w = glms_vec3_scale(normal, local_dir.z); vec3s world_dir = glms_vec3_add(glms_vec3_add(term_u, term_v), term_w); // The result should be normalized due to the sampling method and basis transform. // Is it necessary to be that safe? // return glms_vec3_normalize(world_dir); return world_dir; } #endif // COMMON_H