Update project files and enhance rendering system
Added: - Updated `.gitignore` to ignore the `[Bb]uild/` directory. - Additional tasks added to the roadmap in `README.md` for light unit standardization and GPU backend support. Changed: - Removed line in `settings.json` that disabled error squiggles for C/C++ code. - Modified `Triangle.h` to include `material_id` in `triangle_t` and reorganized properties. - Reordered parameters in `triangle_collection_init` for clarity. - Updated `shading_context_t` in `Material.h` and added size parameter to `material_create`. - Streamlined initialization in `scene_init` and updated `scene_free` for proper resource management. - Updated `window_create` in `Window.h` to accept a `render_job_t` parameter. - Introduced `renderer_start` in `Renderer.c` to handle rendering jobs and optimized pixel rendering logic.
This commit is contained in:
@@ -12,7 +12,7 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
const material_collection_t* materials = &scene->materials;
|
||||
const light_collection_t* lights = &scene->lights;
|
||||
|
||||
vec4s accumulated_color = glms_vec4_zero();
|
||||
vec4s accumulated_color = (vec4s){0.0f, 0.0f, 0.0f, 1.0f};
|
||||
vec3s throughput = glms_vec3_one();
|
||||
|
||||
ray_t active_ray = ray;
|
||||
@@ -34,14 +34,14 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
float weight = power_heuristic(pdf_bsdf, pdf_nee);
|
||||
sky_light = glms_vec3_scale(sky_light, weight);
|
||||
}
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 1.0f)); // TODO: Physical Skybox
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f)); // TODO: Physical Skybox
|
||||
break;
|
||||
}
|
||||
|
||||
// Add the emission of the hit material to the accumulated color
|
||||
material_t* hit_material = &materials->buffer[triangles->buffer[closest_hit.triangle_id].material_id];
|
||||
vec3s emission = hit_material->emission;
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(glms_vec3_mul(throughput, emission), 1.0f));
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(glms_vec3_mul(throughput, emission), 0.0f));
|
||||
|
||||
light_shading_context_t light_context = {
|
||||
.normal = closest_hit.normal,
|
||||
@@ -59,11 +59,11 @@ vec4s path_trace(const scene_t* scene, ray_t ray, uint32_t sample_index, uint16_
|
||||
for (uint32_t i = 0; i < lights->directional_light_count; i++)
|
||||
{
|
||||
vec3s l = evaluate_bsdf_directional(lights->directional_lights[i], &light_context, throughput, sample_index);
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(l, 1.0f));
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(l, 0.0f));
|
||||
}
|
||||
|
||||
vec3s sky_light = evaluate_bsdf_sky(scene, &light_context, throughput, sample_index);
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 1.0f));
|
||||
accumulated_color = glms_vec4_add(accumulated_color, glms_vec4(sky_light, 0.0f));
|
||||
|
||||
// Bounce and prepare for the next iteration
|
||||
vec3s wo = glms_vec3_negate(active_ray.direction); // We need to negate the direction of the incoming ray
|
||||
|
||||
@@ -6,6 +6,10 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
|
||||
{
|
||||
hit_result_t result = {0};
|
||||
|
||||
vec3s edge1 = glms_vec3_sub(triangle.point_2, triangle.point_1);
|
||||
vec3s edge2 = glms_vec3_sub(triangle.point_3, triangle.point_2);
|
||||
vec3s edge3 = glms_vec3_sub(triangle.point_1, triangle.point_3);
|
||||
|
||||
vec3s normal_face = triangle.normal_face;
|
||||
float n_dot_r = glms_vec3_dot(normal_face, ray.direction);
|
||||
if (n_dot_r > 0.0f)
|
||||
@@ -30,9 +34,7 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
|
||||
vec3s intersection_point = glms_vec3_add(ray.origin, glms_vec3_scale(ray.direction, distance));
|
||||
|
||||
// Check if the intersection point is inside the triangle using barycentric coordinates
|
||||
vec3s edge1 = glms_vec3_sub(triangle.point_2, triangle.point_1);
|
||||
vec3s edge2 = glms_vec3_sub(triangle.point_3, triangle.point_2);
|
||||
vec3s edge3 = glms_vec3_sub(triangle.point_1, triangle.point_3);
|
||||
|
||||
vec3s vp = glms_vec3_sub(intersection_point, triangle.point_1);
|
||||
vec3s vp2 = glms_vec3_sub(intersection_point, triangle.point_2);
|
||||
vec3s vp3 = glms_vec3_sub(intersection_point, triangle.point_3);
|
||||
@@ -60,11 +62,7 @@ hit_result_t ray_intersect_triangle(ray_t ray, triangle_t triangle)
|
||||
|
||||
bool ray_intersect_aabb(ray_t ray, aabb_t aabb, float* enter_out, float* exit_out)
|
||||
{
|
||||
// vec3s inv_dir = glms_vec3_div(glms_vec3_one(), ray.direction);
|
||||
vec3s inv_dir;
|
||||
inv_dir.x = ray.direction.x != 0.0f ? 1.0f / ray.direction.x : FLT_MAX;
|
||||
inv_dir.y = ray.direction.y != 0.0f ? 1.0f / ray.direction.y : FLT_MAX;
|
||||
inv_dir.z = ray.direction.z != 0.0f ? 1.0f / ray.direction.z : FLT_MAX;
|
||||
vec3s inv_dir = glms_vec3_div(glms_vec3_one(), ray.direction);
|
||||
float t_near = -FLT_MIN;
|
||||
float t_far = FLT_MAX;
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "Geometry/Triangle.h"
|
||||
|
||||
bool triangle_collection_init(size_t size, triangle_collection_t* triangles)
|
||||
bool triangle_collection_init(triangle_collection_t* triangles, size_t size)
|
||||
{
|
||||
if (size > UINT64_MAX)
|
||||
{
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#include "Material/Material.h"
|
||||
#include <string.h>
|
||||
|
||||
bool material_collection_init(size_t size, material_collection_t* materials)
|
||||
{
|
||||
if (size > 254)
|
||||
{
|
||||
size = 254; // Limit the count to 254 to fit in a uint8_t
|
||||
size = 254;
|
||||
}
|
||||
|
||||
material_collection_t temp = {0};
|
||||
@@ -40,12 +41,21 @@ void material_collection_free(material_collection_t* materials)
|
||||
{
|
||||
if (materials->buffer != NULL)
|
||||
{
|
||||
for (uint8_t i; i < materials->count; i++)
|
||||
{
|
||||
void* data = materials->buffer[i].data;
|
||||
if (data != NULL)
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
}
|
||||
|
||||
free(materials->buffer);
|
||||
materials->buffer = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, void* data, material_collection_t* collection)
|
||||
material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample_pdf, evaluate_bsdf_f evaluate, void* data, size_t size_of_data, material_collection_t* collection)
|
||||
{
|
||||
material_t material = {0};
|
||||
|
||||
@@ -57,7 +67,13 @@ material_entity_t material_create(sample_bsdf_f sample, sample_bsdf_pdf_f sample
|
||||
material.sample_bsdf = sample;
|
||||
material.sample_bsdf_pdf = sample_pdf;
|
||||
material.evaluate_bsdf = evaluate;
|
||||
material.data = data;
|
||||
material.data = malloc(size_of_data);
|
||||
if (material.data == NULL)
|
||||
{
|
||||
return (material_entity_t){.id = 255};
|
||||
}
|
||||
|
||||
memcpy(material.data, data, size_of_data);
|
||||
|
||||
material_entity_t entity = {.id = collection->count};
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ static vec3s DIELECTRIC_REFLECTIVE = {0.04f, 0.04f, 0.04f}; // Standard dielectr
|
||||
// Simple lit, but keep it unbiased as much as possible
|
||||
vec3s sample_bsdf_simple_lit(const void* data, vec3s normal, vec3s wo, uint32_t sample_index, uint32_t bounce, float* pdf_out)
|
||||
{
|
||||
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
||||
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
||||
|
||||
//TODO: having a bsdf data struct to avoid recomputing the same thing in both sample and evaluate
|
||||
vec3s f0 = glms_vec3_lerp(DIELECTRIC_REFLECTIVE, shading_data.albedo, shading_data.metallic);
|
||||
@@ -129,8 +129,8 @@ float sample_bsdf_pdf_simple_lit(const void* data, vec3s normal, vec3s wo, vec3s
|
||||
|
||||
vec3s evaluate_bsdf_simple_lit(const shading_context_t* context, const void* data)
|
||||
{
|
||||
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
||||
shading_context_t shading_context = *context;
|
||||
simple_lit_data_t shading_data = *(const simple_lit_data_t*)data;
|
||||
shading_context_t shading_context = *context;
|
||||
|
||||
vec3s h = glms_vec3_normalize(glms_vec3_add(shading_context.wi, shading_context.wo));
|
||||
float n_dot_l = fmaxf(FLT_EPSILON, glms_vec3_dot(shading_context.normal, shading_context.wi));
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#include "Debug.h"
|
||||
#include "Rendering/Debug.h"
|
||||
#include "Algorithm/RayIntersection.h"
|
||||
|
||||
static void ray_intersect_bvh_count(ray_t ray, bvh_tree_t bvh_tree, uint64_t node_index, uint32_t* count_out)
|
||||
@@ -6,7 +6,6 @@
|
||||
bool render_target_init(uint32_t width, uint32_t height, render_target_t* render_target)
|
||||
|
||||
{
|
||||
*render_target = (render_target_t){0};
|
||||
render_target->width = width;
|
||||
render_target->height = height;
|
||||
|
||||
|
||||
129
source/Rendering/Renderer.c
Normal file
129
source/Rendering/Renderer.c
Normal file
@@ -0,0 +1,129 @@
|
||||
#include "Rendering/Renderer.h"
|
||||
#include "Algorithm/PathTracing.h"
|
||||
|
||||
#define FLIP_Y
|
||||
|
||||
static inline void ensure_camera_aspect_ratio(camera_t* camera, const rendering_config_t* config)
|
||||
{
|
||||
float aspect_ratio = (float)config->width / config->height;
|
||||
if (fabsf((float)config->width / config->height - camera->size_x / camera->size_y) > 0.001f)
|
||||
{
|
||||
camera->size_y = camera->size_x / aspect_ratio;
|
||||
camera->fov_y = 2.0f * (float)atan(camera->size_x / (2.0f * camera->focal_length * aspect_ratio));
|
||||
}
|
||||
}
|
||||
|
||||
static inline vec2s compute_ndc(float x, float y, uint32_t width, uint32_t height)
|
||||
{
|
||||
return (vec2s){
|
||||
.x = x / (float)width,
|
||||
#ifdef FLIP_Y
|
||||
.y = 1.0f - y / (float)height
|
||||
#else
|
||||
.y = y / (float)height
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
static inline uint16_t get_sample_count(uint16_t sample_count, int flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case DEBUG_BVH:
|
||||
case DEBUG_SOBOL:
|
||||
return 1;
|
||||
default:
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
static void render_pixel(const rendering_config_t* config, scene_t* scene, vec3s coord, uint32_t x, uint32_t y, int flag, vec4s* pixel_color)
|
||||
{
|
||||
vec4s accumulated_color = glms_vec4_zero();
|
||||
uint32_t pixel_id = y * config->width + x;
|
||||
|
||||
uint16_t sample_count = get_sample_count(config->sample_count, flag);
|
||||
vec2s position_ndc = compute_ndc((float)x, (float)y, config->width, config->height);
|
||||
vec3s camera_right = quat_get_right(scene->camera.rotation);
|
||||
vec3s camera_up = quat_get_up(scene->camera.rotation);
|
||||
|
||||
for (uint16_t k = 0; k < sample_count; k++)
|
||||
{
|
||||
float screen_x = position_ndc.x * 2.0f - 1.0f;
|
||||
float screen_y = position_ndc.y * 2.0f - 1.0f;
|
||||
float sensor_offset_x = screen_x * scene->camera.size_x * 0.5f;
|
||||
float sensor_offset_y = screen_y * scene->camera.size_y * 0.5f;
|
||||
|
||||
vec3s image_plane_point = coord;
|
||||
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_right, sensor_offset_x));
|
||||
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_up, sensor_offset_y));
|
||||
|
||||
ray_t ray = {
|
||||
.origin = scene->camera.position,
|
||||
.direction = glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position))
|
||||
};
|
||||
|
||||
vec4s out_color = glms_vec4_zero();
|
||||
|
||||
if (flag != 0)
|
||||
{
|
||||
out_color = render_debug(scene, ray, k, flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Hash it
|
||||
uint32_t sobol_idx = pixel_id * config->sample_count + (k + 1);
|
||||
out_color = path_trace(scene, ray, sobol_idx, config->max_depth);
|
||||
}
|
||||
|
||||
accumulated_color = glms_vec4_add(accumulated_color, out_color);
|
||||
}
|
||||
|
||||
*pixel_color = glms_vec4_scale(accumulated_color, 1.0f / (float)sample_count);
|
||||
}
|
||||
|
||||
// TODO: Progressive rendering
|
||||
void renderer_start(render_job_t* job)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&job->scene->camera, job->config);
|
||||
|
||||
uint32_t tile_count_x = (job->config->width + job->config->bucket_size - 1) / job->config->bucket_size;
|
||||
uint32_t tile_count_y = (job->config->height + job->config->bucket_size - 1) / job->config->bucket_size;
|
||||
uint32_t tile_count = tile_count_x * tile_count_y;
|
||||
|
||||
float inv_sample = 1.0f / job->config->sample_count;
|
||||
vec3s coord = glms_vec3_add(job->scene->camera.position, glms_vec3_scale(quat_get_forward(job->scene->camera.rotation), job->scene->camera.focal_length));
|
||||
|
||||
int64_t x, y, tile_index; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers. To avoid overflow, we need to use int64_t
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_count_x, tile_count_y, tile_count, coord, inv_sample, job) \
|
||||
private(x, y, tile_index)
|
||||
for (tile_index = 0; tile_index < tile_count; tile_index++)
|
||||
{
|
||||
uint32_t tile_x_0 = (uint32_t)tile_index % tile_count_x * job->config->bucket_size;
|
||||
uint32_t tile_y_0 = (uint32_t)tile_index / tile_count_x * job->config->bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + job->config->bucket_size, job->config->width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + job->config->bucket_size, job->config->height);
|
||||
|
||||
for (y = tile_y_0; y < tile_y_1; y++)
|
||||
{
|
||||
for (x = tile_x_0; x < tile_x_1; x++)
|
||||
{
|
||||
if (job->is_done)
|
||||
{
|
||||
goto tile_done;
|
||||
}
|
||||
|
||||
vec4s pixel_color;
|
||||
render_pixel(job->config, job->scene, coord, (uint32_t)x, (uint32_t)y, job->rendering_flag, &pixel_color);
|
||||
render_target_set_pixel(job->render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
||||
}
|
||||
}
|
||||
tile_done:;
|
||||
}
|
||||
|
||||
job->is_done = true;
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
#include "Debug.h"
|
||||
#include "Rendering/Scene.h"
|
||||
#include "Algorithm/PathTracing.h"
|
||||
|
||||
#define FLIP_Y
|
||||
|
||||
bool scene_init(uint64_t triangle_count, uint8_t material_count, uint32_t punctual_light_count, scene_t* scene)
|
||||
bool scene_init(scene_t* scene, uint64_t triangle_count, uint8_t material_count, uint32_t punctual_light_count)
|
||||
{
|
||||
scene_t temp = {0};
|
||||
|
||||
if (!triangle_collection_init(triangle_count, &temp.triangles))
|
||||
if (!triangle_collection_init(&temp.triangles, triangle_count))
|
||||
{
|
||||
goto triangle_failed;
|
||||
}
|
||||
@@ -42,14 +38,6 @@ triangle_failed:
|
||||
return false;
|
||||
}
|
||||
|
||||
void scene_free(scene_t* scene)
|
||||
{
|
||||
bvh_tree_free(&scene->bvh_tree);
|
||||
triangle_collection_free(&scene->triangles);
|
||||
material_collection_free(&scene->materials);
|
||||
light_collection_free(&scene->lights);
|
||||
}
|
||||
|
||||
bool scene_build_bvh(scene_t* scene)
|
||||
{
|
||||
if (scene == NULL || scene->triangles.count == 0)
|
||||
@@ -68,193 +56,10 @@ bool scene_build_bvh(scene_t* scene)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void ensure_camera_aspect_ratio(camera_t* camera, rendering_config_t config)
|
||||
void scene_free(scene_t* scene)
|
||||
{
|
||||
if (fabsf((float)config.width / config.height - camera->aspect_ratio) > FLT_EPSILON)
|
||||
{
|
||||
*camera = camera_create(
|
||||
camera->position,
|
||||
camera->rotation,
|
||||
camera->focal_length,
|
||||
camera->size_x,
|
||||
(float)config.width / (float)config.height
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static inline vec2s compute_ndc(float x, float y, uint32_t width, uint32_t height)
|
||||
{
|
||||
return (vec2s){
|
||||
.x = x / (float)width,
|
||||
#ifdef FLIP_Y
|
||||
.y = 1.0f - y / (float)height
|
||||
#else
|
||||
.y = y / (float)height
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
||||
static inline uint16_t get_sample_count(uint16_t sample_count, int flag)
|
||||
{
|
||||
switch (flag)
|
||||
{
|
||||
case DEBUG_BVH:
|
||||
case DEBUG_SOBOL:
|
||||
return 1;
|
||||
default:
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
return sample_count;
|
||||
}
|
||||
|
||||
static void screen_render_pixel(scene_t* scene, rendering_config_t config, vec3s coord, uint32_t x, uint32_t y, int flag, vec4s* pixel_color)
|
||||
{
|
||||
vec4s accumulated_color = glms_vec4_zero();
|
||||
*pixel_color = accumulated_color;
|
||||
uint32_t pixel_id = y * config.width + x;
|
||||
|
||||
uint16_t sample_count = get_sample_count(config.sample_count, flag);
|
||||
vec2s position_ndc = compute_ndc((float)x, (float)y, config.width, config.height);
|
||||
vec3s camera_right = quat_get_right(scene->camera.rotation);
|
||||
vec3s camera_up = quat_get_up(scene->camera.rotation);
|
||||
|
||||
for (uint16_t k = 0; k < sample_count; k++)
|
||||
{
|
||||
float screen_x = position_ndc.x * 2.0f - 1.0f;
|
||||
float screen_y = position_ndc.y * 2.0f - 1.0f;
|
||||
float sensor_offset_x = screen_x * scene->camera.size_x / 2.0f;
|
||||
float sensor_offset_y = screen_y * scene->camera.size_y / 2.0f;
|
||||
|
||||
vec3s image_plane_point = coord;
|
||||
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_right, sensor_offset_x));
|
||||
image_plane_point = glms_vec3_add(image_plane_point, glms_vec3_scale(camera_up, sensor_offset_y));
|
||||
|
||||
ray_t ray = {
|
||||
.origin = scene->camera.position,
|
||||
.direction = glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position))
|
||||
};
|
||||
|
||||
vec4s out_color = glms_vec4_zero();
|
||||
|
||||
if (flag != 0)
|
||||
{
|
||||
out_color = render_debug(scene, ray, k, flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: Hash it
|
||||
uint32_t sobol_idx = pixel_id * config.sample_count + (k + 1);
|
||||
out_color = path_trace(scene, ray, sobol_idx, config.max_depth);
|
||||
}
|
||||
|
||||
accumulated_color = glms_vec4_add(accumulated_color, out_color);
|
||||
}
|
||||
|
||||
*pixel_color = glms_vec4_scale(accumulated_color, 1.0f / (float)sample_count);
|
||||
}
|
||||
|
||||
// TODO: This should be moved to renderer
|
||||
bool scene_render_tile(scene_t* scene, rendering_context_t* ctx, rendering_config_t config, uint32_t tile_index, int rendering_flag, render_target_t* render_target, tile_t* tile_out)
|
||||
{
|
||||
if (ctx->is_done)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!ctx->is_init)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&scene->camera, config);
|
||||
ctx->tile_count_x = (config.width + config.bucket_size - 1) / config.bucket_size;
|
||||
ctx->tile_count_y = (config.height + config.bucket_size - 1) / config.bucket_size;
|
||||
ctx->coord = glms_vec3_add(scene->camera.position, glms_vec3_scale(quat_get_forward(scene->camera.rotation), scene->camera.focal_length));
|
||||
ctx->is_init = true;
|
||||
}
|
||||
|
||||
if (tile_index >= ctx->tile_count_x * ctx->tile_count_y)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t tile_x_0 = tile_index % ctx->tile_count_x * config.bucket_size;
|
||||
uint32_t tile_y_0 = tile_index / ctx->tile_count_x * config.bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + config.bucket_size, config.width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + config.bucket_size, config.height);
|
||||
|
||||
tile_out->x = tile_x_0;
|
||||
tile_out->y = tile_y_0;
|
||||
tile_out->width = tile_x_1 - tile_x_0;
|
||||
tile_out->height = tile_y_1 - tile_y_0;
|
||||
|
||||
int64_t x, y; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers. To avoid overflow, we need to use int64_t
|
||||
rendering_config_t config_copy = config; // Have to copy it, otherwise OpenMP will cause it become invalid. Not sure if this is a bug or not.
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_x_0, tile_x_1, tile_y_0, tile_y_1, config_copy) \
|
||||
private(x, y)
|
||||
for (y = tile_y_0; y < tile_y_1; y++)
|
||||
{
|
||||
for (x = tile_x_0; x < tile_x_1; x++)
|
||||
{
|
||||
vec4s pixel_color;
|
||||
screen_render_pixel(scene, config_copy, ctx->coord, (uint32_t)x, (uint32_t)y, rendering_flag, &pixel_color);
|
||||
render_target_set_pixel(render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
||||
}
|
||||
}
|
||||
|
||||
if (tile_index == ctx->tile_count_x * ctx->tile_count_y - 1)
|
||||
{
|
||||
ctx->is_done = true;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: This should be moved to renderer
|
||||
bool scene_render(scene_t* scene, rendering_config_t config, int rendering_flag, render_target_t* render_target)
|
||||
{
|
||||
ensure_camera_aspect_ratio(&scene->camera, config);
|
||||
|
||||
if (render_target->buffer != NULL)
|
||||
{
|
||||
render_target_free(render_target);
|
||||
}
|
||||
|
||||
if (render_target_init(config.width, config.height, render_target))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t tile_count_x = (config.width + config.bucket_size - 1) / config.bucket_size;
|
||||
uint32_t tile_count_y = (config.height + config.bucket_size - 1) / config.bucket_size;
|
||||
uint32_t tile_count = tile_count_x * tile_count_y;
|
||||
|
||||
float inv_sample = 1.0f / config.sample_count;
|
||||
vec3s coord = glms_vec3_add(scene->camera.position, glms_vec3_scale(quat_get_forward(scene->camera.rotation), scene->camera.focal_length));
|
||||
|
||||
int64_t x, y, tile_index; // OpenMP requires these to be declared outside the parallel region. Also, they need to be signed integers. To avoid overflow, we need to use int64_t
|
||||
rendering_config_t config_copy = config;
|
||||
|
||||
#pragma omp parallel for schedule(dynamic, 1) default(none) \
|
||||
shared(tile_count_x, tile_count_y, tile_count, config_copy, coord, inv_sample, render_target) \
|
||||
private(x, y, tile_index)
|
||||
for (tile_index = 0; tile_index < tile_count; tile_index++)
|
||||
{
|
||||
uint32_t tile_x_0 = (uint32_t)tile_index % tile_count_x * config.bucket_size;
|
||||
uint32_t tile_y_0 = (uint32_t)tile_index / tile_count_x * config.bucket_size;
|
||||
uint32_t tile_x_1 = (uint32_t)fmin(tile_x_0 + config.bucket_size, config.width);
|
||||
uint32_t tile_y_1 = (uint32_t)fmin(tile_y_0 + config.bucket_size, config.height);
|
||||
|
||||
for (y = tile_y_0; y < tile_y_1; y++)
|
||||
{
|
||||
for (x = tile_x_0; x < tile_x_1; x++)
|
||||
{
|
||||
vec4s pixel_color;
|
||||
screen_render_pixel(scene, config_copy, coord, (uint32_t)x, (uint32_t)y, rendering_flag, &pixel_color);
|
||||
render_target_set_pixel(render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
bvh_tree_free(&scene->bvh_tree);
|
||||
triangle_collection_free(&scene->triangles);
|
||||
material_collection_free(&scene->materials);
|
||||
light_collection_free(&scene->lights);
|
||||
}
|
||||
|
||||
@@ -48,7 +48,13 @@ static LRESULT CALLBACK wndow_proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lp
|
||||
}
|
||||
}
|
||||
|
||||
int window_create(const char* title, HINSTANCE hInst, int width, int height)
|
||||
static DWORD WINAPI RenderThreadProc(LPVOID lpParameter) {
|
||||
render_job_t* job = (render_job_t*)lpParameter;
|
||||
renderer_start(job);
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool window_create(const char* title, HINSTANCE hInst, int width, int height, render_job_t* render_job)
|
||||
{
|
||||
RECT rect = {0, 0, width, height};
|
||||
AdjustWindowRect(&rect, WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, FALSE);
|
||||
@@ -75,15 +81,21 @@ int window_create(const char* title, HINSTANCE hInst, int width, int height)
|
||||
|
||||
if (!hwnd)
|
||||
{
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
ShowWindow(hwnd, SW_SHOW);
|
||||
|
||||
return 0;
|
||||
render_thread = CreateThread(NULL, 0, RenderThreadProc, render_job, 0, NULL);
|
||||
if (render_thread == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void window_update_pixels(vec4s color, int pixel_x, int pixel_y)
|
||||
void window_update_pixel(vec4s color, int pixel_x, int pixel_y)
|
||||
{
|
||||
int pixel_index = (pixel_y * window_width + pixel_x) * 4;
|
||||
|
||||
@@ -103,3 +115,11 @@ void window_refresh_region(int pixel_x, int pixel_y, int region_width, int regio
|
||||
|
||||
ReleaseDC(hwnd, hdc);
|
||||
}
|
||||
|
||||
void window_close()
|
||||
{
|
||||
PostMessage(hwnd, WM_DESTROY, 0, 0);
|
||||
WaitForSingleObject(render_thread, INFINITE);
|
||||
CloseHandle(render_thread);
|
||||
DestroyWindow(hwnd);
|
||||
}
|
||||
|
||||
331
source/main.c
331
source/main.c
@@ -3,8 +3,6 @@
|
||||
#include <svpng.inc>
|
||||
|
||||
#include "Algorithm/Sobol.h"
|
||||
#include "Debug.h"
|
||||
// #include "Geometry/GeometryUtilities.h"
|
||||
#include "Geometry/Mesh.h"
|
||||
#include "Lighting/SkyLight.h"
|
||||
#include "Material/SimpleLit.h"
|
||||
@@ -12,26 +10,169 @@
|
||||
#include "Rendering/Scene.h"
|
||||
#include "Window.h"
|
||||
|
||||
static void save_img(render_target_t* source, uint32_t width, uint32_t height, const char* filename)
|
||||
#define TITLE "Path Tracing"
|
||||
#define SPONZA_PATH "./assets/sponza.obj"
|
||||
|
||||
static bool scene_setup(scene_t* scene)
|
||||
{
|
||||
FILE* file_stream;
|
||||
fopen_s(&file_stream, filename, "wb");
|
||||
if (file_stream == NULL)
|
||||
if (!scene_init(scene, 67000, 8, 1))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
scene->camera.position = (vec3s){-7.5f, 2.0f, 0.0f};
|
||||
scene->camera.rotation = euler_to_quat(10.0f, -90.0f, 0.0f);
|
||||
|
||||
// TODO: Standardize light unit
|
||||
light_entity_t sun = light_create_directional_light(&scene->lights);
|
||||
directional_light_t* sun_light = &scene->lights.directional_lights[sun.id];
|
||||
sun_light->direction = glms_vec3_normalize((vec3s){-0.5f, 1.0f, 0.15f});
|
||||
sun_light->color = (vec3s){1.0f, 0.93f, 0.87f};
|
||||
sun_light->intensity = 2.0f;
|
||||
sun_light->angular_diameter = 0.53f;
|
||||
|
||||
scene->lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t)
|
||||
{
|
||||
.color = (vec3s){0.73f, 0.82f, 1.0f},
|
||||
.intensity = 1.0f,
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool load_assets(scene_t* scene)
|
||||
{
|
||||
simple_lit_data_t floor_lit_data =
|
||||
{
|
||||
.albedo = (vec3s){1.0f, 1.0f, 1.0f},
|
||||
.roughness = 0.95f,
|
||||
.metallic = 0.0f,
|
||||
};
|
||||
|
||||
material_entity_t floor_material = material_create_simple_lit(&floor_lit_data, &scene->materials);
|
||||
|
||||
mesh_load(SPONZA_PATH, floor_material.id, &scene->triangles, &scene->materials);
|
||||
|
||||
return scene_build_bvh(scene);
|
||||
}
|
||||
|
||||
static bool initialize_renderer(const rendering_config_t* config, render_job_t** outJob, render_target_t* outImg, scene_t* outScene)
|
||||
{
|
||||
if (!scene_setup(outScene))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!load_assets(outScene))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!scene_build_bvh(outScene))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!render_target_init(config->width, config->height, outImg))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
render_job_t* job = malloc(sizeof(render_job_t));
|
||||
if (job == NULL)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*job = (render_job_t){
|
||||
.scene = outScene,
|
||||
.render_target = outImg,
|
||||
.config = config,
|
||||
|
||||
.rendering_type = TILE_BASED,
|
||||
.rendering_flag = DEBUG_NONE,
|
||||
.is_done = false,
|
||||
};
|
||||
|
||||
sobol_init();
|
||||
|
||||
*outJob = job;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void shutdown_renderer(render_job_t* job, render_target_t* img, scene_t* scene)
|
||||
{
|
||||
if (job != NULL)
|
||||
{
|
||||
free(job);
|
||||
}
|
||||
|
||||
render_target_free(img);
|
||||
scene_free(scene);
|
||||
}
|
||||
|
||||
static void update_pixel_buffer(render_target_t* render_target)
|
||||
{
|
||||
if (render_target == NULL || render_target->buffer == NULL)
|
||||
{
|
||||
fprintf(stderr, "Failed to open file for writing: %s\n", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
unsigned char* img_buffer = render_target_to_char(source);
|
||||
svpng(file_stream, width, height, img_buffer, 1);
|
||||
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 = gamma_correct(pixel, 2.2f);
|
||||
pixel = aces_tone_map(pixel);
|
||||
window_update_pixel(pixel, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(file_stream);
|
||||
free(img_buffer);
|
||||
window_refresh_region(0, 0, render_target->width, render_target->height);
|
||||
}
|
||||
|
||||
static int run_main_loop(render_job_t* job, render_target_t* img)
|
||||
{
|
||||
MSG msg;
|
||||
bool running = true;
|
||||
|
||||
while (running)
|
||||
{
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
switch (msg.message)
|
||||
{
|
||||
case WM_QUIT:
|
||||
case WM_CLOSE:
|
||||
{
|
||||
running = false;
|
||||
job->is_done = true;
|
||||
}
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
if (!job->is_done && running)
|
||||
{
|
||||
update_pixel_buffer(img);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// int main()
|
||||
int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ PWSTR pCmdLine, _In_ int nCmdShow)
|
||||
{
|
||||
omp_set_num_threads(16);
|
||||
|
||||
scene_t scene;
|
||||
render_target_t img;
|
||||
render_job_t* job = NULL;
|
||||
|
||||
rendering_config_t config = {
|
||||
.width = 1920 / 4,
|
||||
.height = 1080 / 4,
|
||||
@@ -40,166 +181,22 @@ int WINAPI wWinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance,
|
||||
.bucket_size = 64,
|
||||
};
|
||||
|
||||
int result = window_create("Path Tracing", hInstance, config.width, config.height);
|
||||
if (result != 0)
|
||||
if (!initialize_renderer(&config, &job, &img, &scene))
|
||||
{
|
||||
fprintf(stderr, "Failed to create window\n");
|
||||
shutdown_renderer(job, &img, &scene);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!window_create(TITLE, hInstance, config.width, config.height, job))
|
||||
{
|
||||
shutdown_renderer(job, &img, &scene);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#pragma region SceneSetup
|
||||
omp_set_num_threads(16);
|
||||
sobol_init();
|
||||
int result = run_main_loop(job, &img);
|
||||
|
||||
scene_t scene;
|
||||
if (!scene_init(67000, 8, 1, &scene))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
window_close();
|
||||
shutdown_renderer(job, &img, &scene);
|
||||
|
||||
scene.camera.position = (vec3s){-7.5f, 2.0f, 0.0f};
|
||||
scene.camera.rotation = euler_to_quat(10.0f, -90.0f, 0.0f);
|
||||
|
||||
light_entity_t sun = light_create_directional_light(&scene.lights);
|
||||
directional_light_t* sun_light = &scene.lights.directional_lights[sun.id];
|
||||
sun_light->direction = glms_vec3_normalize((vec3s){-0.5f, 1.0f, 0.15f});
|
||||
sun_light->color = (vec3s){1.0f, 0.93f, 0.87f};
|
||||
sun_light->intensity = 2.0f;
|
||||
sun_light->angular_diameter = 0.53f;
|
||||
|
||||
scene.lights.sky_light = sky_create_constant_sky(&(constant_sky_data_t){
|
||||
.color = (vec3s){0.73f, 0.82f, 1.0f},
|
||||
.intensity = 1.0f,
|
||||
});
|
||||
|
||||
// simple_lit_data_t gray_lit_data =
|
||||
// {
|
||||
// .albedo = (vec3s){0.73f, 0.73f, 0.73f},
|
||||
// .roughness = 0.5f,
|
||||
// .metallic = 0.0f,
|
||||
// };
|
||||
// simple_lit_data_t blue_lit_data =
|
||||
// {
|
||||
// .albedo = (vec3s){0.0f, 0.0f, 1.0f},
|
||||
// .roughness = 0.5f,
|
||||
// .metallic = 0.0f,
|
||||
// };
|
||||
// simple_lit_data_t red_lit_data =
|
||||
// {
|
||||
// .albedo = (vec3s){1.0f, 0.0f, 0.0f},
|
||||
// .roughness = 0.5f,
|
||||
// .metallic = 0.0f,
|
||||
// };
|
||||
// simple_lit_data_t green_lit_data =
|
||||
// {
|
||||
// .albedo = (vec3s){0.0f, 1.0f, 0.0f},
|
||||
// .roughness = 0.5f,
|
||||
// .metallic = 0.0f,
|
||||
// };
|
||||
simple_lit_data_t floor_lit_data = {
|
||||
.albedo = (vec3s){1.0f, 1.0f, 1.0f},
|
||||
.roughness = 0.95f,
|
||||
.metallic = 0.0f,
|
||||
};
|
||||
// material_entity_t gray_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
|
||||
// material_entity_t gray_light_material = material_create_simple_lit(&gray_lit_data, &scene.materials);
|
||||
// material_entity_t blue_material = material_create_simple_lit(&blue_lit_data, &scene.materials);
|
||||
// material_entity_t red_material = material_create_simple_lit(&red_lit_data, &scene.materials);
|
||||
// material_entity_t green_material = material_create_simple_lit(&green_lit_data, &scene.materials);
|
||||
material_entity_t floor_material = material_create_simple_lit(&floor_lit_data, &scene.materials);
|
||||
|
||||
// scene.materials.buffer[gray_light_material.id].emission = (vec3s){10.0f, 10.0f, 10.0f};
|
||||
|
||||
mesh_load("./assets/sponza.obj", floor_material.id, &scene.triangles, &scene.materials);
|
||||
|
||||
// quad_create(
|
||||
// (vec3s){0.0f, 3.95f, 0.0f},
|
||||
// (vec3s){0.0f, -1.0f, 0.0f},
|
||||
// (vec3s){0.0f, 0.0f, 1.0f},
|
||||
// 1.0f, gray_light_material.id, &scene.triangles
|
||||
// );
|
||||
// quad_create(
|
||||
// (vec3s){0.0f, 0.0f, 0.0f},
|
||||
// (vec3s){0.0f, 1.0f, 0.0f},
|
||||
// (vec3s){0.0f, 0.0f, 1.0f},
|
||||
// 4.0f, floor_material.id, &scene.triangles
|
||||
// );
|
||||
// quad_create(
|
||||
// (vec3s){0.0f, 4.0f, 0.0f},
|
||||
// (vec3s){0.0f, -1.0f, 0.0f},
|
||||
// (vec3s){0.0f, 0.0f, 1.0f},
|
||||
// 4.0f, gray_material.id, &scene.triangles
|
||||
// );
|
||||
// quad_create(
|
||||
// (vec3s){0.0f, 2.0f, -2.0f},
|
||||
// (vec3s){0.0f, 0.0f, 1.0f},
|
||||
// (vec3s){0.0f, 1.0f, 0.0f},
|
||||
// 4.0f, green_material.id, &scene.triangles
|
||||
// );
|
||||
// quad_create(
|
||||
// (vec3s){-2.0f, 2.0f, 0.0f},
|
||||
// (vec3s){1.0f, 0.0f, 0.0f},
|
||||
// (vec3s){0.0f, 1.0f, 0.0f},
|
||||
// 4.0f, blue_material.id, &scene.triangles
|
||||
// );
|
||||
// quad_create(
|
||||
// (vec3s){2.0f, 2.0f, 0.0f},
|
||||
// (vec3s){-1.0f, 0.0f, 0.0f},
|
||||
// (vec3s){0.0f, 1.0f, 0.0f},
|
||||
// 4.0f, red_material.id, &scene.triangles
|
||||
// );
|
||||
|
||||
#pragma endregion
|
||||
|
||||
render_target_t img;
|
||||
render_target_init(config.width, config.height, &img);
|
||||
|
||||
if (!scene_build_bvh(&scene))
|
||||
{
|
||||
fprintf(stderr, "Failed to build BVH\n");
|
||||
goto exit;
|
||||
}
|
||||
|
||||
MSG msg;
|
||||
uint16_t tile_index = 0;
|
||||
tile_t current_tile = {0};
|
||||
rendering_context_t ctx = {0};
|
||||
|
||||
while (true)
|
||||
{
|
||||
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
||||
{
|
||||
if (msg.message == WM_QUIT)
|
||||
{
|
||||
goto exit;
|
||||
}
|
||||
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
// TODO: This hurt performance a lot, need to be optimized
|
||||
if (scene_render_tile(&scene, &ctx, config, tile_index, DEBUG_NONE, &img, ¤t_tile))
|
||||
{
|
||||
for (uint32_t y = current_tile.y; y < current_tile.y + current_tile.height; y++)
|
||||
{
|
||||
for (uint32_t x = current_tile.x; x < current_tile.x + current_tile.width; x++)
|
||||
{
|
||||
vec4s pixel = render_target_get_pixel(&img, x, y);
|
||||
pixel = gamma_correct(pixel, 2.2f);
|
||||
pixel = aces_tone_map(pixel);
|
||||
window_update_pixels(pixel, x, y);
|
||||
}
|
||||
}
|
||||
|
||||
window_refresh_region(current_tile.x, current_tile.y, current_tile.width, current_tile.height);
|
||||
tile_index++;
|
||||
}
|
||||
}
|
||||
|
||||
exit:
|
||||
render_target_free(&img);
|
||||
scene_free(&scene);
|
||||
|
||||
return 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user