Change project structure;
Added new c# binding;
This commit is contained in:
106
native/source/Material/Material.c
Normal file
106
native/source/Material/Material.c
Normal file
@@ -0,0 +1,106 @@
|
||||
#include "Material/Material.h"
|
||||
#include <string.h>
|
||||
|
||||
bool material_collection_init(uint8_t size, material_collection_t* materials)
|
||||
{
|
||||
if (size > 254)
|
||||
{
|
||||
size = 254;
|
||||
}
|
||||
|
||||
material_collection_t temp = {0};
|
||||
temp.buffer = (material_t*)malloc(size * sizeof(material_t));
|
||||
if (temp.buffer == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
temp.size = (uint8_t)size;
|
||||
temp.count = 0;
|
||||
*materials = temp;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void material_collection_resize(material_collection_t* materials, size_t size)
|
||||
{
|
||||
if (size == INVALID_MATERIAL_ID)
|
||||
{
|
||||
size = INVALID_MATERIAL_ID - 1;
|
||||
}
|
||||
|
||||
if (size == materials->size)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
material_t* temp = (material_t*)realloc(materials->buffer, size * sizeof(material_t));
|
||||
if (temp != NULL)
|
||||
{
|
||||
materials->buffer = temp;
|
||||
materials->size = (uint8_t)size;
|
||||
}
|
||||
}
|
||||
|
||||
void material_collection_free(material_collection_t* materials)
|
||||
{
|
||||
if (materials->buffer != NULL)
|
||||
{
|
||||
for (uint8_t i = 0; i < materials->count; i++)
|
||||
{
|
||||
free(materials->buffer[i].properties);
|
||||
materials->buffer[i].properties = NULL;
|
||||
}
|
||||
|
||||
free(materials->buffer);
|
||||
materials->buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
material_handle_t material_create(const void* properties, size_t properties_size, material_render_loop_f render_loop, material_render_aov_f render_aov, material_collection_t* collection)
|
||||
{
|
||||
material_t material = {0};
|
||||
|
||||
if (collection->count >= collection->size)
|
||||
{
|
||||
material_collection_resize(collection, collection->size * 2);
|
||||
}
|
||||
|
||||
void* temp = malloc(properties_size);
|
||||
if (temp == NULL)
|
||||
{
|
||||
return invalid_material_entity();
|
||||
}
|
||||
|
||||
memcpy(temp, properties, properties_size);
|
||||
material.properties = temp;
|
||||
material.properties_size = properties_size;
|
||||
|
||||
material.render_loop = render_loop;
|
||||
material.render_aov = render_aov;
|
||||
|
||||
material_handle_t entity = {.id = collection->count};
|
||||
|
||||
collection->buffer[collection->count] = material;
|
||||
collection->count++;
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
// void material_free(material_entity_t entity, material_collection_t* collection)
|
||||
// {
|
||||
// if (entity.id >= collection->count || !is_material_entity_valid(entity))
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// free(collection->buffer[entity.id].properties);
|
||||
// collection->buffer[entity.id].properties = NULL;
|
||||
//
|
||||
// for (uint8_t i = entity.id; i < collection->count - 1; i++)
|
||||
// {
|
||||
// collection->buffer[i] = collection->buffer[i + 1];
|
||||
// }
|
||||
//
|
||||
// collection->count--;
|
||||
// }
|
||||
388
native/source/Material/StandardLit.c
Normal file
388
native/source/Material/StandardLit.c
Normal file
@@ -0,0 +1,388 @@
|
||||
#include "Material/StandardLit.h"
|
||||
#include "Algorithm/BSDF.h"
|
||||
#include "Algorithm/MicrofacetGGX.h"
|
||||
#include "Algorithm/GGXMultiScatter.h"
|
||||
#include "Lighting/LightEvaluation.h"
|
||||
|
||||
static float oren_nayar_eval(vec3s l, vec3s v, vec3s n, float roughness, float n_dot_l, float n_dot_v)
|
||||
{
|
||||
// Full Qualitative Oren-Nayar
|
||||
float sigma2 = roughness * roughness;
|
||||
float A = 1.0f - (sigma2 / (2.0f * (sigma2 + 0.33f)));
|
||||
float B = 0.45f * sigma2 / (sigma2 + 0.09f);
|
||||
|
||||
// Cosine of azimuth difference (phi)
|
||||
// Project V and L onto the tangent plane
|
||||
vec3s v_plane = glms_vec3_normalize(glms_vec3_sub(v, glms_vec3_scale(n, n_dot_v)));
|
||||
vec3s l_plane = glms_vec3_normalize(glms_vec3_sub(l, glms_vec3_scale(n, n_dot_l)));
|
||||
float cos_phi_diff = fmaxf(0.0f, glms_vec3_dot(v_plane, l_plane));
|
||||
|
||||
// Sine and Tangent terms
|
||||
// alpha = max(theta_l, theta_v), beta = min(theta_l, theta_v)
|
||||
// We use sin/tan relationships: sin(x) = sqrt(1-cos^2(x))
|
||||
float sin_theta_l = sqrtf(fmaxf(0.0f, 1.0f - n_dot_l * n_dot_l));
|
||||
float sin_theta_v = sqrtf(fmaxf(0.0f, 1.0f - n_dot_v * n_dot_v));
|
||||
|
||||
float sin_alpha, tan_beta;
|
||||
if (n_dot_v > n_dot_l)
|
||||
{
|
||||
sin_alpha = sin_theta_l;
|
||||
tan_beta = sin_theta_v / fmaxf(n_dot_v, 0.0001f);
|
||||
}
|
||||
else
|
||||
{
|
||||
sin_alpha = sin_theta_v;
|
||||
tan_beta = sin_theta_l / fmaxf(n_dot_l, 0.0001f);
|
||||
}
|
||||
|
||||
return (A + B * cos_phi_diff * sin_alpha * tan_beta) * INV_PI;
|
||||
}
|
||||
|
||||
static void get_surface_data(const shading_context_t* context, const standard_lit_properties_t* properties, standard_lit_surface_data_t* data_out)
|
||||
{
|
||||
// Use the ray cone width (footprint) instead of simple distance for mip selection
|
||||
float distance = glms_vec3_distance(context->camera_position, context->position);
|
||||
vec3s view = context->camera_direction;
|
||||
|
||||
// Fetch geometry data for LOD calculation
|
||||
const triangle_t* triangle = &context->triangles->buffer[context->triangle_id];
|
||||
texture_sample_context_t sample_context =
|
||||
{
|
||||
.view_direction = view,
|
||||
.normal = context->normal,
|
||||
.edge1 = glms_vec3_sub(triangle->vertices[1].position, triangle->vertices[0].position),
|
||||
.edge2 = glms_vec3_sub(triangle->vertices[2].position, triangle->vertices[0].position),
|
||||
.uv1 = glms_vec2_sub(triangle->vertices[1].uv, triangle->vertices[0].uv),
|
||||
.uv2 = glms_vec2_sub(triangle->vertices[2].uv, triangle->vertices[0].uv),
|
||||
.ray_width = context->cone_width,
|
||||
.distance = distance
|
||||
};
|
||||
|
||||
data_out->albedo = properties->albedo;
|
||||
const texture_t* albedo_texture = get_texture(context->textures, properties->albedo_texture);
|
||||
if (albedo_texture != NULL && albedo_texture->data != NULL)
|
||||
{
|
||||
data_out->albedo = glms_vec3_mul(data_out->albedo, glms_vec3(texture_sample(albedo_texture, &sample_context, context->uv)));
|
||||
}
|
||||
|
||||
data_out->normal = context->normal;
|
||||
const texture_t* normal_texture = get_texture(context->textures, properties->normal_texture);
|
||||
if (normal_texture != NULL && normal_texture->data != NULL)
|
||||
{
|
||||
vec3s normal_sample = glms_vec3(texture_sample(normal_texture, &sample_context, context->uv));
|
||||
normal_sample = normal_unpack(normal_sample);
|
||||
data_out->normal = normal_ts_to_ws(normal_sample, context->normal, context->tangent);
|
||||
}
|
||||
|
||||
data_out->diffuse_roughness = properties->diffuse_roughness;
|
||||
|
||||
data_out->roughness = properties->roughness;
|
||||
const texture_t* roughness_texture = get_texture(context->textures, properties->roughness_texture);
|
||||
if (roughness_texture != NULL && roughness_texture->data != NULL)
|
||||
{
|
||||
data_out->roughness = data_out->roughness * texture_sample(roughness_texture, &sample_context, context->uv).x;
|
||||
}
|
||||
|
||||
data_out->roughness = fmaxf(data_out->roughness, 0.001f);
|
||||
|
||||
data_out->metallic = properties->metallic;
|
||||
const texture_t* metallic_texture = get_texture(context->textures, properties->metallic_texture);
|
||||
if (metallic_texture != NULL && metallic_texture->data != NULL)
|
||||
{
|
||||
data_out->metallic = data_out->metallic * texture_sample(metallic_texture, &sample_context, context->uv).x;
|
||||
}
|
||||
}
|
||||
|
||||
static vec3s evaluate_bsdf_standard_lit(const shading_context_t* context, standard_lit_surface_data_t* surface_data, vec3s wi)
|
||||
{
|
||||
vec3s n = surface_data->normal;
|
||||
vec3s v = glms_vec3_negate(context->wo);
|
||||
vec3s l = wi;
|
||||
|
||||
float n_dot_l = fmaxf(glms_vec3_dot(n, l), 0.0f);
|
||||
float n_dot_v = fmaxf(glms_vec3_dot(n, v), 0.0f);
|
||||
if (n_dot_l <= 0.0f || n_dot_v <= 0.0f || glms_vec3_dot(context->normal, l) <= 0.0f)
|
||||
{
|
||||
return glms_vec3_zero();
|
||||
}
|
||||
|
||||
vec3s h = glms_vec3_normalize(glms_vec3_add(v, l));
|
||||
float n_dot_h = fmaxf(glms_vec3_dot(n, h), 0.0f);
|
||||
float v_dot_h = fmaxf(glms_vec3_dot(v, h), 0.0f);
|
||||
|
||||
// Fresnel
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, surface_data->albedo, surface_data->metallic);
|
||||
vec3s F = fresnel_schlick_vec3(f0, v_dot_h);
|
||||
|
||||
// Specular (GGX)
|
||||
float D = ggx_distribution(n_dot_h, surface_data->roughness);
|
||||
float G = ggx_g_smith(n_dot_v, n_dot_l, surface_data->roughness);
|
||||
vec3s spec = glms_vec3_scale(glms_vec3_mul(F, (vec3s){D * G, D * G, D * G}), 1.0f / fmaxf(4.0f * n_dot_v * n_dot_l, 0.0001f));
|
||||
|
||||
// Multi-scatter GGX (broad lobe)
|
||||
vec3s ms = ggx_multi_scatter_lambert(f0, n_dot_v, n_dot_l, surface_data->roughness);
|
||||
|
||||
// Diffuse (Oren-Nayar)
|
||||
// Using (1 - F) here can make rough dielectrics look too dark because our specular is single-scatter GGX
|
||||
// (missing multi-scattering energy compensation). A stable approximation is (1 - F0_diel).
|
||||
float kd_scale = (1.0f - surface_data->metallic) * (1.0f - DIELECTRIC_REFLECTIVE_F0);
|
||||
vec3s kD = glms_vec3_scale(glms_vec3_one(), kd_scale);
|
||||
float on_val = oren_nayar_eval(l, v, n, surface_data->diffuse_roughness, n_dot_l, n_dot_v);
|
||||
vec3s diff = glms_vec3_scale(glms_vec3_mul(surface_data->albedo, kD), on_val);
|
||||
|
||||
return glms_vec3_add(glms_vec3_add(diff, spec), ms);
|
||||
}
|
||||
|
||||
static float sample_bsdf_pdf(const standard_lit_surface_data_t* surface_data, vec3s V, vec3s L)
|
||||
{
|
||||
float n_dot_l = fmaxf(glms_vec3_dot(surface_data->normal, L), 0.0f);
|
||||
if (n_dot_l <= 0.0f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
// Lobe probabilities (single-scatter spec vs cosine)
|
||||
// We allocate some cosine probability for multi-scatter spec, especially for rough metals.
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_F0, surface_data->albedo, surface_data->metallic);
|
||||
float n_dot_v = fmaxf(glms_vec3_dot(surface_data->normal, V), 0.0001f);
|
||||
vec3s F_est = fresnel_schlick_vec3(f0, n_dot_v);
|
||||
|
||||
float spec_strength = luminance(F_est);
|
||||
float diff_strength = (1.0f - surface_data->metallic) * (1.0f - spec_strength);
|
||||
|
||||
float Eo = ggx_ms_E(n_dot_v, surface_data->roughness);
|
||||
float w_ss = spec_strength * Eo;
|
||||
float w_cos = spec_strength * (1.0f - Eo) + diff_strength;
|
||||
|
||||
float sum_w = w_ss + w_cos;
|
||||
if (sum_w < FLT_EPSILON)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
w_ss /= sum_w;
|
||||
w_cos /= sum_w;
|
||||
|
||||
// Specular PDF (GGX VNDF reflection)
|
||||
// GGX VNDF reflection pdf:
|
||||
// p_h(h) = D(h) * G1(v) * (N·H)/(N·V)
|
||||
// p_w(wi) = p_h(h) / (4 * (V·H))
|
||||
vec3s H = glms_vec3_normalize(glms_vec3_add(L, V));
|
||||
float v_dot_h = glms_vec3_dot(V, H);
|
||||
if (v_dot_h <= 1e-6f)
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
float n_dot_h = fmaxf(glms_vec3_dot(surface_data->normal, H), 0.0f);
|
||||
float D = ggx_distribution(n_dot_h, surface_data->roughness);
|
||||
float G1v = ggx_g1(n_dot_v, surface_data->roughness);
|
||||
|
||||
float pdf_h = (D * G1v * n_dot_h) / fmaxf(n_dot_v, 1e-6f);
|
||||
float pdf_spec = pdf_h / (4.0f * fmaxf(v_dot_h, 1e-6f));
|
||||
|
||||
// Cosine PDF (used for diffuse + multi-scatter)
|
||||
float pdf_cos = n_dot_l * INV_PI;
|
||||
|
||||
return w_ss * pdf_spec + w_cos * pdf_cos;
|
||||
}
|
||||
|
||||
path_output standard_lit_render_loop(const standard_lit_properties_t* properties, const shading_context_t* context)
|
||||
{
|
||||
standard_lit_surface_data_t surface_data; // Assuming you reuse your struct
|
||||
get_surface_data(context, properties, &surface_data);
|
||||
|
||||
// Keep shading normal in the same hemisphere as the geometric normal to avoid invalid transport.
|
||||
if (glms_vec3_dot(surface_data.normal, context->normal) < 0.0f)
|
||||
{
|
||||
surface_data.normal = glms_vec3_negate(surface_data.normal);
|
||||
}
|
||||
|
||||
path_output output = {0};
|
||||
vec3s V = glms_vec3_negate(context->wo);
|
||||
// Keep shading normal in the same hemisphere as the geometric normal.
|
||||
if (glms_vec3_dot(surface_data.normal, context->normal) < 0.0f)
|
||||
{
|
||||
surface_data.normal = glms_vec3_negate(surface_data.normal);
|
||||
}
|
||||
|
||||
// If shading normal faces away from the view, fall back to geometric normal (prevents VNDF/pdf blow-ups).
|
||||
if (glms_vec3_dot(surface_data.normal, V) <= 0.0f)
|
||||
{
|
||||
surface_data.normal = context->normal;
|
||||
}
|
||||
|
||||
float n_dot_v = fmaxf(glms_vec3_dot(surface_data.normal, V), 0.0001f);
|
||||
|
||||
// Ensure LUT is ready (thread-safe, one-time).
|
||||
ggx_ms_init_lut_once();
|
||||
|
||||
light_shading_context_t light_context = {
|
||||
.position = context->position,
|
||||
.normal = surface_data.normal,
|
||||
.geometric_normal = context->normal,
|
||||
.tangent = context->tangent,
|
||||
.uv = context->uv,
|
||||
.wo = context->wo,
|
||||
|
||||
.bounce_depth = context->bounce_depth,
|
||||
|
||||
.scene = context->scene,
|
||||
|
||||
.bvh_tree = context->bvh_tree,
|
||||
.textures = context->textures,
|
||||
};
|
||||
|
||||
uint32_t scramble = hash_position(context->position);
|
||||
|
||||
// Running the light loop.
|
||||
// TODO: Implementing other light types.
|
||||
for (uint32_t i = 0; i < context->lights->directional_light_count; i++)
|
||||
{
|
||||
path_output light_output = evaluate_bsdf_directional(context->lights->directional_lights[i], &light_context, context->throughput, context->sample_index);
|
||||
if (light_output.state == PS_SUCCESS)
|
||||
{
|
||||
vec3s bsdf_dir_light = evaluate_bsdf_standard_lit(context, &surface_data, light_output.wi);
|
||||
float pdf_bsdf = sample_bsdf_pdf(&surface_data, V, light_output.wi);
|
||||
vec3s light_contribute = weight_nee_light(bsdf_dir_light, light_output.direct_lighting, pdf_bsdf, light_output.pdf);
|
||||
output.direct_lighting = glms_vec3_add(output.direct_lighting, light_contribute);
|
||||
}
|
||||
}
|
||||
|
||||
// Sky
|
||||
path_output sky_output = evaluate_bsdf_sky(context->lights, &light_context, context->throughput, context->sample_index);
|
||||
if (sky_output.state == PS_SUCCESS)
|
||||
{
|
||||
vec3s bsdf_sky_light = evaluate_bsdf_standard_lit(context, &surface_data, sky_output.wi);
|
||||
float pdf_bsdf = sample_bsdf_pdf(&surface_data, V, sky_output.wi);
|
||||
vec3s sky_light = weight_nee_light(bsdf_sky_light, sky_output.direct_lighting, pdf_bsdf, sky_output.pdf);
|
||||
output.direct_lighting = glms_vec3_add(output.direct_lighting, sky_light);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------
|
||||
// Indirect Lighting (Sampling Next Ray)
|
||||
// ----------------------------------------------------
|
||||
|
||||
// 1. Choose lobe: VNDF single-scatter spec vs cosine (diffuse + multi-scatter spec)
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_F0, surface_data.albedo, surface_data.metallic);
|
||||
vec3s F_est = fresnel_schlick_vec3(f0, n_dot_v);
|
||||
|
||||
float spec_strength = luminance(F_est);
|
||||
float diff_strength = (1.0f - surface_data.metallic) * (1.0f - spec_strength);
|
||||
|
||||
float Eo = ggx_ms_E(n_dot_v, surface_data.roughness);
|
||||
float w_ss = spec_strength * Eo;
|
||||
float w_cos = spec_strength * (1.0f - Eo) + diff_strength;
|
||||
|
||||
float sum_w = w_ss + w_cos;
|
||||
if (sum_w < FLT_EPSILON)
|
||||
{
|
||||
output.state = PS_TERMINATE;
|
||||
return output;
|
||||
}
|
||||
|
||||
w_ss /= sum_w;
|
||||
w_cos /= sum_w;
|
||||
|
||||
vec3s f_eval = glms_vec3_zero();
|
||||
float n_dot_l = 0.0f;
|
||||
float r_lobe = sobol_sample_scrambled(context->sample_index, sobol_get_dimension(context->bounce_depth, PRNG_BSDF), scramble);
|
||||
bool is_specular = (r_lobe < w_ss);
|
||||
|
||||
if (is_specular)
|
||||
{
|
||||
// Sample GGX
|
||||
uint32_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_BSDF_U);
|
||||
uint32_t d2 = sobol_get_dimension(context->bounce_depth, PRNG_BSDF_V);
|
||||
|
||||
float u1 = sobol_sample_scrambled(context->sample_index, d1, scramble);
|
||||
float u2 = sobol_sample_scrambled(context->sample_index, d2, scramble);
|
||||
|
||||
vec3s H = ggx_sample_vndf(surface_data.normal, V, surface_data.roughness, u1, u2);
|
||||
output.wi = glms_vec3_reflect(context->wo, H); // reflect(-V, H) -> V is wo inverted
|
||||
|
||||
if (glms_vec3_dot(output.wi, surface_data.normal) <= 0.0f || glms_vec3_dot(output.wi, context->normal) <= 0.0f)
|
||||
{
|
||||
output.state = PS_TERMINATE;
|
||||
return output;
|
||||
}
|
||||
|
||||
// Recalculate dots
|
||||
n_dot_l = fmaxf(glms_vec3_dot(surface_data.normal, output.wi), 0.0001f);
|
||||
vec3s H_new = glms_vec3_normalize(glms_vec3_add(output.wi, V));
|
||||
float n_dot_h = fmaxf(glms_vec3_dot(surface_data.normal, H_new), 0.0001f);
|
||||
float v_dot_h = fmaxf(glms_vec3_dot(V, H_new), 0.0001f);
|
||||
|
||||
// Evaluate BSDF
|
||||
float D = ggx_distribution(n_dot_h, surface_data.roughness);
|
||||
float G1v = ggx_g1(n_dot_v, surface_data.roughness);
|
||||
float G = ggx_g_smith(n_dot_v, n_dot_l, surface_data.roughness);
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_F0, surface_data.albedo, surface_data.metallic);
|
||||
vec3s F = fresnel_schlick_vec3(f0, v_dot_h);
|
||||
|
||||
vec3s spec_f = glms_vec3_scale(glms_vec3_mul(F, (vec3s){D * G, D * G, D * G}),
|
||||
1.0f / fmaxf(4.0f * n_dot_v * n_dot_l, 1e-6f));
|
||||
|
||||
f_eval = spec_f;
|
||||
|
||||
// Propagate spread angle for ray cones
|
||||
// Heuristic: spread increases with roughness
|
||||
output.spread_angle = context->spread_angle + surface_data.roughness * QUARTER_PI;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Sample Cosine hemisphere (Diffuse + Multi-scatter spec)
|
||||
// Note: We use cosine sampling for Oren-Nayar and the broad MS term.
|
||||
uint32_t d1 = sobol_get_dimension(context->bounce_depth, PRNG_BSDF_U);
|
||||
uint32_t d2 = sobol_get_dimension(context->bounce_depth, PRNG_BSDF_V);
|
||||
output.wi = random_cosine_direction(surface_data.normal, context->sample_index, d1, d2, scramble);
|
||||
|
||||
n_dot_l = fmaxf(glms_vec3_dot(surface_data.normal, output.wi), 0.0001f);
|
||||
if (glms_vec3_dot(output.wi, context->normal) <= 0.0f)
|
||||
{
|
||||
output.state = PS_TERMINATE;
|
||||
return output;
|
||||
}
|
||||
|
||||
float kd_scale = (1.0f - surface_data.metallic) * (1.0f - DIELECTRIC_REFLECTIVE_F0);
|
||||
vec3s kD = glms_vec3_scale(glms_vec3_one(), kd_scale);
|
||||
float on = oren_nayar_eval(output.wi, V, surface_data.normal, surface_data.diffuse_roughness, n_dot_l, n_dot_v);
|
||||
|
||||
|
||||
// Diffuse bounce significantly increases spread (effectively resets or becomes very wide)
|
||||
output.spread_angle = context->spread_angle + 0.5f;
|
||||
vec3s diff_f = glms_vec3_scale(glms_vec3_mul(surface_data.albedo, kD), on);
|
||||
|
||||
// Multi-scatter GGX term (broad): sampled here with cosine.
|
||||
vec3s ms_f = ggx_multi_scatter_lambert(f0, n_dot_v, n_dot_l, surface_data.roughness);
|
||||
|
||||
// Throughput multiplier: (f * NoL) / pdf
|
||||
vec3s f_sum = glms_vec3_add(diff_f, ms_f);
|
||||
f_eval = f_sum;
|
||||
}
|
||||
|
||||
output.pdf = sample_bsdf_pdf(&surface_data, V, output.wi);
|
||||
if (output.pdf < 1e-12f)
|
||||
{
|
||||
output.state = PS_TERMINATE;
|
||||
return output;
|
||||
}
|
||||
|
||||
// Throughput multiplier must be: (f * NoL) / pdf_total (mixture PDF)
|
||||
output.bsdf = glms_vec3_scale(f_eval, n_dot_l / output.pdf);
|
||||
output.state = PS_SUCCESS;
|
||||
return output;
|
||||
}
|
||||
|
||||
void standard_lit_render_aov(const standard_lit_properties_t* properties, const shading_context_t* context, aov_output_t* aov_output)
|
||||
{
|
||||
standard_lit_surface_data_t surface_data; // Assuming you reuse your struct
|
||||
get_surface_data(context, properties, &surface_data);
|
||||
// Keep shading normal in the same hemisphere as the geometric normal to avoid invalid transport.
|
||||
if (glms_vec3_dot(surface_data.normal, context->normal) < 0.0f)
|
||||
{
|
||||
surface_data.normal = glms_vec3_negate(surface_data.normal);
|
||||
}
|
||||
|
||||
aov_output->albedo = glms_vec4(surface_data.albedo, 1.0f);
|
||||
vec3s n_ws = glms_vec3_normalize(surface_data.normal);
|
||||
aov_output->normal = (vec4s){n_ws.x * 0.5f + 0.5f, n_ws.y * 0.5f + 0.5f, n_ws.z * 0.5f + 0.5f, 1.0f};
|
||||
}
|
||||
Reference in New Issue
Block a user