renderer refactoring; controls for lookat

main
Clyne 5 months ago
parent fdc2d91285
commit 456b9f4cef
Signed by: clyne
GPG Key ID: 3267C8EBF3F9AFC7

@ -30,15 +30,17 @@ static int threads = 4;
static int SamplesPerPixel = 20;
static int SamplesPerPixelTmp = 20;
static float Daylight = 0.5f;
static std::unique_ptr<Renderer> renderer;
static Renderer renderer;
static std::chrono::time_point<std::chrono::high_resolution_clock> renderStart;
static std::chrono::duration<double> renderTime;
static color ray_color(const ray& r, int depth = 50);
static void initiateRender(SDL_Surface *canvas);
static void showObjectControls(int index, std::unique_ptr<Object>& o);
static void showCameraControls(SDL_Surface *canvas);
static void addRandomObject();
static void preview(SDL_Surface *canvas);
static void exportScreenshot(SDL_Surface *canvas);
int main()
{
@ -47,6 +49,8 @@ int main()
auto window = SDL_CreateWindow("raytrace", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, Width, Height, SDL_WINDOW_RESIZABLE);
auto canvas = SDL_CreateRGBSurfaceWithFormat(0, Width, Height, 32, SDL_PIXELFORMAT_RGBA8888);
auto painter = SDL_CreateRenderer(window, -1, SDL_RENDERER_PRESENTVSYNC /*| SDL_RENDERER_ACCELERATED*/);
auto tex = SDL_CreateTextureFromSurface(painter, canvas);
bool run = true;
ImGui::CreateContext();
ImGui_ImplSDL2_InitForSDLRenderer(window, painter);
@ -57,17 +61,12 @@ int main()
for (auto i : std::views::iota(0, 10))
addRandomObject();
std::cout << "Spawning threads..." << std::endl;
initiateRender(canvas);
auto tex = SDL_CreateTextureFromSurface(painter, canvas);
std::cout << "Entering render..." << std::endl;
bool run = true;
for (SDL_Event event; run;) {
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if (event.type == SDL_QUIT) {
renderer->stop();
renderer.stop();
run = false;
}
}
@ -80,51 +79,36 @@ int main()
if (ImGui::SliderFloat("fov", &Camera.fieldOfView, 10, 160))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(80);
ImGui::InputInt("T", &threads);
ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("X", &Camera.camera.x(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("Y", &Camera.camera.y(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("Z", &Camera.camera.z(), 0.1, 0.05, "%.2lf"))
preview(canvas);
if (ImGui::InputInt("T", &threads))
threads = std::max(threads, 1);
showCameraControls(canvas);
if (ImGui::SliderInt("samples", &SamplesPerPixel, 1, 200)) {
SamplesPerPixelTmp = SamplesPerPixel;
}
ImGui::SliderFloat("shade", &Daylight, 0.25f, 1.f);
if (ImGui::Button("recalculate")) {
if (ImGui::Button("recalculate"))
initiateRender(canvas);
}
ImGui::SameLine();
if (ImGui::Button("export")) {
std::string filename ("screenshot_");
filename += std::to_string(int(randomN() * 1000000));
filename += ".png";
IMG_SavePNG(canvas, filename.c_str());
std::cout << "saved " << filename << std::endl;
}
if (ImGui::Button("export"))
exportScreenshot(canvas);
ImGui::SameLine();
if (ImGui::Button("exit")) {
renderer->stop();
renderer.stop();
run = false;
}
if (*renderer) {
if (renderer) {
SDL_DestroyTexture(tex);
tex = SDL_CreateTextureFromSurface(painter, canvas);
ImGui::SameLine();
if (ImGui::Button("stop")) {
renderer->stop();
}
ImGui::Text("wait... %u%%", renderer->progress());
if (ImGui::Button("stop"))
renderer.stop();
ImGui::Text("wait... %u%%", renderer.progress());
} else if (renderTime == std::chrono::duration<double>::zero()) {
SDL_DestroyTexture(tex);
tex = SDL_CreateTextureFromSurface(painter, canvas);
renderTime = std::chrono::high_resolution_clock::now() - renderStart;
SamplesPerPixel = SamplesPerPixelTmp;
} else {
@ -132,21 +116,20 @@ int main()
}
ImGui::End();
ImGui::Begin("balls", nullptr, ImGuiWindowFlags_NoResize); {
std::ranges::for_each(
std::views::zip(std::views::iota(0),
std::views::drop(world.objects, 1)),
[](auto io) { std::apply(showObjectControls, io); });
ImGui::Begin("balls", nullptr, ImGuiWindowFlags_NoResize);
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")) {
addRandomObject();
initiateRender(canvas);
}
if (ImGui::Button("del")) {
world.objects.pop_back();
initiateRender(canvas);
}
} ImGui::End();
if (ImGui::Button("add")) {
addRandomObject();
initiateRender(canvas);
}
if (ImGui::Button("del")) {
world.objects.pop_back();
initiateRender(canvas);
}
ImGui::End();
ImGui::Render();
SDL_RenderClear(painter);
@ -185,21 +168,23 @@ color ray_color(const ray& r, int depth)
void initiateRender(SDL_Surface *canvas)
{
renderStart = std::chrono::high_resolution_clock::now();
if (renderer)
renderer.stop();
renderTime = std::chrono::duration<double>::zero();
auto func = [format = canvas->format](auto x, auto y, auto pbuf) {
auto func = [format = canvas->format](auto x, auto y) {
auto col = std::ranges::fold_left(std::views::iota(0, SamplesPerPixel), color(),
[y, x](color c, int i) { return c + ray_color(Camera.getRay(x, y, true)); });
col = col / SamplesPerPixel * 255;
pbuf[y * Width + x] = SDL_MapRGBA(format, col.x(), col.y(), col.z(), 255);
return SDL_MapRGB(format, col.x(), col.y(), col.z());
};
Camera.recalculate();
threads = std::clamp(threads, 1, Renderer::MaxThreads);
renderer.reset(new Renderer(threads, func, Width, Height,
(uint32_t *)canvas->pixels));
renderer.setBuffer((uint32_t *)canvas->pixels, Width, Height);
renderStart = std::chrono::high_resolution_clock::now();
renderer.start(func, threads);
}
void showObjectControls(int index, std::unique_ptr<Object>& o)
@ -209,9 +194,9 @@ void showObjectControls(int index, std::unique_ptr<Object>& o)
ImGui::SetNextItemWidth(200);
ImGui::Combo((std::string("mat") + idx).c_str(),
reinterpret_cast<int *>(&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::SameLine(); ImGui::SetNextItemWidth(100);
ImGui::InputDouble((std::string("radius") + idx).c_str(),
&dynamic_cast<Sphere *>(o.get())->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");
@ -223,6 +208,28 @@ void showObjectControls(int index, std::unique_ptr<Object>& o)
&o->center.z(), 0.1, 0.05, "%.2lf");
}
void showCameraControls(SDL_Surface *canvas)
{
ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("X", &Camera.camera.x(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("Y", &Camera.camera.y(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("Z", &Camera.camera.z(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("I", &Camera.lookat.x(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("J", &Camera.lookat.y(), 0.1, 0.05, "%.2lf"))
preview(canvas);
ImGui::SameLine(); ImGui::SetNextItemWidth(100);
if (ImGui::InputDouble("K", &Camera.lookat.z(), 0.1, 0.05, "%.2lf"))
preview(canvas);
}
void addRandomObject()
{
const point3 pos = vec3::random() * vec3(6, 0.8, 3) - vec3(3, 0, 3.8);
@ -238,3 +245,12 @@ void preview(SDL_Surface *canvas)
initiateRender(canvas);
}
void exportScreenshot(SDL_Surface *canvas)
{
std::string filename ("screenshot_");
filename += std::to_string(int(randomN() * 1000000));
filename += ".png";
IMG_SavePNG(canvas, filename.c_str());
std::cout << "saved " << filename << std::endl;
}

@ -3,48 +3,28 @@
#include <algorithm>
#include <atomic>
#include <memory>
#include <ranges>
#include <semaphore>
#include <thread>
class Renderer
{
public:
static constexpr int MaxThreads = 64;
private:
int N;
std::counting_semaphore<MaxThreads> Workers;
std::atomic_uint processed;
unsigned total;
std::atomic_bool Stop;
std::unique_ptr<std::thread> primary;
public:
Renderer(int n, auto func, unsigned width, unsigned height, auto pbuf):
N(n), Workers(N), processed(0), total(N * 8)
{
template<typename Fn>
void start(Fn func, int tn) {
actives.store(tn);
processed.store(0);
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::single(pbuf)),
width * height / total),
[=, this](auto chunk) { return std::thread([=, this] { worker(func, chunk); }); });
if (primary.joinable())
primary.join();
primary.reset(new std::thread([=, this] {
for (auto th : threads) {
Workers.acquire();
th.detach();
}
for (int i : std::views::iota(0, N))
Workers.acquire();
Stop.store(true);
}));
primary = std::thread(&Renderer::dispatchWorkers<Fn>, this, func);
}
void setBuffer(std::uint32_t *pixelbuf, unsigned w, unsigned h) {
pixelBuffer = pixelbuf;
width = w;
height = h;
}
~Renderer() {
@ -61,20 +41,51 @@ public:
void stop() {
Stop.store(true);
if (primary->joinable())
primary->join();
if (primary.joinable())
primary.join();
}
private:
std::uint32_t *pixelBuffer = nullptr;
unsigned width = 0, height = 0, total = 0;
std::thread primary;
std::atomic_uint actives;
std::atomic_uint processed;
std::atomic_bool Stop;
template<typename Fn>
void dispatchWorkers(Fn func) {
const auto N = actives.load();
total = N * 16;
auto threads = std::views::transform(
std::views::chunk(
std::views::cartesian_product(
std::views::iota(0u, width),
std::views::iota(0u, height)),
width * height / total),
[=, this](auto chunk) { return std::thread([=, this] { worker(func, chunk); }); });
for (auto th : threads) {
while (actives.load() == 0)
std::this_thread::yield();
--actives;
th.detach();
}
while (actives.load() < N)
std::this_thread::yield();
Stop.store(true);
}
void worker(auto func, auto chunk) {
for (auto xyb : chunk) {
for (auto [x, y] : chunk) {
if (Stop.load())
break;
std::apply(func, xyb);
pixelBuffer[y * width + x] = func(x, y);
}
processed++;
Workers.release();
++processed;
++actives;
}
};

@ -8,11 +8,11 @@
struct View
{
static constexpr auto lookat = point3(0, 0, -1); // Point camera is looking at
static constexpr auto vup = vec3(0, 1, 0); // Camera-relative "up" direction
float fieldOfView = 90.f;
point3 camera {0, 0.5, 0.5};
point3 lookat {0, 0, -1}; // Point camera is looking at
float focalLength;
float viewportHeight;

Loading…
Cancel
Save