diff options
| -rw-r--r-- | include/material.hpp | 2 | ||||
| -rw-r--r-- | include/utils.hpp | 6 | ||||
| -rw-r--r-- | shaders/compute.comp | 45 | ||||
| -rw-r--r-- | src/main.cpp | 10 | ||||
| -rw-r--r-- | src/utils.cpp | 15 | ||||
| -rw-r--r-- | src/vk-api.cpp | 3 |
6 files changed, 52 insertions, 29 deletions
diff --git a/include/material.hpp b/include/material.hpp index 10886da..ee4281b 100644 --- a/include/material.hpp +++ b/include/material.hpp @@ -7,10 +7,12 @@ enum MATERIAL_TYPE: uint32_t { LAMBERTIAN = 0, METAL = 1, DIELECTRIC = 2, + EMISSIVE = 3, }; struct material { color albedo; + color emissive; float fuzz; float ior; uint32_t type; diff --git a/include/utils.hpp b/include/utils.hpp index 11d377a..a95c95d 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -20,19 +20,21 @@ struct input_data { bvh_node nodes[1024]; uint32_t max_bounce; - uint32_t samples_per_pixel; // Output image resolution uint32_t width; uint32_t height; uint32_t sample_index; + + uint32_t spheres_count; + uint32_t padding[3]; }; std::vector<uint8_t> get_shader_code(const char* path); std::vector<uint8_t> get_shader_code(std::string& path); -struct input_data create_inputs(uint32_t width, uint32_t height, uint32_t samples_per_pixel); +struct input_data create_inputs(uint32_t width, uint32_t height); #define PI 3.14159265359 diff --git a/shaders/compute.comp b/shaders/compute.comp index 11256ad..a6fd9e4 100644 --- a/shaders/compute.comp +++ b/shaders/compute.comp @@ -9,11 +9,13 @@ const float PI = 3.141592653589793; const uint LAMBERTIAN_MATERIAL = 0; const uint METAL_MATERIAL = 1; const uint DIELECTRIC_MATERIAL = 2; +const uint EMISSIVE_MATERIAL = 3; const vec3 light_position = vec3(10, -25, 0); struct material { vec4 albedo; + vec4 emissive; float fuzz; float ior; uint type; @@ -73,11 +75,11 @@ layout(set = 0, binding = 0) buffer Inputs { bvh_node[1024] nodes; uint max_bounce; - uint samples_per_pixel; uint width; uint height; - uint sample_index; + + uint spheres_count; uint[3] padding; }; @@ -221,7 +223,7 @@ bool hit_spheres(ray r, float min_t, float max_t, out hit_info info) { float closest_t = max_t; hit_info temp_info; bool hit = false; - for (uint sphere_index = 0; sphere_index < 512; sphere_index++) { + for (uint sphere_index = 0; sphere_index <= spheres_count; sphere_index++) { if (spheres[sphere_index].radius > 0.0 && hit_sphere(spheres[sphere_index], r, min_t, closest_t, temp_info)) { info = temp_info; closest_t = temp_info.t; @@ -280,28 +282,37 @@ vec4 ray_color(ray r, uint seed) { hit_info info; uint bounces = 0; - vec4 final_color = vec4(1.0); + vec4 final_color = vec4(0.0); + vec4 throughput = vec4(1.0); ray current_ray = r; while (hit_spheres(current_ray, 0.001, 1e15, info) && bounces < max_bounce) { ray scattered; - vec4 attenuation; + vec4 attenuation = vec4(0.0); if ((info.mat.type == LAMBERTIAN_MATERIAL && lambertian_scattering(info.mat, current_ray, seed, info, attenuation, scattered)) || (info.mat.type == METAL_MATERIAL && metal_scattering(info.mat, current_ray, seed, info, attenuation, scattered)) || - (info.mat.type == DIELECTRIC_MATERIAL && dielectric_scattering(info.mat, current_ray, seed, info, attenuation, scattered))) { - final_color *= attenuation; + (info.mat.type == DIELECTRIC_MATERIAL && dielectric_scattering(info.mat, current_ray, seed, info, attenuation, scattered)) || + info.mat.type == EMISSIVE_MATERIAL) { + final_color += info.mat.emissive * throughput; + throughput *= attenuation; current_ray = scattered; - } else { - return vec4(0.0); - } - bounces++; - } + // Russian Roulette + // As the throughput gets smaller, the ray is more likely to get terminated early. + // Survivors have their value boosted to make up for fewer samples being in the average. + { + float p = max(throughput.r, max(throughput.g, throughput.b)); + if (rand(seed) > p) { + break; + } - if (bounces == max_bounce) { - return vec4(0.0); + // Add the energy we 'lose' by randomly terminating paths + throughput *= 1.0 / p; + } + } + bounces++; } - return final_color * mix(ground_color, sky_color, (normalize(r.direction).y + 1.0) * 0.5); + return final_color; } void main() { @@ -310,7 +321,7 @@ void main() { vec2 uv = vec2(float(x) / (width - 1), 1.0 - (float(y) / (height - 1))); - uint seed = (y * width + x) * 100 + sample_index; + uint seed = uint(x * uint(1973) + y * uint(9277) + sample_index * uint(26699)) | uint(1); vec2 rand_disk = disk_vec(seed); vec2 jittered_uvs = uv + vec2(rand_disk.x / (width - 1), rand_disk.y / (height - 1)); vec2 lens_disk = rand_disk * cam.lens_radius; @@ -321,7 +332,7 @@ void main() { vec4 out_color = ray_color(r, seed); - float spp_scale = (1.0 / samples_per_pixel); + float spp_scale = (1.0 / sample_index); vec4 acc_color = imageLoad(accumulation_image, ivec2(x, y)); imageStore(output_image, ivec2(x, y), vec4(mix(acc_color, out_color, spp_scale))); } diff --git a/src/main.cpp b/src/main.cpp index 0e2911c..071903a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -54,7 +54,7 @@ int main() { uint64_t frame_count = 0; auto start = std::chrono::high_resolution_clock::now(); auto end = std::chrono::high_resolution_clock::now(); - auto inputs = create_inputs(width, height, samples_per_pixel); + auto inputs = create_inputs(width, height); auto can_render = true; vkrenderer renderer { wnd, inputs }; @@ -179,10 +179,7 @@ int main() { ImGui::Text("frame per second %u\n", static_cast<uint32_t>(1000.f / delta_time)); ImGui::Text("frame time %f ms\n", delta_time); - - if (ImGui::SliderInt("samples per pixel", (int32_t*)&((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->samples_per_pixel, 1, 1000)) { - reset_accumulation = true; - } + ImGui::Text("frame count %u\n", ((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->sample_index); ImGui::SliderInt("bounces", (int32_t*)&((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->max_bounce, 2, 250); @@ -200,12 +197,13 @@ int main() { renderer.begin_frame(); if (reset_accumulation) { + ((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->sample_index = 0; renderer.reset_accumulation(); } renderer.compute(width, height); - ((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->sample_index = frame_count % 1000; + ((input_data*) renderer.compute_shader_buffer.alloc_info.pMappedData)->sample_index++; renderer.ui(); diff --git a/src/utils.cpp b/src/utils.cpp index d4419b0..6467563 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -104,10 +104,17 @@ uint32_t random_scene(sphere *world) { material3.type = MATERIAL_TYPE::METAL; world[world_sphere_index + 2] = { { 4, 1, 0 }, material3, 1.0 }; - return world_sphere_index + 2; + material material4 = {}; + material4.albedo = color{ 0.0, 0.0, 0.0 }; + material4.emissive = color{ 1.0, 1.0, 1.0 } * 100.0; + material4.fuzz = 0.0; + material4.type = MATERIAL_TYPE::EMISSIVE; + world[world_sphere_index + 3] = { { 2, 1.5, 2 }, material4, 0.5 }; + + return world_sphere_index + 3; } -struct input_data create_inputs(uint32_t width, uint32_t height, uint32_t samples_per_pixel) { +struct input_data create_inputs(uint32_t width, uint32_t height) { // Image dimensions const float aspect_ratio = (float)width / (float) height; @@ -129,7 +136,7 @@ struct input_data create_inputs(uint32_t width, uint32_t height, uint32_t sample .nodes = {}, .max_bounce = max_depth, - .samples_per_pixel = samples_per_pixel, + .width = width, .height = height, @@ -139,6 +146,8 @@ struct input_data create_inputs(uint32_t width, uint32_t height, uint32_t sample // World hittable objects uint32_t spheres_count = random_scene(inputs.spheres); + inputs.spheres_count = spheres_count; + inputs.nodes[0] = { inputs.spheres, 0, spheres_count, inputs.nodes, 1 }; return inputs; diff --git a/src/vk-api.cpp b/src/vk-api.cpp index dc30520..1d0c8a5 100644 --- a/src/vk-api.cpp +++ b/src/vk-api.cpp @@ -736,7 +736,8 @@ Swapchain vkapi::create_swapchain(VkSurfaceKHR surface, size_t min_image_count, create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; create_info.preTransform = caps.currentTransform; create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; - create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + // create_info.presentMode = VK_PRESENT_MODE_FIFO_KHR; + create_info.presentMode = VK_PRESENT_MODE_MAILBOX_KHR; create_info.clipped = VK_TRUE; create_info.oldSwapchain = old_swapchain; |
