features/modernize #1
@@ -1,7 +1,7 @@
|
|||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
cmake_minimum_required(VERSION 3.10)
|
cmake_minimum_required(VERSION 3.10)
|
||||||
|
|
||||||
set(CMAKE_C_STANDARD 11)
|
set(CMAKE_C_STANDARD 23)
|
||||||
set(PROJECT_NAME SimpleRayTracer)
|
set(PROJECT_NAME SimpleRayTracer)
|
||||||
|
|
||||||
project(${PROJECT_NAME} LANGUAGES C)
|
project(${PROJECT_NAME} LANGUAGES C)
|
||||||
|
|||||||
@@ -21,7 +21,8 @@ typedef struct
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vec3s point;
|
vec3s point;
|
||||||
vec3s normal; // Should we remove normal, tangent, and uv from here and output u, v, w instead?
|
vec3s normal;
|
||||||
|
vec3s geometric_normal;
|
||||||
vec3s tangent;
|
vec3s tangent;
|
||||||
vec2s uv;
|
vec2s uv;
|
||||||
mesh_model_handle_t model;
|
mesh_model_handle_t model;
|
||||||
|
|||||||
@@ -40,12 +40,11 @@ typedef enum
|
|||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vec3s wi;
|
vec3s wi;
|
||||||
vec3s direct_lighting;
|
|
||||||
vec3s bsdf;
|
vec3s bsdf;
|
||||||
|
vec3s lighting;
|
||||||
float pdf;
|
float pdf;
|
||||||
float spread_angle;
|
float spread_angle;
|
||||||
path_state state;
|
path_state state;
|
||||||
bool is_delta;
|
|
||||||
} path_output;
|
} path_output;
|
||||||
|
|
||||||
inline float random_float()
|
inline float random_float()
|
||||||
|
|||||||
@@ -21,35 +21,37 @@ typedef struct
|
|||||||
{ \
|
{ \
|
||||||
uint32_t length; \
|
uint32_t length; \
|
||||||
T* buffer; \
|
T* buffer; \
|
||||||
} name##_array_t; \
|
} name##_array_t;
|
||||||
static inline result_t name##_array_init(name##_array_t* array, uint32_t size) \
|
|
||||||
|
#define array_init(array, size) \
|
||||||
|
do \
|
||||||
{ \
|
{ \
|
||||||
array->length = size; \
|
array.length = size; \
|
||||||
T* temp = (T*)malloc(sizeof(T) * size); \
|
array.buffer = malloc(sizeof(*array.buffer) * size); \
|
||||||
if (temp == NULL) \
|
} while (0)
|
||||||
|
|
||||||
|
#define array_free(array) \
|
||||||
|
do \
|
||||||
{ \
|
{ \
|
||||||
return RESULT_OUT_OF_MEMORY; \
|
if (array.buffer != NULL) \
|
||||||
|
{ \
|
||||||
|
free(array.buffer); \
|
||||||
|
array.buffer = NULL; \
|
||||||
|
array.length = 0; \
|
||||||
} \
|
} \
|
||||||
array->buffer = temp; \
|
} while (0)
|
||||||
return RESULT_SUCCESS; \
|
|
||||||
} \
|
#define array_get(array, index) \
|
||||||
static inline void name##_array_free(name##_array_t* array) \
|
((index) >= (array).length ? NULL : &(array).buffer[index])
|
||||||
|
|
||||||
|
#define list_init(list, capacity) \
|
||||||
|
do \
|
||||||
{ \
|
{ \
|
||||||
if (array->buffer != NULL) \
|
capacity = capacity == 0 ? 1 : capacity; \
|
||||||
{ \
|
list.count = 0; \
|
||||||
free(array->buffer); \
|
list.capacity = capacity; \
|
||||||
array->buffer = NULL; \
|
list.buffer = (T*)malloc(sizeof(T) * capacity); \
|
||||||
array->length = 0; \
|
} while (0)
|
||||||
} \
|
|
||||||
} \
|
|
||||||
static inline T* name##_array_get(name##_array_t* array, uint32_t index) \
|
|
||||||
{ \
|
|
||||||
if (index >= array->length) \
|
|
||||||
{ \
|
|
||||||
return NULL; \
|
|
||||||
} \
|
|
||||||
return &array->buffer[index]; \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define LIST_DEF(T, name) \
|
#define LIST_DEF(T, name) \
|
||||||
typedef struct \
|
typedef struct \
|
||||||
|
|||||||
@@ -5,9 +5,6 @@
|
|||||||
#include "Rendering/Texture.h"
|
#include "Rendering/Texture.h"
|
||||||
#include "cglm/struct/vec3.h"
|
#include "cglm/struct/vec3.h"
|
||||||
|
|
||||||
struct scene_t;
|
|
||||||
struct bvh_tree_t;
|
|
||||||
|
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
vec3s position;
|
vec3s position;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
#include "Rendering/Scene.h"
|
#include "Rendering/Scene.h"
|
||||||
|
|
||||||
path_output evaluate_bsdf_directional( directional_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index);
|
path_output evaluate_bsdf_directional( directional_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index);
|
||||||
|
path_output evaluate_bsdf_punctual_light(punctual_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index);
|
||||||
|
|
||||||
inline path_output evaluate_bsdf_sky(const light_collection_t* lights, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
inline path_output evaluate_bsdf_sky(const light_collection_t* lights, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ typedef struct
|
|||||||
|
|
||||||
vec3s position;
|
vec3s position;
|
||||||
vec3s normal;
|
vec3s normal;
|
||||||
|
vec3s geometric_normal;
|
||||||
vec3s tangent;
|
vec3s tangent;
|
||||||
vec3s wo;
|
vec3s wo;
|
||||||
vec3s throughput;
|
vec3s throughput;
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
#ifndef SHADING_CONTEXT_H
|
|
||||||
#define SHADING_CONTEXT_H
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endif // SHADING_CONTEXT_H
|
|
||||||
@@ -56,7 +56,7 @@ bool scene_build_bvh(scene_t* scene);
|
|||||||
void scene_free(scene_t* scene);
|
void scene_free(scene_t* scene);
|
||||||
|
|
||||||
// Mesh model/instance API (simple first, flexible later).
|
// Mesh model/instance API (simple first, flexible later).
|
||||||
mesh_model_handle_t scene_add_mesh_model(scene_t* scene, uint64_t triangle_reserve);
|
mesh_model_handle_t scene_add_mesh_model(scene_t* scene, uint32_t triangle_reserve);
|
||||||
mesh_instance_handle_t scene_add_mesh_instance(scene_t* scene, mesh_model_handle_t model, mat4s local_to_world);
|
mesh_instance_handle_t scene_add_mesh_instance(scene_t* scene, mesh_model_handle_t model, mat4s local_to_world);
|
||||||
void scene_remove_mesh_instance(scene_t* scene, mesh_instance_handle_t instance);
|
void scene_remove_mesh_instance(scene_t* scene, mesh_instance_handle_t instance);
|
||||||
void scene_set_mesh_instance_transform(scene_t* scene, mesh_instance_handle_t instance, mat4s local_to_world);
|
void scene_set_mesh_instance_transform(scene_t* scene, mesh_instance_handle_t instance, mat4s local_to_world);
|
||||||
|
|||||||
@@ -71,11 +71,11 @@ typedef struct
|
|||||||
} texture_sample_context_t;
|
} texture_sample_context_t;
|
||||||
|
|
||||||
texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stride_t stride, texture_slot_map_t* textures);
|
texture_handle_t texture_load(const char* filename, bool srgb, bool mipmap, stride_t stride, texture_slot_map_t* textures);
|
||||||
|
void texture_free(texture_t* texture);
|
||||||
vec4s texture_get_pixel(const texture_t* texture, vec2s uv, uint8_t lod);
|
vec4s texture_get_pixel(const texture_t* texture, vec2s uv, uint8_t lod);
|
||||||
float texture_get_sample_lod(const texture_t* texture, const texture_sample_context_t* sample_context);
|
float texture_get_sample_lod(const texture_t* texture, const texture_sample_context_t* sample_context);
|
||||||
vec4s texture_sample(const texture_t* texture, const texture_sample_context_t* sample_context, vec2s uv);
|
vec4s texture_sample(const texture_t* texture, const texture_sample_context_t* sample_context, vec2s uv);
|
||||||
vec4s texture_sample_lod(const texture_t* texture, vec2s uv, float lod);
|
vec4s texture_sample_lod(const texture_t* texture, vec2s uv, float lod);
|
||||||
void texture_free(texture_t* texture);
|
|
||||||
|
|
||||||
inline bool is_texture_entity_valid(texture_handle_t entity)
|
inline bool is_texture_entity_valid(texture_handle_t entity)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ static inline shading_context_t make_shading_context(const scene_t* scene,
|
|||||||
|
|
||||||
.position = hit->point,
|
.position = hit->point,
|
||||||
.normal = hit->normal,
|
.normal = hit->normal,
|
||||||
|
.geometric_normal = hit->geometric_normal,
|
||||||
.tangent = hit->tangent,
|
.tangent = hit->tangent,
|
||||||
.uv = hit->uv,
|
.uv = hit->uv,
|
||||||
.wo = wo,
|
.wo = wo,
|
||||||
@@ -184,7 +185,7 @@ static void trace_lighting_aovs(const scene_t* scene,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vec3s env_contrib = glms_vec3_scale(sky_output.direct_lighting, w);
|
vec3s env_contrib = glms_vec3_scale(sky_output.lighting, w);
|
||||||
aov_accumulate_nee(out, aov_flags, env_contrib, depth);
|
aov_accumulate_nee(out, aov_flags, env_contrib, depth);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -223,12 +224,12 @@ static void trace_lighting_aovs(const scene_t* scene,
|
|||||||
}
|
}
|
||||||
|
|
||||||
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.lighting) || glms_vec3_isnan(material_output.lighting))
|
||||||
{
|
{
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
aov_accumulate_nee(out, aov_flags, material_output.direct_lighting, depth);
|
aov_accumulate_nee(out, aov_flags, material_output.lighting, depth);
|
||||||
|
|
||||||
if (material_output.pdf < FLT_EPSILON)
|
if (material_output.pdf < FLT_EPSILON)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ hit_result_t ray_intersect_triangle(const ray_t* ray, const triangle_t* triangle
|
|||||||
normal = glms_vec3_add(normal, glms_vec3_scale(triangle->vertices[2].normal, v));
|
normal = glms_vec3_add(normal, glms_vec3_scale(triangle->vertices[2].normal, v));
|
||||||
normal = glms_vec3_dot(normal, direction) < 0.0f ? normal : glms_vec3_negate(normal);
|
normal = glms_vec3_dot(normal, direction) < 0.0f ? normal : glms_vec3_negate(normal);
|
||||||
result.normal = glms_vec3_normalize(normal);
|
result.normal = glms_vec3_normalize(normal);
|
||||||
|
result.geometric_normal = triangle->face_normal;
|
||||||
|
|
||||||
vec3s tangent = glms_vec3_scale(triangle->vertices[0].tangent, w);
|
vec3s tangent = glms_vec3_scale(triangle->vertices[0].tangent, w);
|
||||||
tangent = glms_vec3_add(tangent, glms_vec3_scale(triangle->vertices[1].tangent, u));
|
tangent = glms_vec3_add(tangent, glms_vec3_scale(triangle->vertices[1].tangent, u));
|
||||||
@@ -308,7 +309,7 @@ void ray_intersect_bvh_any(const ray_t* ray, const bvh_node_t* bvh_nodes, const
|
|||||||
{
|
{
|
||||||
for (uint32_t i = 0; i < node->primitive_count; i++)
|
for (uint32_t i = 0; i < node->primitive_count; i++)
|
||||||
{
|
{
|
||||||
uint64_t triangle_index = primitive_indices[node->start_index + i];
|
uint32_t triangle_index = primitive_indices[node->start_index + i];
|
||||||
hit_result_t hit_result = ray_intersect_triangle(ray, &all_triangles->buffer[triangle_index]);
|
hit_result_t hit_result = ray_intersect_triangle(ray, &all_triangles->buffer[triangle_index]);
|
||||||
if (hit_result.hit)
|
if (hit_result.hit)
|
||||||
{
|
{
|
||||||
@@ -322,8 +323,8 @@ void ray_intersect_bvh_any(const ray_t* ray, const bvh_node_t* bvh_nodes, const
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Internal node: traverse children in near‐first order
|
// Internal node: traverse children in near‐first order
|
||||||
uint64_t left_child_index = node->left_child_offset;
|
uint32_t left_child_index = node->left_child_offset;
|
||||||
uint64_t right_child_index = node->right_child_offset;
|
uint32_t right_child_index = node->right_child_offset;
|
||||||
|
|
||||||
const bvh_node_t* left_child = &bvh_nodes[left_child_index];
|
const bvh_node_t* left_child = &bvh_nodes[left_child_index];
|
||||||
const bvh_node_t* right_child = &bvh_nodes[right_child_index];
|
const bvh_node_t* right_child = &bvh_nodes[right_child_index];
|
||||||
@@ -376,7 +377,7 @@ hit_result_t ray_intersect_scene_closest(const ray_t* ray, const scene_t* scene)
|
|||||||
{
|
{
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
uint64_t node_index;
|
uint32_t node_index;
|
||||||
float enter;
|
float enter;
|
||||||
} tlas_stack_entry_t;
|
} tlas_stack_entry_t;
|
||||||
|
|
||||||
@@ -467,8 +468,8 @@ hit_result_t ray_intersect_scene_closest(const ray_t* ray, const scene_t* scene)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t left = node->left_child_offset;
|
uint32_t left = node->left_child_offset;
|
||||||
uint64_t right = node->right_child_offset;
|
uint32_t right = node->right_child_offset;
|
||||||
|
|
||||||
float left_enter, left_exit, right_enter, right_exit;
|
float left_enter, left_exit, right_enter, right_exit;
|
||||||
bool hit_left = ray_intersect_aabb(ray, scene->tlas.nodes[left].bounds, &left_enter, &left_exit);
|
bool hit_left = ray_intersect_aabb(ray, scene->tlas.nodes[left].bounds, &left_enter, &left_exit);
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ mesh_model_handle_t mesh_load(const char* filename, scene_t* scene)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reserve a model sized for the imported geometry.
|
// Reserve a model sized for the imported geometry.
|
||||||
uint64_t triangle_reserve = 0;
|
uint32_t triangle_reserve = 0;
|
||||||
for (uint32_t i = 0; i < mesh_scene->mNumMeshes; i++)
|
for (uint32_t i = 0; i < mesh_scene->mNumMeshes; i++)
|
||||||
{
|
{
|
||||||
const struct aiMesh* mesh = mesh_scene->mMeshes[i];
|
const struct aiMesh* mesh = mesh_scene->mMeshes[i];
|
||||||
@@ -45,7 +45,7 @@ mesh_model_handle_t mesh_load(const char* filename, scene_t* scene)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Each face is expected to be a triangle; we still validate per face below.
|
// Each face is expected to be a triangle; we still validate per face below.
|
||||||
triangle_reserve += (uint64_t)mesh->mNumFaces;
|
triangle_reserve += (uint32_t)mesh->mNumFaces;
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_model_handle_t handle = scene_add_mesh_model(scene, triangle_reserve);
|
mesh_model_handle_t handle = scene_add_mesh_model(scene, triangle_reserve);
|
||||||
|
|||||||
@@ -51,8 +51,61 @@ path_output evaluate_bsdf_directional(directional_light_t light, const light_sha
|
|||||||
vec3s light_radiance = glms_vec3_scale(light.color, light.intensity);
|
vec3s light_radiance = glms_vec3_scale(light.color, light.intensity);
|
||||||
vec3s light_contribute = glms_vec3_scale( throughput, fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for directional light
|
vec3s light_contribute = glms_vec3_scale( throughput, fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for directional light
|
||||||
|
|
||||||
output.direct_lighting = glms_vec3_mul(light_radiance, light_contribute);
|
output.lighting = glms_vec3_mul(light_radiance, light_contribute);
|
||||||
output.wi = wi;
|
output.wi = wi;
|
||||||
output.state = PS_SUCCESS;
|
output.state = PS_SUCCESS;
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Support light size and soft shadows
|
||||||
|
// TODO: Support different light types (spot, area)
|
||||||
|
path_output evaluate_bsdf_punctual_light(punctual_light_t light, const light_shading_context_t* context, vec3s throughput, uint32_t sample_index)
|
||||||
|
{
|
||||||
|
path_output output = {0.0f};
|
||||||
|
output.state = PS_TERMINATE;
|
||||||
|
output.pdf = 1.0f;
|
||||||
|
|
||||||
|
if (context == NULL)
|
||||||
|
{
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (light.intensity <= 0.0f)
|
||||||
|
{
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3s light_dir = glms_vec3_normalize(glms_vec3_sub(light.position, context->position));
|
||||||
|
float distance_squared = glms_vec3_dot(glms_vec3_sub(light.position, context->position), glms_vec3_sub(light.position, context->position));
|
||||||
|
|
||||||
|
float n_dot_l = glms_vec3_dot(context->normal, light_dir);
|
||||||
|
if (n_dot_l <= 0.0f)
|
||||||
|
{
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
ray_t shadow_ray = ray_create(offset_ray_origin(context->position, context->normal, context->wo), light_dir, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
hit_result_t shadow_hit = {0};
|
||||||
|
if (context->scene != NULL)
|
||||||
|
{
|
||||||
|
shadow_hit = ray_intersect_scene_any(&shadow_ray, context->scene);
|
||||||
|
}
|
||||||
|
else if (context->bvh_tree != NULL)
|
||||||
|
{
|
||||||
|
ray_intersect_bvh_any(&shadow_ray, context->bvh_tree->nodes, context->bvh_tree->primitive_indices, context->bvh_tree->triangles, 0, &shadow_hit);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shadow_hit.hit && shadow_hit.distance * shadow_hit.distance < distance_squared - FLT_EPSILON)
|
||||||
|
{
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3s light_radiance = glms_vec3_scale(light.color, light.intensity / distance_squared);
|
||||||
|
vec3s light_contribute = glms_vec3_scale(throughput, fmaxf(0.0f, n_dot_l)); // we always assume pdf = 1.0f for punctual light
|
||||||
|
|
||||||
|
output.lighting = glms_vec3_mul(light_radiance, light_contribute);
|
||||||
|
output.wi = light_dir;
|
||||||
|
output.state = PS_SUCCESS;
|
||||||
|
return output;
|
||||||
|
}
|
||||||
@@ -40,7 +40,7 @@ path_output evaluate_bsdf_const_sky(const void* data, const light_shading_contex
|
|||||||
// cosine-weighted hemisphere around the last shading normal.
|
// cosine-weighted hemisphere around the last shading normal.
|
||||||
if (context == NULL || (context->scene == NULL && context->bvh_tree == NULL))
|
if (context == NULL || (context->scene == NULL && context->bvh_tree == NULL))
|
||||||
{
|
{
|
||||||
output.direct_lighting = glms_vec3_mul(sky_light, throughput);
|
output.lighting = glms_vec3_mul(sky_light, throughput);
|
||||||
|
|
||||||
// If a normal wasn't provided, fall back to a normal-independent uniform sphere PDF.
|
// If a normal wasn't provided, fall back to a normal-independent uniform sphere PDF.
|
||||||
float n2 = (context != NULL) ? glms_vec3_norm2(context->normal) : 0.0f;
|
float n2 = (context != NULL) ? glms_vec3_norm2(context->normal) : 0.0f;
|
||||||
@@ -89,7 +89,7 @@ path_output evaluate_bsdf_const_sky(const void* data, const light_shading_contex
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), cos_theta / pdf);
|
output.lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), cos_theta / pdf);
|
||||||
|
|
||||||
output.wi = wi;
|
output.wi = wi;
|
||||||
output.state = PS_SUCCESS;
|
output.state = PS_SUCCESS;
|
||||||
@@ -348,7 +348,7 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
|
|||||||
}
|
}
|
||||||
|
|
||||||
vec4s sky_light = texture_sample_lod(get_texture(context->textures, sky_data->texture), uv, lod);
|
vec4s sky_light = texture_sample_lod(get_texture(context->textures, sky_data->texture), uv, lod);
|
||||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(glms_vec3(sky_light), throughput), sky_data->intensity);
|
output.lighting = glms_vec3_scale(glms_vec3_mul(glms_vec3(sky_light), throughput), sky_data->intensity);
|
||||||
// Return the correct environment PDF for MIS when the BSDF-sampled ray escapes to the sky.
|
// 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);
|
output.pdf = hdr_sky_pdf_direction(sky_data, context->wo);
|
||||||
return output;
|
return output;
|
||||||
@@ -400,9 +400,11 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Both texture sampling and PDF calculation can be bilinear interpolated for better quality
|
||||||
|
// This requires us to change the uv from always being at the center of the texel to being continuous
|
||||||
vec2s uv = (vec2s){u, v};
|
vec2s uv = (vec2s){u, v};
|
||||||
const texture_t* hdri = get_texture(context->textures, sky_data->texture);
|
const texture_t* hdri = get_texture(context->textures, sky_data->texture);
|
||||||
vec4s pixel = texture_get_pixel(hdri, uv, 0);
|
vec4s pixel = texture_sample_lod(hdri, uv, 0);
|
||||||
vec3s sky_light = glms_vec3_scale(glms_vec3(pixel), sky_data->intensity);
|
vec3s sky_light = glms_vec3_scale(glms_vec3(pixel), sky_data->intensity);
|
||||||
|
|
||||||
float mass = sky_data->pdf_uv_mass[y_idx * sky_data->width + x_idx];
|
float mass = sky_data->pdf_uv_mass[y_idx * sky_data->width + x_idx];
|
||||||
@@ -412,7 +414,7 @@ path_output evaluate_bsdf_hdr_sky(const void* data, const light_shading_context_
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
output.direct_lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), n_dot_l / pdf);
|
output.lighting = glms_vec3_scale(glms_vec3_mul(sky_light, throughput), n_dot_l / pdf);
|
||||||
output.wi = wi;
|
output.wi = wi;
|
||||||
output.pdf = pdf;
|
output.pdf = pdf;
|
||||||
output.state = PS_SUCCESS;
|
output.state = PS_SUCCESS;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ static void get_surface_data(const shading_context_t* context, const standard_li
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static vec3s evaluate_bsdf_standard_lit(const shading_context_t* context, standard_lit_surface_data_t* surface_data, vec3s wi)
|
static vec3s evaluate_bsdf_standard_lit(const shading_context_t* context, const standard_lit_surface_data_t* surface_data, vec3s wi)
|
||||||
{
|
{
|
||||||
vec3s n = surface_data->normal;
|
vec3s n = surface_data->normal;
|
||||||
vec3s v = glms_vec3_negate(context->wo);
|
vec3s v = glms_vec3_negate(context->wo);
|
||||||
@@ -188,6 +188,14 @@ static float sample_bsdf_pdf(const standard_lit_surface_data_t* surface_data, ve
|
|||||||
return w_ss * pdf_spec + w_cos * pdf_cos;
|
return w_ss * pdf_spec + w_cos * pdf_cos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void accumulate_lighting(path_output* output, const path_output* light_output, const shading_context_t* context, vec3s V, const standard_lit_surface_data_t* surface_data)
|
||||||
|
{
|
||||||
|
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->lighting, pdf_bsdf, light_output->pdf);
|
||||||
|
output->lighting = glms_vec3_add(output->lighting, light_contribute);
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
standard_lit_surface_data_t surface_data; // Assuming you reuse your struct
|
standard_lit_surface_data_t surface_data; // Assuming you reuse your struct
|
||||||
@@ -243,10 +251,17 @@ path_output standard_lit_render_loop(const standard_lit_properties_t* properties
|
|||||||
path_output light_output = evaluate_bsdf_directional(context->lights->directional_lights.buffer[i], &light_context, context->throughput, context->sample_index);
|
path_output light_output = evaluate_bsdf_directional(context->lights->directional_lights.buffer[i], &light_context, context->throughput, context->sample_index);
|
||||||
if (light_output.state == PS_SUCCESS)
|
if (light_output.state == PS_SUCCESS)
|
||||||
{
|
{
|
||||||
vec3s bsdf_dir_light = evaluate_bsdf_standard_lit(context, &surface_data, light_output.wi);
|
accumulate_lighting(&output, &light_output, context, V, &surface_data);
|
||||||
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);
|
|
||||||
|
// Punctual Lights
|
||||||
|
for (uint32_t i = 0; i < context->lights->punctual_lights.count; i++)
|
||||||
|
{
|
||||||
|
path_output light_output = evaluate_bsdf_punctual_light(context->lights->punctual_lights.buffer[i], &light_context, context->throughput, context->sample_index);
|
||||||
|
if (light_output.state == PS_SUCCESS)
|
||||||
|
{
|
||||||
|
accumulate_lighting(&output, &light_output, context, V, &surface_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,10 +269,7 @@ path_output standard_lit_render_loop(const standard_lit_properties_t* properties
|
|||||||
path_output sky_output = evaluate_bsdf_sky(context->lights, &light_context, context->throughput, context->sample_index);
|
path_output sky_output = evaluate_bsdf_sky(context->lights, &light_context, context->throughput, context->sample_index);
|
||||||
if (sky_output.state == PS_SUCCESS)
|
if (sky_output.state == PS_SUCCESS)
|
||||||
{
|
{
|
||||||
vec3s bsdf_sky_light = evaluate_bsdf_standard_lit(context, &surface_data, sky_output.wi);
|
accumulate_lighting(&output, &sky_output, context, V, &surface_data);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------
|
// ----------------------------------------------------
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Build list of active instances.
|
// Build list of active instances.
|
||||||
uint64_t active_count = 0;
|
uint32_t active_count = 0;
|
||||||
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
||||||
{
|
{
|
||||||
if (scene->mesh_instances.occupied[i])
|
if (scene->mesh_instances.occupied[i])
|
||||||
@@ -64,13 +64,13 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t* indices = (uint64_t*)malloc(sizeof(uint64_t) * active_count);
|
uint32_t* indices = (uint32_t*)malloc(sizeof(uint32_t) * active_count);
|
||||||
if (indices == NULL)
|
if (indices == NULL)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t cursor = 0;
|
uint32_t cursor = 0;
|
||||||
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
for (uint32_t i = 0; i < scene->mesh_instances.capacity; ++i)
|
||||||
{
|
{
|
||||||
if (scene->mesh_instances.occupied[i])
|
if (scene->mesh_instances.occupied[i])
|
||||||
@@ -115,6 +115,8 @@ static bool scene_rebuild_tlas(scene_t* scene)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ARRAY_DEF(int, test)
|
||||||
|
|
||||||
bool scene_init(scene_t* scene, uint32_t triangle_count, uint32_t texture_count, uint32_t material_count, uint32_t punctual_light_count)
|
bool scene_init(scene_t* scene, uint32_t triangle_count, uint32_t texture_count, uint32_t material_count, uint32_t punctual_light_count)
|
||||||
{
|
{
|
||||||
scene_t temp = {0};
|
scene_t temp = {0};
|
||||||
@@ -232,7 +234,7 @@ void scene_free(scene_t* scene)
|
|||||||
light_collection_free(&scene->lights);
|
light_collection_free(&scene->lights);
|
||||||
}
|
}
|
||||||
|
|
||||||
mesh_model_handle_t scene_add_mesh_model(scene_t* scene, uint64_t triangle_reserve)
|
mesh_model_handle_t scene_add_mesh_model(scene_t* scene, uint32_t triangle_reserve)
|
||||||
{
|
{
|
||||||
if (scene == NULL)
|
if (scene == NULL)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -362,7 +362,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) * 0.5f;
|
return log2f(texels_covered) * 0.5f; // TODO: Apply EWA for better quality
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <omp.h>
|
#include <omp.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <svpng.inc>
|
|
||||||
|
|
||||||
#include "Algorithm/Sobol.h"
|
#include "Algorithm/Sobol.h"
|
||||||
#include "Geometry/GeometryUtilities.h"
|
#include "Geometry/GeometryUtilities.h"
|
||||||
@@ -15,7 +14,14 @@
|
|||||||
|
|
||||||
#define TITLE "Path Tracing"
|
#define TITLE "Path Tracing"
|
||||||
#define SCENE_PATH "./assets/sponza.fbx"
|
#define SCENE_PATH "./assets/sponza.fbx"
|
||||||
#define HDRI_PATH "C:/Users/Misaki/Downloads/shanghai_bund_1k.hdr"
|
#define HDRI_PATH "./assets/hdri/golden_gate_hills_1k.hdr"
|
||||||
|
|
||||||
|
#define SAVE_OUTPUT
|
||||||
|
|
||||||
|
#ifdef SAVE_OUTPUT
|
||||||
|
#include <svpng.inc>
|
||||||
|
#define OUTPUT_PATH "./output.png"
|
||||||
|
#endif
|
||||||
|
|
||||||
static bool scene_setup(scene_t* scene)
|
static bool scene_setup(scene_t* scene)
|
||||||
{
|
{
|
||||||
@@ -33,21 +39,54 @@ static bool scene_setup(scene_t* scene)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// TODO: Standardize light unit
|
// TODO: Standardize light unit
|
||||||
directional_light_t sun_light =
|
// directional_light_t sun_light =
|
||||||
{
|
// {
|
||||||
.direction = glms_vec3_normalize((vec3s){0.6f, 1.0f, 0.25f}),
|
// .direction = glms_vec3_normalize((vec3s){0.6f, 1.0f, 0.25f}),
|
||||||
.color = (vec3s){1.0f, 0.93f, 0.87f},
|
// .color = (vec3s){1.0f, 0.93f, 0.87f},
|
||||||
.intensity = 0.0f,
|
// .intensity = 1.0f,
|
||||||
.angular_diameter = 0.53f
|
// .angular_diameter = 0.53f
|
||||||
};
|
// };
|
||||||
|
|
||||||
light_handle_t sun = light_add_directional_light(&scene->lights, &sun_light);
|
// light_handle_t sun = light_add_directional_light(&scene->lights, &sun_light);
|
||||||
|
|
||||||
|
// punctual_light_t point_light1 =
|
||||||
|
// {
|
||||||
|
// .position = (vec3s){1.0f, 5.0f, 0.0f},
|
||||||
|
// .color = (vec3s){1.0f, 1.0f, 1.0f},
|
||||||
|
// .intensity = 10.0f,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// punctual_light_t point_light2 =
|
||||||
|
// {
|
||||||
|
// .position = (vec3s){1.0f, 5.0f, 2.5f},
|
||||||
|
// .color = (vec3s){1.0f, 1.0f, 1.0f},
|
||||||
|
// .intensity = 50.0f,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// punctual_light_t point_light3 =
|
||||||
|
// {
|
||||||
|
// .position = (vec3s){1.0f, 5.0f, 2.5f},
|
||||||
|
// .color = (vec3s){1.0f, 1.0f, 1.0f},
|
||||||
|
// .intensity = 50.0f,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// punctual_light_t point_light4 =
|
||||||
|
// {
|
||||||
|
// .position = (vec3s){1.0f, 5.0f, 2.5f},
|
||||||
|
// .color = (vec3s){1.0f, 1.0f, 1.0f},
|
||||||
|
// .intensity = 50.0f,
|
||||||
|
// };
|
||||||
|
|
||||||
|
// light_add_punctual_light(&scene->lights, &point_light1);
|
||||||
|
// light_add_punctual_light(&scene->lights, &point_light2);
|
||||||
|
// light_add_punctual_light(&scene->lights, &point_light3);
|
||||||
|
// light_add_punctual_light(&scene->lights, &point_light4);
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
scene->lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t)
|
scene->lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t)
|
||||||
{
|
{
|
||||||
.color = (vec3s){1.0f, 1.0f, 1.0f},
|
.color = (vec3s){1.0f, 1.0f, 1.0f},
|
||||||
.intensity = 1.0f,
|
.intensity = 0.0f,
|
||||||
});
|
});
|
||||||
#else
|
#else
|
||||||
texture_handle_t hdri = texture_load(HDRI_PATH, false, false, FLOAT_32, &scene->textures);
|
texture_handle_t hdri = texture_load(HDRI_PATH, false, false, FLOAT_32, &scene->textures);
|
||||||
@@ -65,7 +104,7 @@ static bool scene_setup(scene_t* scene)
|
|||||||
|
|
||||||
static bool load_assets(scene_t* scene)
|
static bool load_assets(scene_t* scene)
|
||||||
{
|
{
|
||||||
#if 0
|
#if 1
|
||||||
mesh_model_handle_t model_handle = mesh_load(SCENE_PATH, scene);
|
mesh_model_handle_t model_handle = mesh_load(SCENE_PATH, scene);
|
||||||
if (!IS_VALID_HANDLE(model_handle))
|
if (!IS_VALID_HANDLE(model_handle))
|
||||||
{
|
{
|
||||||
@@ -211,6 +250,46 @@ static int run_main_loop(render_job_t* job, uint8_t aov_index)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void save_render_output_png(const render_target_t* render_target, const char* output_path)
|
||||||
|
{
|
||||||
|
#ifdef SAVE_OUTPUT
|
||||||
|
if (render_target == NULL || render_target->buffer == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t* png_buffer = (uint8_t*)malloc(render_target->width * render_target->height * 3);
|
||||||
|
if (png_buffer == NULL)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t y = 0; y < render_target->height; y++)
|
||||||
|
{
|
||||||
|
for (uint32_t x = 0; x < render_target->width; x++)
|
||||||
|
{
|
||||||
|
vec4s pixel = render_target_get_pixel(render_target, x, y);
|
||||||
|
pixel = aces_tone_map(pixel);
|
||||||
|
pixel = gamma_correct(pixel, 2.2f);
|
||||||
|
|
||||||
|
size_t index = (size_t)(y * render_target->width + x) * 3;
|
||||||
|
png_buffer[index + 0] = (uint8_t)(fminf(fmaxf(pixel.x, 0.0f), 1.0f) * 255.0f);
|
||||||
|
png_buffer[index + 1] = (uint8_t)(fminf(fmaxf(pixel.y, 0.0f), 1.0f) * 255.0f);
|
||||||
|
png_buffer[index + 2] = (uint8_t)(fminf(fmaxf(pixel.z, 0.0f), 1.0f) * 255.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE* fp = fopen(output_path, "wb");
|
||||||
|
if (fp != NULL)
|
||||||
|
{
|
||||||
|
svpng(fp, render_target->width, render_target->height, png_buffer, 0);
|
||||||
|
fclose(fp);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(png_buffer);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
// int main()
|
// int main()
|
||||||
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR pCmdLine, _In_ int nCmdShow)
|
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR pCmdLine, _In_ int nCmdShow)
|
||||||
{
|
{
|
||||||
@@ -226,12 +305,12 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
|||||||
render_job_t* job = NULL;
|
render_job_t* job = NULL;
|
||||||
|
|
||||||
rendering_config_t config = {
|
rendering_config_t config = {
|
||||||
.width = 1920 / 2,
|
.width = 1920 / 1,
|
||||||
.height = 1080 / 2,
|
.height = 1080 / 1,
|
||||||
.sample_count = 16 * 4,
|
.sample_count = 16 * 1,
|
||||||
.max_depth = 4,
|
.max_depth = 4,
|
||||||
.bucket_size = 64,
|
.bucket_size = 128,
|
||||||
.rendering_mode = RENDER_PROGRESSIVE,
|
.rendering_mode = RENDER_TILE_BASED,
|
||||||
.aov_flags = AOV_BEAUTY,
|
.aov_flags = AOV_BEAUTY,
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -244,6 +323,8 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
|||||||
|
|
||||||
int result = run_main_loop(job, AOV_BEAUTY_INDEX);
|
int result = run_main_loop(job, AOV_BEAUTY_INDEX);
|
||||||
|
|
||||||
|
save_render_output_png(job->aov_target[AOV_BEAUTY_INDEX], OUTPUT_PATH);
|
||||||
|
|
||||||
window_close();
|
window_close();
|
||||||
shutdown_renderer(job, &scene);
|
shutdown_renderer(job, &scene);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user