#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; }