Refactor the codebase and add aov support.
This commit is contained in:
@@ -7,7 +7,9 @@
|
|||||||
#include "Rendering/AOV.h"
|
#include "Rendering/AOV.h"
|
||||||
#include "Rendering/Scene.h"
|
#include "Rendering/Scene.h"
|
||||||
|
|
||||||
vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth);
|
// Unified AOV integrator:
|
||||||
void render_aov(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth, aov_output_t* aov_output);
|
// - If only surface AOVs are requested, executes a single-hit fast path.
|
||||||
|
// - If beauty/direct/indirect are requested, runs the full path tracer and buckets contributions.
|
||||||
|
void path_trace_aov(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth, aov_flags_t aov_flags, aov_output_t* out);
|
||||||
|
|
||||||
#endif // PATH_TRACING_H
|
#endif // PATH_TRACING_H
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
#define PI 3.141592653589793f
|
#define PI 3.141592653589793f
|
||||||
#define TWO_PI 6.283185307179586f // 2 * PI
|
#define TWO_PI 6.283185307179586f // 2 * PI
|
||||||
#define HALF_PI 1.570796326794896f // PI / 2
|
#define HALF_PI 1.570796326794896f // PI / 2
|
||||||
|
#define QUARTER_PI 0.785398163397448f // PI / 4
|
||||||
#define PI_TWO 9.869604401089358f // PI^2
|
#define PI_TWO 9.869604401089358f // PI^2
|
||||||
|
|
||||||
#define INV_PI 0.318309886183790f // 1 / PI
|
#define INV_PI 0.318309886183790f // 1 / PI
|
||||||
@@ -34,8 +35,8 @@ typedef struct
|
|||||||
vec3s direct_lighting;
|
vec3s direct_lighting;
|
||||||
vec3s bsdf;
|
vec3s bsdf;
|
||||||
float pdf;
|
float pdf;
|
||||||
path_state state;
|
|
||||||
float spread_angle;
|
float spread_angle;
|
||||||
|
path_state state;
|
||||||
} path_output;
|
} path_output;
|
||||||
|
|
||||||
inline float random_float()
|
inline float random_float()
|
||||||
|
|||||||
@@ -31,13 +31,12 @@ typedef struct
|
|||||||
|
|
||||||
|
|
||||||
path_output standard_lit_render_loop(const standard_lit_properties_t* properties, const shading_context_t* context);
|
path_output standard_lit_render_loop(const standard_lit_properties_t* properties, const shading_context_t* context);
|
||||||
float sample_bsdf_pdf(const standard_lit_surface_data_t* surface_data, vec3s V, vec3s L);
|
void standard_lit_render_aov(const standard_lit_properties_t* properties, const shading_context_t* context, aov_output_t* aov_output);
|
||||||
void standard_lit_render_aov(const shading_context_t* properties, const shading_context_t* context, aov_output_t* aov_output);
|
|
||||||
|
|
||||||
|
|
||||||
inline material_handle_t material_create_standard_lit_default(const standard_lit_properties_t* properties, material_collection_t* collection)
|
inline material_handle_t material_create_standard_lit_default(const standard_lit_properties_t* properties, material_collection_t* collection)
|
||||||
{
|
{
|
||||||
return material_create(properties, sizeof(standard_lit_properties_t), (material_render_loop_f)standard_lit_render_loop, NULL, collection);
|
return material_create(properties, sizeof(standard_lit_properties_t), (material_render_loop_f)standard_lit_render_loop, (material_render_aov_f)standard_lit_render_aov, collection);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // STANDARd_LIT_H
|
#endif // STANDARd_LIT_H
|
||||||
|
|||||||
@@ -2,8 +2,9 @@
|
|||||||
#define AOV_H
|
#define AOV_H
|
||||||
|
|
||||||
#include "cglm/struct/vec4.h"
|
#include "cglm/struct/vec4.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#define MAX_AOV_TARGET 5
|
#define MAX_AOV_TARGET 7
|
||||||
|
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@@ -13,6 +14,9 @@ typedef enum
|
|||||||
AOV_NORMAL = 1 << 2,
|
AOV_NORMAL = 1 << 2,
|
||||||
AOV_DEPTH = 1 << 3,
|
AOV_DEPTH = 1 << 3,
|
||||||
AOV_POSITION = 1 << 4,
|
AOV_POSITION = 1 << 4,
|
||||||
|
// Lighting AOVs (require integration / multiple samples)
|
||||||
|
AOV_DIRECT = 1 << 5,
|
||||||
|
AOV_INDIRECT = 1 << 6,
|
||||||
} aov_flags_t;
|
} aov_flags_t;
|
||||||
|
|
||||||
typedef enum
|
typedef enum
|
||||||
@@ -22,6 +26,8 @@ typedef enum
|
|||||||
AOV_NORMAL_INDEX = 2,
|
AOV_NORMAL_INDEX = 2,
|
||||||
AOV_DEPTH_INDEX = 3,
|
AOV_DEPTH_INDEX = 3,
|
||||||
AOV_POSITION_INDEX = 4,
|
AOV_POSITION_INDEX = 4,
|
||||||
|
AOV_DIRECT_INDEX = 5,
|
||||||
|
AOV_INDIRECT_INDEX = 6,
|
||||||
} aov_index_t;
|
} aov_index_t;
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
@@ -31,6 +37,9 @@ typedef struct
|
|||||||
vec4s normal;
|
vec4s normal;
|
||||||
vec4s position;
|
vec4s position;
|
||||||
|
|
||||||
|
vec4s direct;
|
||||||
|
vec4s indirect;
|
||||||
|
|
||||||
float depth;
|
float depth;
|
||||||
} aov_output_t;
|
} aov_output_t;
|
||||||
|
|
||||||
@@ -40,7 +49,22 @@ inline void accumulate_aov(aov_output_t* aov, const aov_output_t* new_aov, float
|
|||||||
aov->albedo = glms_vec4_add(aov->albedo, glms_vec4_scale(new_aov->albedo, inv_sample_count));
|
aov->albedo = glms_vec4_add(aov->albedo, glms_vec4_scale(new_aov->albedo, inv_sample_count));
|
||||||
aov->normal = glms_vec4_add(aov->normal, glms_vec4_scale(new_aov->normal, inv_sample_count));
|
aov->normal = glms_vec4_add(aov->normal, glms_vec4_scale(new_aov->normal, inv_sample_count));
|
||||||
aov->position = glms_vec4_add(aov->position, glms_vec4_scale(new_aov->position, inv_sample_count));
|
aov->position = glms_vec4_add(aov->position, glms_vec4_scale(new_aov->position, inv_sample_count));
|
||||||
|
|
||||||
|
aov->direct = glms_vec4_add(aov->direct, glms_vec4_scale(new_aov->direct, inv_sample_count));
|
||||||
|
aov->indirect = glms_vec4_add(aov->indirect, glms_vec4_scale(new_aov->indirect, inv_sample_count));
|
||||||
|
|
||||||
|
// Depth: keep nearest valid depth across samples; treat 0 as "unset".
|
||||||
|
if (new_aov->depth > 0.0f)
|
||||||
|
{
|
||||||
|
if (aov->depth <= 0.0f)
|
||||||
|
{
|
||||||
|
aov->depth = new_aov->depth;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
aov->depth = fminf(aov->depth, new_aov->depth);
|
aov->depth = fminf(aov->depth, new_aov->depth);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif // AOV_H
|
#endif // AOV_H
|
||||||
|
|||||||
@@ -3,15 +3,157 @@
|
|||||||
#include "Lighting/LightEvaluation.h"
|
#include "Lighting/LightEvaluation.h"
|
||||||
#include "Algorithm/BSDF.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
|
static inline bool aov_wants_lighting(aov_flags_t flags)
|
||||||
vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth)
|
{
|
||||||
|
return (flags & (AOV_BEAUTY | AOV_DIRECT | AOV_INDIRECT)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool aov_wants_surface(aov_flags_t flags)
|
||||||
|
{
|
||||||
|
return (flags & (AOV_AlBEDO | AOV_NORMAL | AOV_DEPTH | AOV_POSITION)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline vec4s vec4_add_rgb(vec4s base, vec3s rgb)
|
||||||
|
{
|
||||||
|
return glms_vec4_add(base, glms_vec4(rgb, 0.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void aov_init_output(aov_output_t* out)
|
||||||
|
{
|
||||||
|
*out = (aov_output_t){0};
|
||||||
|
out->beauty = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
out->direct = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
out->indirect = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline shading_context_t make_shading_context(const scene_t* scene,
|
||||||
|
vec3s wo,
|
||||||
|
vec3s throughput,
|
||||||
|
uint32_t sample_index,
|
||||||
|
uint32_t bounce_depth,
|
||||||
|
const hit_result_t* hit,
|
||||||
|
float cone_width,
|
||||||
|
float spread_angle)
|
||||||
|
{
|
||||||
|
return (shading_context_t){
|
||||||
|
.camera_position = scene->camera.position,
|
||||||
|
.camera_direction = glms_vec3_normalize(glms_vec3_sub(hit->point, scene->camera.position)),
|
||||||
|
|
||||||
|
.position = hit->point,
|
||||||
|
.normal = hit->normal,
|
||||||
|
.tangent = hit->tangent,
|
||||||
|
.uv = hit->uv,
|
||||||
|
.wo = wo,
|
||||||
|
.throughput = throughput,
|
||||||
|
|
||||||
|
.sample_index = sample_index,
|
||||||
|
.bounce_depth = bounce_depth,
|
||||||
|
|
||||||
|
.bvh_tree = &scene->bvh_tree,
|
||||||
|
.triangles = &scene->triangles,
|
||||||
|
.lights = &scene->lights,
|
||||||
|
.textures = &scene->textures,
|
||||||
|
|
||||||
|
.triangle_id = hit->triangle_id,
|
||||||
|
.cone_width = cone_width,
|
||||||
|
.spread_angle = spread_angle,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trace_surface_aovs_only(const scene_t* scene,
|
||||||
|
ray_t ray,
|
||||||
|
uint32_t sample_index,
|
||||||
|
aov_flags_t aov_flags,
|
||||||
|
aov_output_t* out)
|
||||||
|
{
|
||||||
|
hit_result_t closest_hit = ray_intersect_scene_closest(&ray, scene);
|
||||||
|
if (!closest_hit.hit)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const material_t* hit_material = &scene->materials.buffer[scene->triangles.buffer[closest_hit.triangle_id].material_id];
|
||||||
|
|
||||||
|
float cone_width = ray.width + closest_hit.distance * ray.spread_angle;
|
||||||
|
shading_context_t shading_context = make_shading_context(scene,
|
||||||
|
ray.direction,
|
||||||
|
glms_vec3_one(),
|
||||||
|
sample_index,
|
||||||
|
0,
|
||||||
|
&closest_hit,
|
||||||
|
cone_width,
|
||||||
|
ray.spread_angle);
|
||||||
|
|
||||||
|
render_material_aov(hit_material, &shading_context, out);
|
||||||
|
|
||||||
|
if (aov_flags & AOV_POSITION)
|
||||||
|
{
|
||||||
|
out->position = glms_vec4(closest_hit.point, 1.0f);
|
||||||
|
}
|
||||||
|
if (aov_flags & AOV_DEPTH)
|
||||||
|
{
|
||||||
|
out->depth = closest_hit.distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void aov_accumulate_env(aov_output_t* out, aov_flags_t flags, vec3s env_contrib, uint16_t depth)
|
||||||
|
{
|
||||||
|
if (flags & AOV_BEAUTY)
|
||||||
|
{
|
||||||
|
out->beauty = vec4_add_rgb(out->beauty, env_contrib);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Environment visible from camera => direct; reached after bounces => indirect.
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
if (flags & AOV_DIRECT)
|
||||||
|
{
|
||||||
|
out->direct = vec4_add_rgb(out->direct, env_contrib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (flags & AOV_INDIRECT)
|
||||||
|
{
|
||||||
|
out->indirect = vec4_add_rgb(out->indirect, env_contrib);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void aov_accumulate_nee(aov_output_t* out, aov_flags_t flags, vec3s L, uint16_t depth)
|
||||||
|
{
|
||||||
|
// Next-event estimation at camera-visible vertex => direct.
|
||||||
|
// NEE at later vertices is reached via bounces => indirect.
|
||||||
|
if (depth == 0)
|
||||||
|
{
|
||||||
|
if (flags & AOV_DIRECT)
|
||||||
|
{
|
||||||
|
out->direct = vec4_add_rgb(out->direct, L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (flags & AOV_INDIRECT)
|
||||||
|
{
|
||||||
|
out->indirect = vec4_add_rgb(out->indirect, L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags & AOV_BEAUTY)
|
||||||
|
{
|
||||||
|
out->beauty = vec4_add_rgb(out->beauty, L);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void trace_lighting_aovs(const scene_t* scene,
|
||||||
|
ray_t ray,
|
||||||
|
uint32_t sample_index,
|
||||||
|
uint16_t max_depth,
|
||||||
|
aov_flags_t aov_flags,
|
||||||
|
aov_output_t* out)
|
||||||
{
|
{
|
||||||
vec4s accumulated_color = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
|
||||||
vec3s throughput = glms_vec3_one();
|
vec3s throughput = glms_vec3_one();
|
||||||
|
|
||||||
ray_t active_ray = ray;
|
ray_t active_ray = ray;
|
||||||
|
|
||||||
// PDF of the direction that generated the current ray segment (used for MIS on env hits).
|
|
||||||
float last_bsdf_pdf = 0.0f;
|
float last_bsdf_pdf = 0.0f;
|
||||||
|
|
||||||
uint16_t depth = 0;
|
uint16_t depth = 0;
|
||||||
@@ -21,7 +163,6 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
|||||||
|
|
||||||
if (!closest_hit.hit)
|
if (!closest_hit.hit)
|
||||||
{
|
{
|
||||||
// Set bvh to null indicate that the ray is not hit anything
|
|
||||||
light_shading_context_t light_context =
|
light_shading_context_t light_context =
|
||||||
{
|
{
|
||||||
.wo = active_ray.direction,
|
.wo = active_ray.direction,
|
||||||
@@ -30,7 +171,6 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
|||||||
};
|
};
|
||||||
path_output sky_output = evaluate_bsdf_sky(&scene->lights, &light_context, throughput, sample_index);
|
path_output sky_output = evaluate_bsdf_sky(&scene->lights, &light_context, throughput, sample_index);
|
||||||
|
|
||||||
// MIS for BSDF-sampled environment hit (for depth==0 camera ray, use weight 1).
|
|
||||||
float w = 1.0f;
|
float w = 1.0f;
|
||||||
if (depth > 0)
|
if (depth > 0)
|
||||||
{
|
{
|
||||||
@@ -43,52 +183,48 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec3s env_contrib = glms_vec3_scale(sky_output.direct_lighting, w);
|
vec3s env_contrib = glms_vec3_scale(sky_output.direct_lighting, w);
|
||||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(env_contrib, 0.0f));
|
aov_accumulate_env(out, aov_flags, env_contrib, depth);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t material_id = 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];
|
const material_t* hit_material = &scene->materials.buffer[material_id];
|
||||||
|
|
||||||
// Calculate ray cone width at the hit point
|
|
||||||
float current_cone_width = active_ray.width + closest_hit.distance * active_ray.spread_angle;
|
float current_cone_width = active_ray.width + closest_hit.distance * active_ray.spread_angle;
|
||||||
|
shading_context_t shading_context = make_shading_context(scene,
|
||||||
|
active_ray.direction,
|
||||||
|
throughput,
|
||||||
|
sample_index,
|
||||||
|
depth,
|
||||||
|
&closest_hit,
|
||||||
|
current_cone_width,
|
||||||
|
active_ray.spread_angle);
|
||||||
|
|
||||||
shading_context_t shading_context =
|
// First-hit surface AOVs are still cheap; record them if requested.
|
||||||
|
if (depth == 0 && aov_wants_surface(aov_flags))
|
||||||
{
|
{
|
||||||
.camera_position = scene->camera.position,
|
render_material_aov(hit_material, &shading_context, out);
|
||||||
.camera_direction = glms_vec3_normalize(glms_vec3_sub(closest_hit.point, scene->camera.position)),
|
if (aov_flags & AOV_POSITION)
|
||||||
|
{
|
||||||
.position = closest_hit.point,
|
out->position = glms_vec4(closest_hit.point, 1.0f);
|
||||||
.normal = closest_hit.normal,
|
}
|
||||||
.tangent = closest_hit.tangent,
|
if (aov_flags & AOV_DEPTH)
|
||||||
.uv = closest_hit.uv,
|
{
|
||||||
.wo = active_ray.direction,
|
out->depth = closest_hit.distance;
|
||||||
.throughput = throughput,
|
}
|
||||||
|
}
|
||||||
.sample_index = sample_index,
|
|
||||||
.bounce_depth = depth,
|
|
||||||
|
|
||||||
.bvh_tree = &scene->bvh_tree,
|
|
||||||
.triangles = &scene->triangles,
|
|
||||||
.lights = &scene->lights,
|
|
||||||
.textures = &scene->textures,
|
|
||||||
|
|
||||||
.triangle_id = closest_hit.triangle_id,
|
|
||||||
.cone_width = current_cone_width,
|
|
||||||
.spread_angle = active_ray.spread_angle,
|
|
||||||
};
|
|
||||||
|
|
||||||
path_output material_output = render_material(hit_material, &shading_context);
|
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))
|
if (glms_vec3_isinf(material_output.direct_lighting) || glms_vec3_isnan(material_output.direct_lighting))
|
||||||
{
|
{
|
||||||
goto end_path_trace;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(material_output.direct_lighting, 0.0f));
|
aov_accumulate_nee(out, aov_flags, material_output.direct_lighting, depth);
|
||||||
|
|
||||||
if (material_output.pdf < FLT_EPSILON)
|
if (material_output.pdf < FLT_EPSILON)
|
||||||
{
|
{
|
||||||
goto end_path_trace;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
last_bsdf_pdf = material_output.pdf;
|
last_bsdf_pdf = material_output.pdf;
|
||||||
@@ -96,78 +232,52 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
|||||||
throughput = glms_vec3_mul(throughput, material_output.bsdf);
|
throughput = glms_vec3_mul(throughput, material_output.bsdf);
|
||||||
if (glms_vec3_isinf(throughput) || glms_vec3_isnan(throughput))
|
if (glms_vec3_isinf(throughput) || glms_vec3_isnan(throughput))
|
||||||
{
|
{
|
||||||
goto end_path_trace;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We do Russian roulette to decide whether to continue tracing or terminate the path
|
|
||||||
if (depth > 1)
|
if (depth > 1)
|
||||||
{
|
{
|
||||||
float q = fminf(glms_vec3_max(throughput), 0.95f);
|
float q = fminf(glms_vec3_max(throughput), 0.95f);
|
||||||
float rr = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
|
float rr = sobol_sample(sample_index, sobol_get_dimension(depth, PRNG_TERMINATE));
|
||||||
if (rr > q)
|
if (rr > q)
|
||||||
{
|
{
|
||||||
goto end_path_trace;
|
break;
|
||||||
}
|
}
|
||||||
// Keep the energy of the path by scaling the throughput
|
|
||||||
throughput = glms_vec3_scale(throughput, 1.0f / q);
|
throughput = glms_vec3_scale(throughput, 1.0f / q);
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (material_output.state)
|
if (material_output.state != PS_SUCCESS)
|
||||||
{
|
{
|
||||||
//case PATH_THROUGH:
|
break;
|
||||||
// active_ray = ray_create(BIAS_RAY_ORIGION(closest_hit.point, glms_vec3_negate(closest_hit.normal)), active_ray.direction);
|
}
|
||||||
// continue;
|
|
||||||
case PS_SUCCESS:
|
|
||||||
vec3s origin = offset_ray_origin(closest_hit.point, closest_hit.normal, shading_context.wo);
|
vec3s origin = offset_ray_origin(closest_hit.point, closest_hit.normal, shading_context.wo);
|
||||||
active_ray = ray_create(origin, material_output.wi, current_cone_width, material_output.spread_angle);
|
active_ray = ray_create(origin, material_output.wi, current_cone_width, material_output.spread_angle);
|
||||||
depth++;
|
depth++;
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto end_path_trace;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
end_path_trace:
|
|
||||||
return accumulated_color;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// How to handle multi-bounced aov like indirect lighting?
|
void path_trace_aov(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth, aov_flags_t aov_flags, aov_output_t* out)
|
||||||
// Maybe we should move aov to path_trace and split accumulated_color into direct/indirect diffuse/specular before returning.
|
|
||||||
void render_aov(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_t max_depth, aov_output_t* aov_output)
|
|
||||||
{
|
{
|
||||||
hit_result_t closest_hit = ray_intersect_scene_closest(&ray, scene);
|
if (out == NULL)
|
||||||
if (!closest_hit.hit)
|
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const material_t* hit_material = &scene->materials.buffer[scene->triangles.buffer[closest_hit.triangle_id].material_id];
|
aov_init_output(out);
|
||||||
shading_context_t shading_context =
|
|
||||||
|
// Fast path: surface-only AOVs (single intersection + material aov evaluation).
|
||||||
|
if (!aov_wants_lighting(aov_flags))
|
||||||
{
|
{
|
||||||
.camera_position = scene->camera.position,
|
if (!aov_wants_surface(aov_flags))
|
||||||
.camera_direction = glms_vec3_normalize(glms_vec3_sub(closest_hit.point, scene->camera.position)),
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
.position = closest_hit.point,
|
trace_surface_aovs_only(scene, ray, sample_index, aov_flags, out);
|
||||||
.normal = closest_hit.normal,
|
return;
|
||||||
.tangent = closest_hit.tangent,
|
}
|
||||||
.uv = closest_hit.uv,
|
|
||||||
.wo = ray.direction,
|
|
||||||
.throughput = 1.0f,
|
|
||||||
|
|
||||||
.sample_index = sample_index,
|
trace_lighting_aovs(scene, ray, sample_index, max_depth, aov_flags, out);
|
||||||
.bounce_depth = 0,
|
|
||||||
|
|
||||||
.bvh_tree = &scene->bvh_tree,
|
|
||||||
.triangles = &scene->triangles,
|
|
||||||
.lights = &scene->lights,
|
|
||||||
.textures = &scene->textures,
|
|
||||||
|
|
||||||
.triangle_id = closest_hit.triangle_id,
|
|
||||||
.cone_width = ray.width + closest_hit.distance * ray.spread_angle,
|
|
||||||
.spread_angle = ray.spread_angle,
|
|
||||||
};
|
|
||||||
|
|
||||||
render_material_aov(hit_material, &shading_context, aov_output);
|
|
||||||
aov_output->position = glms_vec4(closest_hit.point, 1.0f);
|
|
||||||
aov_output->depth = closest_hit.distance;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,7 +129,6 @@ static float oren_nayar_eval(vec3s l, vec3s v, vec3s n, float roughness, float n
|
|||||||
static void get_surface_data(const shading_context_t* context, const standard_lit_properties_t* properties, standard_lit_surface_data_t* data_out)
|
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
|
// Use the ray cone width (footprint) instead of simple distance for mip selection
|
||||||
float footprint = context->cone_width;
|
|
||||||
float distance = glms_vec3_distance(context->camera_position, context->position);
|
float distance = glms_vec3_distance(context->camera_position, context->position);
|
||||||
vec3s view = context->camera_direction;
|
vec3s view = context->camera_direction;
|
||||||
|
|
||||||
@@ -143,7 +142,7 @@ static void get_surface_data(const shading_context_t* context, const standard_li
|
|||||||
.edge2 = glms_vec3_sub(triangle->vertices[2].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),
|
.uv1 = glms_vec2_sub(triangle->vertices[1].uv, triangle->vertices[0].uv),
|
||||||
.uv2 = glms_vec2_sub(triangle->vertices[2].uv, triangle->vertices[0].uv),
|
.uv2 = glms_vec2_sub(triangle->vertices[2].uv, triangle->vertices[0].uv),
|
||||||
.ray_width = footprint,
|
.ray_width = context->cone_width,
|
||||||
.distance = distance
|
.distance = distance
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -410,7 +409,7 @@ path_output standard_lit_render_loop(const standard_lit_properties_t* properties
|
|||||||
|
|
||||||
// Propagate spread angle for ray cones
|
// Propagate spread angle for ray cones
|
||||||
// Heuristic: spread increases with roughness
|
// Heuristic: spread increases with roughness
|
||||||
output.spread_angle = context->spread_angle + surface_data.roughness * 0.2f;
|
output.spread_angle = context->spread_angle + surface_data.roughness * QUARTER_PI;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -453,3 +452,19 @@ path_output standard_lit_render_loop(const standard_lit_properties_t* properties
|
|||||||
output.state = PS_SUCCESS;
|
output.state = PS_SUCCESS;
|
||||||
return output;
|
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};
|
||||||
|
}
|
||||||
@@ -32,6 +32,9 @@ bool renderer_aov_target_init(render_job_t* job, aov_flags_t aov_flags)
|
|||||||
create_target_if_required(aov_flags, AOV_DEPTH, &job->aov_target[AOV_DEPTH_INDEX], job->config->width, job->config->height);
|
create_target_if_required(aov_flags, AOV_DEPTH, &job->aov_target[AOV_DEPTH_INDEX], job->config->width, job->config->height);
|
||||||
create_target_if_required(aov_flags, AOV_POSITION, &job->aov_target[AOV_POSITION_INDEX], job->config->width, job->config->height);
|
create_target_if_required(aov_flags, AOV_POSITION, &job->aov_target[AOV_POSITION_INDEX], job->config->width, job->config->height);
|
||||||
|
|
||||||
|
create_target_if_required(aov_flags, AOV_DIRECT, &job->aov_target[AOV_DIRECT_INDEX], job->config->width, job->config->height);
|
||||||
|
create_target_if_required(aov_flags, AOV_INDIRECT, &job->aov_target[AOV_INDIRECT_INDEX], job->config->width, job->config->height);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,15 +60,9 @@ static inline vec2s compute_ndc(float x, float y, uint32_t width, uint32_t heigh
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline uint16_t get_sample_count(uint16_t sample_count, aov_index_t index)
|
static inline bool aov_needs_lighting_samples(aov_flags_t flags)
|
||||||
{
|
{
|
||||||
switch (index)
|
return has_flag(flags, AOV_BEAUTY) || has_flag(flags, AOV_DIRECT) || has_flag(flags, AOV_INDIRECT);
|
||||||
{
|
|
||||||
case AOV_BEAUTY_INDEX:
|
|
||||||
return sample_count;
|
|
||||||
default:
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s coord, uint32_t x, uint32_t y, aov_flags_t aov_flags, aov_output_t* pixel_output)
|
static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s coord, uint32_t x, uint32_t y, aov_flags_t aov_flags, aov_output_t* pixel_output)
|
||||||
@@ -73,7 +70,7 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
|
|||||||
aov_output_t accumulated_color = {0};
|
aov_output_t accumulated_color = {0};
|
||||||
|
|
||||||
uint32_t pixel_id = y * config->width + x;
|
uint32_t pixel_id = y * config->width + x;
|
||||||
uint16_t sample_count = config->sample_count;
|
uint16_t sample_count = aov_needs_lighting_samples(aov_flags) ? (uint16_t)config->sample_count : 1;
|
||||||
float inv_sample = 1.0f / (float)sample_count;
|
float inv_sample = 1.0f / (float)sample_count;
|
||||||
|
|
||||||
vec3s camera_right = quat_get_right(scene->camera.rotation);
|
vec3s camera_right = quat_get_right(scene->camera.rotation);
|
||||||
@@ -82,7 +79,7 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
|
|||||||
for (uint16_t k = 0; k < sample_count; k++)
|
for (uint16_t k = 0; k < sample_count; k++)
|
||||||
{
|
{
|
||||||
// TODO: Hash it
|
// TODO: Hash it
|
||||||
uint32_t sobol_idx = pixel_id * config->sample_count + (k + 1);
|
uint32_t sobol_idx = pixel_id * (uint32_t)sample_count + (k + 1);
|
||||||
|
|
||||||
// Apply AA
|
// Apply AA
|
||||||
float du = sobol_sample(sobol_idx, PRNG_FILTER_U);
|
float du = sobol_sample(sobol_idx, PRNG_FILTER_U);
|
||||||
@@ -105,15 +102,7 @@ static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s
|
|||||||
ray_t ray = ray_create(scene->camera.position, glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position)), 0.0f, spread_angle);
|
ray_t ray = ray_create(scene->camera.position, glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position)), 0.0f, spread_angle);
|
||||||
|
|
||||||
aov_output_t aov_output = {0};
|
aov_output_t aov_output = {0};
|
||||||
if (has_flag(aov_flags, AOV_BEAUTY))
|
path_trace_aov(scene, ray, sobol_idx, config->max_depth, aov_flags, &aov_output);
|
||||||
{
|
|
||||||
aov_output.beauty = path_trace(scene, ray, sobol_idx, config->max_depth);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aov_flags != AOV_BEAUTY)
|
|
||||||
{
|
|
||||||
render_aov(scene, ray, sobol_idx, config->max_depth, &aov_output);
|
|
||||||
}
|
|
||||||
|
|
||||||
accumulate_aov(&accumulated_color, &aov_output, inv_sample);
|
accumulate_aov(&accumulated_color, &aov_output, inv_sample);
|
||||||
}
|
}
|
||||||
@@ -138,6 +127,9 @@ static inline void update_aov(render_target_t** target, const aov_output_t* aov,
|
|||||||
update_aov_pixel_if_exist(&target[AOV_NORMAL_INDEX], aov->normal, x, y);
|
update_aov_pixel_if_exist(&target[AOV_NORMAL_INDEX], aov->normal, x, y);
|
||||||
update_aov_pixel_if_exist(&target[AOV_DEPTH_INDEX], (vec4s){aov->depth, aov->depth, aov->depth, 1.0f}, x, y);
|
update_aov_pixel_if_exist(&target[AOV_DEPTH_INDEX], (vec4s){aov->depth, aov->depth, aov->depth, 1.0f}, x, y);
|
||||||
update_aov_pixel_if_exist(&target[AOV_POSITION_INDEX], aov->position, x, y);
|
update_aov_pixel_if_exist(&target[AOV_POSITION_INDEX], aov->position, x, y);
|
||||||
|
|
||||||
|
update_aov_pixel_if_exist(&target[AOV_DIRECT_INDEX], aov->direct, x, y);
|
||||||
|
update_aov_pixel_if_exist(&target[AOV_INDIRECT_INDEX], aov->indirect, x, y);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Progressive rendering
|
// TODO: Progressive rendering
|
||||||
|
|||||||
@@ -419,7 +419,7 @@ float texture_get_sample_lod(const texture_t* texture, const texture_sample_cont
|
|||||||
// 4. Convert to LOD
|
// 4. Convert to LOD
|
||||||
// LOD 0 = 1 texel. LOD 1 = 2 texels. LOD 2 = 4 texels.
|
// LOD 0 = 1 texel. LOD 1 = 2 texels. LOD 2 = 4 texels.
|
||||||
// log2(texels_covered) gives the mip level.
|
// log2(texels_covered) gives the mip level.
|
||||||
return log2f(texels_covered);
|
return log2f(texels_covered) * 0.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
static vec4s nearest_filter(const texture_t* texture, vec2s uv, uint8_t lod)
|
static vec4s nearest_filter(const texture_t* texture, vec2s uv, uint8_t lod)
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ static bool load_assets(scene_t* scene)
|
|||||||
return scene_build_bvh(scene);
|
return scene_build_bvh(scene);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool initialize_renderer(const rendering_config_t* config, render_job_t** outJob, scene_t* outScene)
|
static bool initialize_renderer(const rendering_config_t* config, aov_flags_t aov_flags, render_job_t** outJob, scene_t* outScene)
|
||||||
{
|
{
|
||||||
if (!scene_setup(outScene)
|
if (!scene_setup(outScene)
|
||||||
|| !load_assets(outScene))
|
|| !load_assets(outScene))
|
||||||
@@ -104,7 +104,7 @@ static bool initialize_renderer(const rendering_config_t* config, render_job_t**
|
|||||||
.config = config,
|
.config = config,
|
||||||
|
|
||||||
.rendering_mode = RENDER_TILE_BASED,
|
.rendering_mode = RENDER_TILE_BASED,
|
||||||
.aov_flags = AOV_BEAUTY,
|
.aov_flags = aov_flags,
|
||||||
.is_done = false,
|
.is_done = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -141,7 +141,7 @@ static void update_pixel_buffer(render_target_t* render_target)
|
|||||||
{
|
{
|
||||||
vec4s pixel = render_target_get_pixel(render_target, x, y);
|
vec4s pixel = render_target_get_pixel(render_target, x, y);
|
||||||
pixel = gamma_correct(pixel, 2.2f);
|
pixel = gamma_correct(pixel, 2.2f);
|
||||||
pixel = aces_tone_map(pixel);
|
//pixel = aces_tone_map(pixel);
|
||||||
window_update_pixel(pixel, x, y);
|
window_update_pixel(pixel, x, y);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -198,14 +198,14 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
|||||||
.bucket_size = 64,
|
.bucket_size = 64,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!initialize_renderer(&config, &job, &scene)
|
if (!initialize_renderer(&config, AOV_INDIRECT, &job, &scene)
|
||||||
|| !window_create(TITLE, hInstance, config.width, config.height, job))
|
|| !window_create(TITLE, hInstance, config.width, config.height, job))
|
||||||
{
|
{
|
||||||
shutdown_renderer(job, &scene);
|
shutdown_renderer(job, &scene);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int result = run_main_loop(job, AOV_BEAUTY_INDEX);
|
int result = run_main_loop(job, AOV_INDIRECT_INDEX);
|
||||||
|
|
||||||
window_close();
|
window_close();
|
||||||
shutdown_renderer(job, &scene);
|
shutdown_renderer(job, &scene);
|
||||||
|
|||||||
Reference in New Issue
Block a user