Fixed cdf and added Standard Lit
This commit is contained in:
@@ -15,10 +15,11 @@ path_output evaluate_bsdf_directional(directional_light_t light, const light_sha
|
||||
|
||||
float angular_radius = glm_rad(light.angular_diameter * 0.5f);
|
||||
|
||||
uint32_t scramble = hash_position(context->position);
|
||||
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
|
||||
uint16_t d2 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_V);
|
||||
|
||||
vec3s wi = random_uniform_cdf_direction_angular(light.direction, sample_index, angular_radius, d1, d2);
|
||||
vec3s wi = random_uniform_cdf_direction_angular(light.direction, sample_index, angular_radius, d1, d2, scramble);
|
||||
|
||||
float n_dot_l = glms_vec3_dot(context->normal, wi);
|
||||
if (n_dot_l <= 0.0f)
|
||||
@@ -41,6 +42,6 @@ path_output evaluate_bsdf_directional(directional_light_t light, const light_sha
|
||||
|
||||
output.direct_lighting = glms_vec3_mul(light_radiance, light_contribute);
|
||||
output.wi = wi;
|
||||
output.state = PS_NORMAL;
|
||||
output.state = PS_SUCCESS;
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "Lighting/SkyLight.h"
|
||||
#include "Common.h"
|
||||
#include <stdio.h>
|
||||
|
||||
// Constant Sky
|
||||
|
||||
@@ -42,10 +43,11 @@ path_output evaluate_bsdf_const_sky(const void* data, const light_shading_contex
|
||||
return output;
|
||||
}
|
||||
|
||||
uint32_t scramble = hash_position(context->position);
|
||||
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
|
||||
uint16_t d2 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_V);
|
||||
|
||||
vec3s wi = random_uniform_cdf_direction(context->normal, sample_index, d1, d2);
|
||||
vec3s wi = random_uniform_cdf_direction(context->normal, sample_index, d1, d2, scramble);
|
||||
|
||||
ray_t shadow_ray = ray_create(offset_ray_origin(context->position, context->normal, context->wo), wi);
|
||||
|
||||
@@ -60,11 +62,10 @@ path_output evaluate_bsdf_const_sky(const void* data, const light_shading_contex
|
||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), cos_theta / pdf);
|
||||
|
||||
output.wi = wi;
|
||||
output.state = PS_NORMAL;
|
||||
output.state = PS_SUCCESS;
|
||||
return output;
|
||||
}
|
||||
|
||||
|
||||
static uint32_t lower_bound_float(const float* arr, uint32_t n, float target)
|
||||
{
|
||||
uint32_t low = 0;
|
||||
@@ -87,7 +88,7 @@ static uint32_t lower_bound_float(const float* arr, uint32_t n, float target)
|
||||
|
||||
// HDR Sky
|
||||
|
||||
sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_entity_t hdri_entity, float intensity)
|
||||
sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_handle_t hdri_entity, float intensity)
|
||||
{
|
||||
sky_light_t light = {
|
||||
.data_size = sizeof(hdr_sky_data_t),
|
||||
@@ -106,6 +107,9 @@ sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_ent
|
||||
.height = hdri->height,
|
||||
.texture = hdri_entity,
|
||||
.intensity = intensity,
|
||||
.pdf_uv_mass = NULL,
|
||||
.cdf_x = NULL,
|
||||
.cdf_y_transposed = NULL,
|
||||
};
|
||||
|
||||
size_t size_hw = (size_t)data.height * data.width; // height * width elements
|
||||
@@ -118,15 +122,6 @@ sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_ent
|
||||
float* intermediate_buffer = (float*)malloc(sizeof(float) * intermediate_buffer_size);
|
||||
if (intermediate_buffer == NULL)
|
||||
{
|
||||
free(light.data);
|
||||
return light;
|
||||
}
|
||||
|
||||
vec3s* cache = (vec3s*)malloc(sizeof(vec3s) * size_hw);
|
||||
if (cache == NULL)
|
||||
{
|
||||
free(light.data);
|
||||
free(intermediate_buffer);
|
||||
return light;
|
||||
}
|
||||
|
||||
@@ -141,16 +136,17 @@ sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_ent
|
||||
float lumSum = 0.0f;
|
||||
for (uint32_t i = 0; i < data.height; ++i)
|
||||
{
|
||||
// Note: If equiareal mapping requires an area element factor like sin(theta),
|
||||
// calculate it here and multiply the luminance before summing and normalizing.
|
||||
// float v = (i + 0.5f) / (float)data.height;
|
||||
// float sin_theta = sinf(v * PI);
|
||||
// Calculate the sine of the polar angle theta
|
||||
// Map row i to V [0,1], then to theta [0, PI]
|
||||
float v = (i + 0.5f) / (float)data.height;
|
||||
float theta = v * PI;
|
||||
float sin_theta = sinf(theta);
|
||||
|
||||
for (uint32_t j = 0; j < data.width; ++j)
|
||||
{
|
||||
vec2s uv = {(float)j / (float)data.width, (float)i / (float)data.height};
|
||||
vec2s uv = {((float)j + 0.5f) / (float)data.width, ((float)i + 0.5f) / (float)data.height};
|
||||
vec4s pixel = texture_get_pixel(hdri, uv, 0);
|
||||
float lum = luminance(glms_vec3(pixel)); // * sin_theta;
|
||||
float lum = luminance(glms_vec3(pixel)) * sin_theta;
|
||||
|
||||
p_xy_original_pdf[i * data.width + j] = lum;
|
||||
lumSum += lum; // Sum of luminances (or lum * area_element)
|
||||
@@ -163,6 +159,15 @@ sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_ent
|
||||
p_xy_original_pdf[i] *= inv_lumSum;
|
||||
}
|
||||
|
||||
// Keep a copy of the normalized discrete probability mass per texel for MIS queries by direction.
|
||||
float* pdf_uv_mass = (float*)malloc(sizeof(float) * size_hw);
|
||||
if (pdf_uv_mass == NULL)
|
||||
{
|
||||
free(intermediate_buffer);
|
||||
return light;
|
||||
}
|
||||
memcpy(pdf_uv_mass, p_xy_original_pdf, sizeof(float) * size_hw);
|
||||
|
||||
// --- Calculate Marginal PDF p(x) ---
|
||||
for (uint32_t j = 0; j < data.width; ++j)
|
||||
{
|
||||
@@ -220,67 +225,69 @@ sky_light_t sky_create_hdr_sky(const texture_collection_t* textures, texture_ent
|
||||
}
|
||||
}
|
||||
|
||||
// --- Precompute Samples using Inverse Transform Sampling ---
|
||||
// Populate the 'cache' array directly, which is now vec3s*
|
||||
// cache[i * width + j] represents the output pixel for input random numbers (i/height, j/width)
|
||||
for (uint32_t i = 0; i < data.height; ++i)
|
||||
{ // Corresponds to xi_1 (vertical random number)
|
||||
for (uint32_t j = 0; j < data.width; ++j)
|
||||
{
|
||||
// Input random numbers [0, 1)
|
||||
float xi_1 = i / (float)data.height;
|
||||
float xi_2 = j / (float)data.width;
|
||||
|
||||
// Sample x index using xi_1 and marginal CDF (cdf_x_buffer)
|
||||
uint32_t x_sample_idx = lower_bound_float(cdf_x_buffer, data.width, xi_1);
|
||||
x_sample_idx = (x_sample_idx == data.width) ? data.width - 1 : x_sample_idx;
|
||||
|
||||
// Sample y index using xi_2 and conditional CDF for x=x_sample_idx (cdf_y_transposed_buffer)
|
||||
// The conditional CDF for x_sample_idx is a 1D array starting at cdf_y_transposed_buffer[x_sample_idx * height]
|
||||
uint32_t y_sample_idx = lower_bound_float(cdf_y_transposed_buffer + x_sample_idx * data.height, data.height, xi_2);
|
||||
y_sample_idx = (y_sample_idx == data.height) ? data.height - 1 : y_sample_idx;
|
||||
|
||||
// Get the normalized PDF value at the sampled texture coordinates (x_sample_idx, y_sample_idx)
|
||||
// This is stored in p_xy_original_pdf (which holds the normalized p(x,y))
|
||||
float pdf_at_sample = p_xy_original_pdf[y_sample_idx * data.width + x_sample_idx];
|
||||
|
||||
// Store the sampled texture coordinates (normalized [0, 1)) and PDF in the cache (vec3s)
|
||||
int cache_idx = i * data.width + j;
|
||||
cache[cache_idx].x = (float)x_sample_idx / (float)data.width; // Sampled U
|
||||
cache[cache_idx].y = (float)y_sample_idx / (float)data.height; // Sampled V
|
||||
cache[cache_idx].z = pdf_at_sample; // Normalized PDF at (U, V)
|
||||
}
|
||||
// Persist CDF tables for correct runtime sampling (keeps wi/pdf consistent)
|
||||
float* cdf_x_persist = (float*)malloc(sizeof(float) * size_w);
|
||||
float* cdf_y_transposed_persist = (float*)malloc(sizeof(float) * size_wh);
|
||||
if (cdf_x_persist == NULL || cdf_y_transposed_persist == NULL)
|
||||
{
|
||||
free(cdf_x_persist);
|
||||
free(cdf_y_transposed_persist);
|
||||
free(pdf_uv_mass);
|
||||
free(intermediate_buffer);
|
||||
return light;
|
||||
}
|
||||
memcpy(cdf_x_persist, cdf_x_buffer, sizeof(float) * size_w);
|
||||
memcpy(cdf_y_transposed_persist, cdf_y_transposed_buffer, sizeof(float) * size_wh);
|
||||
|
||||
free(intermediate_buffer);
|
||||
|
||||
light.data = malloc(sizeof(hdr_sky_data_t));
|
||||
if (light.data == NULL)
|
||||
{
|
||||
free(cache);
|
||||
free(pdf_uv_mass);
|
||||
free(cdf_x_persist);
|
||||
free(cdf_y_transposed_persist);
|
||||
return light;
|
||||
}
|
||||
|
||||
data.total_weight = lumSum;
|
||||
data.cdf_cache = cache;
|
||||
data.pdf_uv_mass = pdf_uv_mass;
|
||||
data.cdf_x = cdf_x_persist;
|
||||
data.cdf_y_transposed = cdf_y_transposed_persist;
|
||||
|
||||
memcpy(light.data, &data, sizeof(hdr_sky_data_t));
|
||||
return light;
|
||||
}
|
||||
|
||||
static inline float hdr_pdf(const vec3s* cdf, vec3s v, uint32_t height, uint32_t width)
|
||||
static inline float hdr_pdf_from_uv_mass(float uv_mass, float v, uint32_t height, uint32_t width)
|
||||
{
|
||||
vec2s uv = direction_to_equirectangular(v);
|
||||
uint32_t x, y;
|
||||
uv_to_index(uv, width, height, &x, &y);
|
||||
float pdf = cdf[y * width + x].z;
|
||||
|
||||
float theta = uv.y * PI;
|
||||
float sin_theta = fmaxf(sinf(theta), FLT_EPSILON);
|
||||
// v in [0,1] maps to theta in [0, PI]
|
||||
float theta = v * PI;
|
||||
float sin_theta = fmaxf(sinf(theta), 1e-4f);
|
||||
|
||||
// Δω per texel for equirectangular map:
|
||||
// Δω = (2π/width) * (π/height) * sinθ = (2π^2 sinθ)/(W H)
|
||||
// pdf(ω) = uv_mass / Δω = uv_mass * (W H)/(2π^2 sinθ)
|
||||
float convert = (float)(height * width) / (2.0f * PI_TWO * sin_theta);
|
||||
float pdf = uv_mass * convert;
|
||||
|
||||
return pdf * convert;
|
||||
return fminf(pdf, 1e6f);
|
||||
}
|
||||
|
||||
static inline float hdr_sky_pdf_direction(const hdr_sky_data_t* sky_data, vec3s wi)
|
||||
{
|
||||
vec2s uv = direction_to_equirectangular(wi);
|
||||
|
||||
uint32_t x, y;
|
||||
uv_to_index(uv, sky_data->width, sky_data->height, &x, &y);
|
||||
|
||||
float mass = 0.0f;
|
||||
if (sky_data->pdf_uv_mass != NULL)
|
||||
{
|
||||
mass = sky_data->pdf_uv_mass[y * sky_data->width + x];
|
||||
}
|
||||
|
||||
return hdr_pdf_from_uv_mass(mass, uv.y, sky_data->height, sky_data->width);
|
||||
}
|
||||
|
||||
path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||
@@ -295,27 +302,37 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
|
||||
return output;
|
||||
}
|
||||
|
||||
// Last hit was no geometry, directly sample the sky
|
||||
if (context->bvh_tree == NULL)
|
||||
{
|
||||
vec2s uv = direction_to_equirectangular(context->wo);
|
||||
vec4s sky_light = texture_sample_lod(get_texture(context->textures, sky_data->texture), uv, 0);
|
||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(glms_vec3(sky_light), throughput), sky_data->intensity);
|
||||
output.pdf = 1.0f / (4.0f * PI);
|
||||
// Return the correct environment PDF for MIS when the BSDF-sampled ray escapes to the sky.
|
||||
output.pdf = hdr_sky_pdf_direction(sky_data, context->wo);
|
||||
return output;
|
||||
}
|
||||
|
||||
uint32_t i,j;
|
||||
// Should we also use sobol numbers for the sky sampling?
|
||||
uint32_t x_idx, y_idx;
|
||||
uint16_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_U);
|
||||
uint16_t d2 = sobol_get_dimension(context->bounce_depth, PRNG_LIGHT_V);
|
||||
float r1 = sobol_sample(sample_index, d1);
|
||||
float r2 = sobol_sample(sample_index, d2);
|
||||
//float r1 = random_float();
|
||||
//float r2 = random_float();
|
||||
|
||||
uv_to_index((vec2s){r1, r2}, sky_data->width, sky_data->height, &j, &i);
|
||||
vec3s cdf = sky_data->cdf_cache[i * sky_data->width + j];
|
||||
vec3s wi = equirectangular_to_direction(cdf.x,-cdf.y);
|
||||
if (sky_data->cdf_x == NULL || sky_data->cdf_y_transposed == NULL || sky_data->pdf_uv_mass == NULL)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
x_idx = lower_bound_float(sky_data->cdf_x, sky_data->width, r1);
|
||||
x_idx = (x_idx == sky_data->width) ? (sky_data->width - 1) : x_idx;
|
||||
|
||||
y_idx = lower_bound_float(sky_data->cdf_y_transposed + x_idx * sky_data->height, sky_data->height, r2);
|
||||
y_idx = (y_idx == sky_data->height) ? (sky_data->height - 1) : y_idx;
|
||||
|
||||
float u = ((float)x_idx + 0.5f) / (float)sky_data->width;
|
||||
float v = ((float)y_idx + 0.5f) / (float)sky_data->height;
|
||||
vec3s wi = equirectangular_to_direction(u, v);
|
||||
|
||||
float n_dot_l = glms_vec3_dot(wi, context->normal);
|
||||
if (n_dot_l <= 0.0f)
|
||||
@@ -331,24 +348,32 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
|
||||
return output;
|
||||
}
|
||||
|
||||
vec2s uv = direction_to_equirectangular(wi);
|
||||
|
||||
vec2s uv = (vec2s){u, v};
|
||||
const texture_t* hdri = get_texture(context->textures, sky_data->texture);
|
||||
vec4s pixel = texture_sample_lod(hdri, uv, 0);
|
||||
vec4s pixel = texture_get_pixel(hdri, uv, 0);
|
||||
vec3s sky_light = glms_vec3_scale(glms_vec3(pixel), sky_data->intensity);
|
||||
|
||||
float pdf = hdr_pdf(sky_data->cdf_cache, wi, sky_data->height, sky_data->width);
|
||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), n_dot_l / pdf);
|
||||
float mass = sky_data->pdf_uv_mass[y_idx * sky_data->width + x_idx];
|
||||
float pdf = hdr_pdf_from_uv_mass(mass, v, sky_data->height, sky_data->width);
|
||||
if (pdf < 1e-12f)
|
||||
{
|
||||
return output;
|
||||
}
|
||||
|
||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), n_dot_l / pdf);
|
||||
output.wi = wi;
|
||||
output.pdf = pdf;
|
||||
output.state = PS_NORMAL;
|
||||
output.state = PS_SUCCESS;
|
||||
return output;
|
||||
}
|
||||
|
||||
void hdr_sky_free(hdr_sky_data_t* data)
|
||||
{
|
||||
free(data->cdf_cache);
|
||||
free(data->pdf_uv_mass);
|
||||
free(data->cdf_x);
|
||||
free(data->cdf_y_transposed);
|
||||
|
||||
data->cdf_cache = NULL;
|
||||
data->pdf_uv_mass = NULL;
|
||||
data->cdf_x = NULL;
|
||||
data->cdf_y_transposed = NULL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user