Changed the pixel rendering function to include anti-aliasing using Sobol sampling in Renderer.c. Changed the path tracing function to adjust light shading context and Russian roulette logic in PathTracing.c. Changed the normal calculation logic to use a ternary operator in RayIntersection.c. Changed the Sobol sample calculation for consistency in Debug.c. Removed some items from the project roadmap in README.md. Modified the sample count from 64 to 16 in main.c, affecting rendering quality and performance.
125 lines
4.8 KiB
C
125 lines
4.8 KiB
C
#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:
|
|
case DEBUG_UV:
|
|
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, debug_flag_t 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);
|
|
float inv_sample = 1.0f / (float)sample_count;
|
|
|
|
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++)
|
|
{
|
|
// TODO: Hash it
|
|
uint32_t sobol_idx = pixel_id * config->sample_count + (k + 1);
|
|
|
|
// Apply AA
|
|
float du = sobol_sample(sobol_idx, PRNG_FILTER_U);
|
|
float dv = sobol_sample(sobol_idx, PRNG_FILTER_V);
|
|
vec2s position_ndc = compute_ndc((float)x + du, (float)y + dv, config->width, config->height);
|
|
|
|
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 = ray_create(scene->camera.position, glms_vec3_normalize(glms_vec3_sub(image_plane_point, scene->camera.position)));
|
|
|
|
vec4s out_color = flag == 0 ? path_trace(scene, ray, sobol_idx, config->max_depth) : render_debug(scene, ray, sobol_idx, flag);
|
|
accumulated_color = glms_vec4_add(accumulated_color, out_color);
|
|
}
|
|
|
|
*pixel_color = glms_vec4_scale(accumulated_color, inv_sample);
|
|
}
|
|
|
|
// 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->debug_flag, &pixel_color);
|
|
render_target_set_pixel(job->render_target, (uint32_t)x, (uint32_t)y, pixel_color);
|
|
}
|
|
}
|
|
tile_done:;
|
|
}
|
|
|
|
job->is_done = true;
|
|
}
|