diff --git a/main.cpp b/main.cpp index bd749ab..edad534 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,6 @@ constexpr unsigned Width = 1000; constexpr double Aspect = 16.0 / 9.0; constexpr unsigned Height = Width / Aspect; -constexpr unsigned Threads = 3; #include "color.h" #include "ray.h" @@ -26,14 +25,17 @@ constexpr unsigned Threads = 3; static View Camera; static World world; +static int threads = 4; static int SamplesPerPixel = 20; static float Daylight = 0.5f; -static std::unique_ptr> renderer; +static std::unique_ptr renderer; static std::chrono::time_point renderStart; static std::chrono::duration renderTime; static color ray_color(const ray& r, int depth = 50); static void initiateRender(SDL_Surface *canvas); +static void showObjectControls(int index, Sphere& o); +static void addRandomObject(); int main() { @@ -47,10 +49,10 @@ int main() ImGui_ImplSDL2_InitForSDLRenderer(window, painter); ImGui_ImplSDLRenderer2_Init(painter); - world.add(point3( 0.00, -100.50, -1.0), 100.0, Material::Lambertian, color(0.5, 1.0, 0.5)); - world.add(point3(-0.50, 0.00, -1.2), 0.5, Material::Dielectric, color(1.0, 0.8, 0.8)); - world.add(point3( 0.50, 0.00, -1.0), 0.5, Material::Metal, color(0.5, 0.5, 0.5)); - world.add(point3(-0.05, -0.35, -0.7), 0.1, Material::Metal, color(0.8, 0.6, 0.0)); + world.add(point3(0.00, -100.50, -1.0), 100.0, + Material::Lambertian, color(0.5, 1.0, 0.5)); + for (auto i : std::views::iota(0, 10)) + addRandomObject(); std::cout << "Spawning threads..." << std::endl; initiateRender(canvas); @@ -73,11 +75,17 @@ int main() ImGui::Begin("settings", nullptr, ImGuiWindowFlags_AlwaysAutoResize); ImGui::SliderFloat("fov", &Camera.fieldOfView, 10, 160); - ImGui::SetNextItemWidth(100); ImGui::InputDouble("X", &Camera.camera.x(), 0.1, 0.05, "%.2lf"); - ImGui::SameLine(); ImGui::SetNextItemWidth(100); ImGui::InputDouble("Y", &Camera.camera.y(), 0.1, 0.05, "%.2lf"); - ImGui::SameLine(); ImGui::SetNextItemWidth(100); ImGui::InputDouble("Z", &Camera.camera.z(), 0.1, 0.05, "%.2lf"); + ImGui::SameLine(); ImGui::SetNextItemWidth(60); + ImGui::InputInt("T", &threads); + ImGui::SetNextItemWidth(100); + ImGui::InputDouble("X", &Camera.camera.x(), 0.1, 0.05, "%.2lf"); + ImGui::SameLine(); ImGui::SetNextItemWidth(100); + ImGui::InputDouble("Y", &Camera.camera.y(), 0.1, 0.05, "%.2lf"); + ImGui::SameLine(); ImGui::SetNextItemWidth(100); + ImGui::InputDouble("Z", &Camera.camera.z(), 0.1, 0.05, "%.2lf"); ImGui::SliderInt("samples", &SamplesPerPixel, 1, 200); ImGui::SliderFloat("shade", &Daylight, 0.f, 1.f); + if (ImGui::Button("recalculate")) { initiateRender(canvas); } @@ -94,6 +102,7 @@ int main() renderer->stop(); run = false; } + if (*renderer) { SDL_DestroyTexture(tex); tex = SDL_CreateTextureFromSurface(painter, canvas); @@ -114,35 +123,13 @@ int main() ImGui::End(); ImGui::Begin("balls", nullptr, ImGuiWindowFlags_NoResize); { - char radius[] = "radius 0"; - char mat[] = "mat 0"; - char xpos[] = "x 0"; - char ypos[] = "y 0"; - char zpos[] = "z 0"; - - for (auto& o : std::views::drop(world.objects, 1)) { - ImGui::SetNextItemWidth(200); - ImGui::Combo(mat, reinterpret_cast(&o.M), - "Lambertian\0Metal\0Dielectric\0"); - ImGui::SameLine(); ImGui::SetNextItemWidth(100); - ImGui::InputDouble(radius, &o.radius, 0.1, 0.05, "%.2lf"); - ImGui::SetNextItemWidth(100); - ImGui::InputDouble(xpos, &o.center.x(), 0.05, 0.05, "%.2lf"); - ImGui::SameLine(); ImGui::SetNextItemWidth(100); - ImGui::InputDouble(ypos, &o.center.y(), 0.1, 0.05, "%.2lf"); - ImGui::SameLine(); ImGui::SetNextItemWidth(100); - ImGui::InputDouble(zpos, &o.center.z(), 0.1, 0.05, "%.2lf"); - - radius[7]++; - mat[4]++; - xpos[2]++; - ypos[2]++; - zpos[2]++; - } + std::ranges::for_each( + std::views::zip(std::views::iota(0), + std::views::drop(world.objects, 1)), + [](auto io) { std::apply(showObjectControls, io); }); + if (ImGui::Button("add")) { - const point3 pos = vec3::random() * vec3(4, 1.5, 4) - vec3(2, 0, 4); - const color col = vec3::random(); - world.add(pos, randomN() * 0.5 + 0.1, Material::Lambertian, col); + addRandomObject(); initiateRender(canvas); } if (ImGui::Button("del")) { @@ -200,5 +187,37 @@ void initiateRender(SDL_Surface *canvas) }; Camera.recalculate(); - renderer.reset(new Renderer(func, Width, Height, (uint32_t *)canvas->pixels)); + threads = std::clamp(threads, 1, Renderer::MaxThreads); + renderer.reset(new Renderer(threads, func, Width, Height, + (uint32_t *)canvas->pixels)); } + +void showObjectControls(int index, Sphere& o) +{ + const auto idx = std::to_string(index); + + ImGui::SetNextItemWidth(200); + ImGui::Combo((std::string("mat") + idx).c_str(), reinterpret_cast(&o.M), + "Lambertian\0Metal\0Dielectric\0"); + ImGui::SameLine(); ImGui::SetNextItemWidth(100); + ImGui::InputDouble((std::string("radius") + idx).c_str(), + &o.radius, 0.1, 0.05, "%.2lf"); + ImGui::SetNextItemWidth(100); + ImGui::InputDouble((std::string("x") + idx).c_str(), + &o.center.x(), 0.05, 0.05, "%.2lf"); + ImGui::SameLine(); ImGui::SetNextItemWidth(100); + ImGui::InputDouble((std::string("y") + idx).c_str(), + &o.center.y(), 0.1, 0.05, "%.2lf"); + ImGui::SameLine(); ImGui::SetNextItemWidth(100); + ImGui::InputDouble((std::string("z") + idx).c_str(), + &o.center.z(), 0.1, 0.05, "%.2lf"); +} + +void addRandomObject() +{ + const point3 pos = vec3::random() * vec3(6, 0.8, 3) - vec3(3, 0, 3.8); + const color col = vec3::random(); + const auto mat = (int)(randomN() * ((int)Material::Undefined - 1)); + world.add(pos, randomN() * 0.3 + 0.05, (Material)mat, col); +} + diff --git a/random.h b/random.h index fb0c7b3..ec60209 100644 --- a/random.h +++ b/random.h @@ -6,7 +6,7 @@ inline double randomN() { static std::uniform_real_distribution distribution (0.0, 1.0); - static std::mt19937 generator; + static std::mt19937 generator (std::random_device{}()); return distribution(generator); } diff --git a/renderer.h b/renderer.h index ec7b170..864c0b8 100644 --- a/renderer.h +++ b/renderer.h @@ -1,3 +1,6 @@ +#ifndef RENDERER_H +#define RENDERER_H + #include #include #include @@ -5,28 +8,32 @@ #include #include -template class Renderer { - std::counting_semaphore Workers; +public: + static constexpr int MaxThreads = 64; + +private: + int N; + std::counting_semaphore Workers; std::atomic_uint processed; unsigned total; std::atomic_bool Stop; std::unique_ptr primary; public: - Renderer(auto func, unsigned Width, unsigned Height, auto pbuf): - Workers(Threads), processed(0), total(Threads * 8) + Renderer(int n, auto func, unsigned width, unsigned height, auto pbuf): + N(n), Workers(N), processed(0), total(N * 8) { Stop.store(false); auto threads = std::views::transform( std::views::chunk( std::views::cartesian_product( - std::views::iota(0u, Width), - std::views::iota(0u, Height), + std::views::iota(0u, width), + std::views::iota(0u, height), std::views::single(pbuf)), - Width * Height / total), + width * height / total), [=, this](auto chunk) { return std::thread([=, this] { worker(func, chunk); }); }); primary.reset(new std::thread([=, this] { @@ -34,10 +41,8 @@ public: Workers.acquire(); th.detach(); } - for (auto i : std::views::iota(0, Threads)) + for (int i : std::views::iota(0, N)) Workers.acquire(); - for (auto i : std::views::iota(0, Threads)) - Workers.release(); Stop.store(true); })); } @@ -73,4 +78,5 @@ private: } }; +#endif // RENDERER_H diff --git a/sphere.h b/sphere.h index c27f29b..3cb2c8c 100644 --- a/sphere.h +++ b/sphere.h @@ -10,9 +10,10 @@ #include enum class Material : int { - Lambertian, + Lambertian = 0, Metal, - Dielectric + Dielectric, + Undefined }; struct Sphere