pathtracing

This commit is contained in:
Ben
2019-08-29 22:32:43 +01:00
parent d0db6b5794
commit c98371068c
13 changed files with 301 additions and 104 deletions

View File

@@ -14,24 +14,24 @@
#include <string>
enum OperationMode {
MODE_OPERATION_DEFAULT,
MODE_OPERATION_PROGRESSIVE_GUI,
MODE_OPERATION_PROGRESSIVE_IMG,
MODE_OPERATION_SAMPLES_IMG
MODE_OPERATION_SAMPLES_IMG,
MODE_OPERATION_DEFAULT
};
enum RenderMode {
MODE_RENDER_DEFAULT,
MODE_RENDER_PATHTRACE,
MODE_RENDER_NORMALS
MODE_RENDER_NORMALS,
MODE_RENDER_DEFAULT
};
enum AccelerationMode {
MODE_ACCELERATION_DEFAULT,
MODE_ACCELERATION_NONE,
MODE_ACCELERATION_KD,
MODE_ACCELERATION_KD_SLOW,
MODE_ACCELERATION_BVH
MODE_ACCELERATION_BVH,
MODE_ACCELERATION_DEFAULT
};
#endif

View File

@@ -8,5 +8,5 @@ Scene::Scene(int width, int height) {
}
glm::vec3 Scene::SampleSky(Ray ray) {
return { 0.617f, 0.980f, 1.000f };
return { 10.0f, 10.0f, 10.0f };
}

View File

@@ -1,45 +0,0 @@
#include "tonemap.hpp"
#include "framebuffer.hpp"
#include "../pixel.hpp"
MapBuffer::MapBuffer(int xres, int yres) {
XRes = xres; YRes = yres;
Data = (glm::vec3*)malloc((xres * yres) * sizeof(glm::vec3));
memset((void*)Data, 0, (xres * yres) * sizeof(glm::vec3));
}
void MapBuffer::SetPixel(int x, int y, glm::vec3 p) {
Data[y * this->XRes + x] = p;
}
void MapBuffer::SetPixelSafe(int x, int y, glm::vec3 p) {
if (x >= 0 && x < this->XRes && y >= 0 && this->YRes) {
Data[y * this->XRes + x] = p;
}
}
void MapBuffer::SetFramebuffer(glm::vec3* fb) {
free(Data);
Data = fb;
}
void MapBuffer::ClearFramebuffer() {
memset((void*)Data, 0, (XRes * YRes) * sizeof(glm::vec3));
}
void MapBuffer::ClampBasic(FrameBuffer* buffer) {
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
buffer->SetPixelSafe(x, y, Clamp(Data[y * this->XRes + x], 1.0f, 0.0f));
}
}
void MapBuffer::MapBasic(FrameBuffer* buffer) {
}
MapBuffer::~MapBuffer() {
free(Data);
}

72
src/display/tonemapfb.cpp Normal file
View File

@@ -0,0 +1,72 @@
#include "tonemapfb.hpp"
#include "framebuffer.hpp"
#include "../pixel.hpp"
ToneMapFrameBuffer::ToneMapFrameBuffer(int xres, int yres) {
XRes = xres; YRes = yres;
RenderTo = (glm::vec3*)malloc((xres * yres) * sizeof(glm::vec3));
memset((void*)RenderTo, 0, (xres * yres) * sizeof(glm::vec3));
ProcData = (glm::vec3*)malloc((xres * yres) * sizeof(glm::vec3));
memset((void*)ProcData, 0, (xres * yres) * sizeof(glm::vec3));
}
void ToneMapFrameBuffer::SetPixel(int x, int y, glm::vec3 p) {
if (LastOp != OPERATION_SET) { ClearFramebuffer(); }
LastOp = OPERATION_SET;
RenderTo[y * this->XRes + x] = p;
}
void ToneMapFrameBuffer::SetPixelSafe(int x, int y, glm::vec3 p) {
if (x >= 0 && x < this->XRes && y >= 0 && this->YRes) {
if (LastOp != OPERATION_SET) { ClearFramebuffer(); }
LastOp = OPERATION_SET;
RenderTo[y * this->XRes + x] = p;
}
}
void ToneMapFrameBuffer::AddPixelSafe(int x, int y, glm::vec3 p) {
if (x >= 0 && x < this->XRes && y >= 0 && this->YRes) {
if (LastOp != OPERATION_ADD) { ClearFramebuffer(); }
LastOp = OPERATION_ADD;
RenderTo[y * this->XRes + x] += p;
}
}
void ToneMapFrameBuffer::SetFramebuffer(glm::vec3* fb) {
free(ProcData);
RenderTo = fb;
}
void ToneMapFrameBuffer::ClearFramebuffer() {
memset((void*)RenderTo, 0, (XRes * YRes) * sizeof(glm::vec3));
}
void ToneMapFrameBuffer::ClampBasic(FrameBuffer* buffer) {
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
buffer->SetPixelSafe(x, y, Clamp(ProcData[y * this->XRes + x], 1.0f, 0.0f));
}
}
void ToneMapFrameBuffer::MapBasic(FrameBuffer* buffer) {
float max = 0.0f;
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
if (ProcData[y * this->XRes + x].r > max) max = ProcData[y * this->XRes + x].r;
if (ProcData[y * this->XRes + x].g > max) max = ProcData[y * this->XRes + x].g;
if (ProcData[y * this->XRes + x].b > max) max = ProcData[y * this->XRes + x].b;
}
for (int x = 0; x < XRes; x++)
for (int y = 0; y < YRes; y++) {
buffer->SetPixelSafe(x, y, ProcData[y * this->XRes + x] / max);
}
}
ToneMapFrameBuffer::~ToneMapFrameBuffer() {
free(RenderTo);
free(ProcData);
}

View File

@@ -9,12 +9,19 @@
class FrameBuffer;
class Pixel;
class MapBuffer {
enum LastOperation {
OPERATION_NONE,
OPERATION_SET,
OPERATION_ADD
};
class ToneMapFrameBuffer {
public:
MapBuffer(int xres, int yres);
ToneMapFrameBuffer(int xres, int yres);
void SetPixel(int x, int y, glm::vec3 p);
void SetPixelSafe(int x, int y, glm::vec3);
void AddPixelSafe(int x, int y, glm::vec3);
void SetFramebuffer(glm::vec3* fb);
void ClearFramebuffer();
@@ -24,10 +31,12 @@ public:
void MapBasic(FrameBuffer* buffer);
glm::vec3* Data;
LastOperation LastOp = OPERATION_NONE;
glm::vec3* RenderTo;
glm::vec3* ProcData;
int XRes, YRes;
~MapBuffer();
~ToneMapFrameBuffer();
};
#endif

View File

@@ -1,5 +1,9 @@
#include "progressiverenderer.hpp"
#include <numeric>
#include <sstream>
#include <chrono>
#include "renderengine.hpp"
#include "../common.hpp"
@@ -7,7 +11,7 @@
#include "../display/displayinterface.hpp"
#include "../display/framebuffer.hpp"
#include "../display/tonemap.hpp"
#include "../display/tonemapfb.hpp"
#include "../util/assetloader.hpp"
#include "../util/threadpool.hpp"
@@ -35,30 +39,75 @@ void ProgressiveRenderer::Input() {
while (SDL_PollEvent(&e))
if (e.type == SDL_QUIT) m_interface->Close();
//const Uint8* state = SDL_GetKeyboardState(NULL);
//if (state[SDL_SCANCODE_W]) m_scene->objects[0]->center.y += 0.01f;
//if (state[SDL_SCANCODE_S]) m_scene->objects[0]->center.y -= 0.01f;
//if (state[SDL_SCANCODE_D]) m_scene->objects[0]->center.x += 0.01f;
//if (state[SDL_SCANCODE_A]) m_scene->objects[0]->center.x -= 0.01f;
if (!m_interface->ImGui) return;
ImGui::NewFrame();
ImGui::Begin("Debug");
ImGui::Checkbox("Render Normals", &m_normals);
if (m_normals) {
m_engine->Mode = MODE_RENDER_NORMALS;
} else {
m_engine->Mode = MODE_RENDER_PATHTRACE;
if (m_engine->Mode != MODE_RENDER_NORMALS) {
std::stringstream str; str << "SPP: " << m_engine->SPP;
ImGui::Text(str.str().c_str());
}
std::stringstream str0; str0 << "FPS: " << 1.0f / AverageFrameTime;
ImGui::Text(str0.str().c_str());
std::stringstream str1; str1 << "MS Per Frame: " << AverageFrameTime * 1000.0f;
ImGui::Text(str1.str().c_str());
std::stringstream str2; str2 << "S Per Frame: " << AverageFrameTime;
ImGui::Text(str2.str().c_str());
float upper = 0.0f; float lower = 0.0f;
for (int i = 0; i < AllFrameTimes.size(); i++) {
if (AllFrameTimes[i] > upper) upper = AllFrameTimes[i];
if (AllFrameTimes[i] < lower) lower = AllFrameTimes[i];
}
ImGui::PlotLines("FrameTimes", FrameTimes.data(), FrameTimes.size(), 0, NULL, lower, upper, ImVec2(0, 40));
ImGui::BeginChild("Render Settings");
const char* renderItems[] = { "PathTrace", "Normals" };
ImGui::Combo("Render Mode", &m_renderModeSelected, renderItems, IM_ARRAYSIZE(renderItems));
m_engine->Mode = (RenderMode)m_renderModeSelected;
const char* toneMapItems[] = { "Clamp", "Basic Tonemap" };
ImGui::Combo("ToneMap Mode", &m_toneMapModeSelected, toneMapItems, IM_ARRAYSIZE(toneMapItems));
ImGui::EndChild();
ImGui::End();
}
void ProgressiveRenderer::Render() {
m_threadPool->SetJobs(this, m_scene->w, m_scene->h);
// Starts render loop
std::chrono::high_resolution_clock::time_point frameStartTime = std::chrono::high_resolution_clock::now();
std::chrono::high_resolution_clock::time_point frameEndTime;
Ready = true;
m_threadPool->Ready = true;
while (m_interface->Active) {
if (m_threadPool->CheckAllJobs()) {
m_threadPool->MappedThreadFrameBuffer->ClampBasic(m_threadPool->ThreadFrameBuffer);
m_engine->PostProcess(m_threadPool->MappedThreadFrameBuffer->RenderTo, m_threadPool->MappedThreadFrameBuffer->ProcData, m_scene->w, m_scene->h);
if (m_engine->Mode != MODE_RENDER_NORMALS) {
if (m_toneMapModeSelected == 0) m_threadPool->MappedThreadFrameBuffer->ClampBasic(m_threadPool->ThreadFrameBuffer);
if (m_toneMapModeSelected == 1) m_threadPool->MappedThreadFrameBuffer->MapBasic(m_threadPool->ThreadFrameBuffer);
} else {
m_threadPool->MappedThreadFrameBuffer->ClampBasic(m_threadPool->ThreadFrameBuffer);
}
m_threadPool->MergeBuffers(m_interface->Framebuffer->Data, m_scene->w, m_scene->h);
m_threadPool->RunJobsAgain();
frameEndTime = std::chrono::high_resolution_clock::now();
m_calculateTimes(frameStartTime, frameEndTime);
frameStartTime = std::chrono::high_resolution_clock::now();
}
Input();
@@ -69,6 +118,17 @@ void ProgressiveRenderer::Render() {
m_threadPool->Destroy();
}
void ProgressiveRenderer::RenderProgressive() {
void ProgressiveRenderer::m_calculateTimes(std::chrono::high_resolution_clock::time_point frameStartTime,
std::chrono::high_resolution_clock::time_point frameEndTime) {
m_framesRendererd++;
float frameTime = std::chrono::duration_cast<std::chrono::milliseconds>(frameEndTime - frameStartTime).count();
frameTime /= 1000;
FrameTimes.push_back(frameTime);
AllFrameTimes.push_back(frameTime);
if (FrameTimes.size() > 11) FrameTimes.erase(FrameTimes.begin());
AverageFrameTime = std::accumulate(AllFrameTimes.begin(), AllFrameTimes.end(), 0.0) / AllFrameTimes.size();
}

View File

@@ -4,9 +4,7 @@
#include "../common.hpp"
#include "../maths.hpp"
#include <sstream>
#include <vector>
#include <chrono>
#include <thread>
#include <mutex>
@@ -24,9 +22,11 @@ public:
void Input();
void Render();
void RenderProgressive();
bool Ready = false;
std::vector<float> FrameTimes = { };
std::vector<float> AllFrameTimes = { };
float AverageFrameTime = 0.0f;
public:
RenderThreadPool* m_threadPool = nullptr;
@@ -37,7 +37,13 @@ public:
private:
std::mutex m_mutex;
bool m_normals = true;
void m_calculateTimes(std::chrono::high_resolution_clock::time_point frameStartTime,
std::chrono::high_resolution_clock::time_point frameEndTime);
int m_renderModeSelected = 0;
int m_toneMapModeSelected = 1;
int m_framesRendererd = 0;
};
#endif

View File

@@ -1,5 +1,7 @@
#include "renderengine.hpp"
#include <random>
#include "../pixel.hpp"
#include "../util/threadpool.hpp"
@@ -10,11 +12,17 @@
#include "../display/displayinterface.hpp"
#include "../display/framebuffer.hpp"
#include "../display/tonemap.hpp"
#include "../display/tonemapfb.hpp"
#include "../engine/renderengine.hpp"
#include "../engine/progressiverenderer.hpp"
static glm::vec3 Black{ 0.0f, 0.0f, 0.0f };
static glm::vec3 White{ 1.0f, 1.0f, 1.0f };
static glm::vec3 Red { 1.0f, 0.2f, 0.2f };
static glm::vec3 Green{ 0.2f, 1.0f, 0.2f };
static glm::vec3 Blue { 0.2f, 0.2f, 1.0f };
void workerThread(RenderThreadPool* threadpool, ProgressiveRenderer* renderer, int idd, int yStart, int yRange) {
while (!renderer->Ready && !threadpool->Ready) {
std::chrono::milliseconds dura(10);
@@ -27,12 +35,21 @@ void workerThread(RenderThreadPool* threadpool, ProgressiveRenderer* renderer, i
for (int x = 0; x < renderer->m_scene->w; x++) {
Ray ray = renderer->m_scene->camera->CastRay(x, y);
glm::vec3 col = renderer->m_engine->GetColour(ray, 0);
threadpool->MappedThreadFrameBuffer->SetPixelSafe(x, y, col);
if (renderer->m_engine->Mode == MODE_RENDER_NORMALS) {
threadpool->MappedThreadFrameBuffer->SetPixelSafe(x, y, col);
} else {
// threadpool->MappedThreadFrameBuffer->SetPixelSafe(x, y, col);
threadpool->MappedThreadFrameBuffer->AddPixelSafe(x, y, col);
}
}
threadpool->ThreadStatus[idd] = true;
while (threadpool->ThreadStatus[idd]) { }
while (threadpool->ThreadStatus[idd]) {
std::chrono::nanoseconds dura(1);
std::this_thread::sleep_for(dura);
}
}
}
@@ -44,22 +61,91 @@ void RenderEngine::SetScene(Scene* scene) {
m_scene = scene;
}
std::default_random_engine generator;
// std::uniform_real_distribution<float> distribution(-1,1);
// (float)rand() / float(RAND_MAX)*2.0f - 1.0f;
float rand01() {
std::uniform_real_distribution<float> distribution(0, 1);
return distribution(generator);
}
glm::vec3 CosineBRDF(glm::vec3 normal) {
const float TWO_PI = 2.0f * PI;
float r0 = rand01();
float r1 = rand01();
glm::vec3 uu = glm::normalize(glm::cross(normal, { 0.0f, 1.0f, 1.0f }));
glm::vec3 vv = glm::cross(uu, normal);
float ra = sqrtf(r1);
float rx = ra * cosf(TWO_PI * r0);
float ry = ra * sinf(TWO_PI * r0);
float rz = sqrtf(1.0f - r1);
return glm::normalize(rx * uu + ry * vv + rz * normal);
}
glm::vec3 RenderEngine::GetColour(Ray ray, int depth) {
if (depth > 6) return { 0.0f, 0.0f, 0.0f };
float t; Primative* hit = nullptr;
bool didhit = TraceRayScene(ray, m_scene, t, hit);
if (!didhit) {
return m_scene->SampleSky(ray);
}
if (!didhit) return m_scene->SampleSky(ray);
glm::vec3 hitPoint = ray.origin + ray.direction * t;
if (Mode == MODE_RENDER_NORMALS) return GetNormalColour(hit, hitPoint);
return { 1.0f, 1.0f, 1.0f };
}
glm::vec3 RenderEngine::GetNormalColour(Primative* hit, glm::vec3 hitPoint) {
glm::vec3 normal = hit->SurfaceNormal(hitPoint);
return { ((normal.x + 1.0f) * 127.5f) / 255.0f, ((normal.y + 1.0f) * 127.5f) / 255.0f, ((normal.z + 1.0f) * 127.5f) / 255.0f };
if (Mode == MODE_RENDER_NORMALS) { return GetNormalColour(normal); }
std::uniform_real_distribution<float> distribution(0, 2);
int col = round(distribution(generator));
glm::vec3 colour = { 1.0f, 1.0f, 1.0f };
if (hit->type == TYPE_PLANE) {
glm::vec2 uv = hit->TexCoords(hitPoint);
float angle = fastDegreetoRadian(.0f);
float s = uv.x * cos(angle) - uv.y * sin(angle);
float t = uv.y * cos(angle) + uv.x * sin(angle);
float S = 0.05f; float T = 0.05f;
float pattern = (modulo(s * S) < 0.5f) ^ (modulo(t * T) < 0.5f);
colour.r = pattern; colour.g = pattern; colour.b = pattern;
}
glm::vec3 direction = CosineBRDF(normal);
Ray newRay{ hitPoint, direction };
// Prevent acne
if (glm::dot(newRay.direction, normal) < 0.0f) {
newRay.origin = ray.origin + ray.direction * t - normal * EPSILON;
}
else {
newRay.origin = ray.origin + ray.direction * t + normal * EPSILON;
}
return GetColour(newRay, depth + 1) * colour;
}
void RenderEngine::PostProcess(glm::vec3* src, glm::vec3* dst, int w, int h) {
if (Mode == MODE_RENDER_NORMALS) {
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
dst[y * w + x] = src[y * w + x];
}
SPP = 0;
return;
}
SPP++;
for (int y = 0; y < h; y++)
for (int x = 0; x < w; x++) {
dst[y * w + x] = src[y * w + x] / (float)SPP;
}
}
glm::vec3 RenderEngine::GetNormalColour(glm::vec3 normal) {
return ((normal + 1.0f) * 127.5f) / 255.0f;
}

View File

@@ -15,10 +15,14 @@ public:
void SetScene(Scene* scene);
glm::vec3 GetColour(Ray ray, int depth);
RenderMode Mode = MODE_RENDER_NORMALS;
void PostProcess(glm::vec3* src, glm::vec3* dst, int w, int h);
RenderMode Mode = MODE_RENDER_PATHTRACE;
int SPP = 0;
private:
glm::vec3 GetNormalColour(Primative* hit, glm::vec3 hitPoint);
glm::vec3 GetNormalColour(glm::vec3 normal);
Scene* m_scene = nullptr;
};

View File

@@ -6,7 +6,7 @@
#include "../display/displayinterface.hpp"
#include "../display/framebuffer.hpp"
#include "../display/tonemap.hpp"
#include "../display/tonemapfb.hpp"
#include "../engine/renderengine.hpp"
#include "../engine/progressiverenderer.hpp"
@@ -18,7 +18,7 @@ RenderThreadPool::RenderThreadPool() {
};
void RenderThreadPool::SetJobs(ProgressiveRenderer* renderer, int w, int h) {
MappedThreadFrameBuffer = new MapBuffer(w, h);
MappedThreadFrameBuffer = new ToneMapFrameBuffer(w, h);
ThreadFrameBuffer = new FrameBuffer(w, h);
for (int i = 0; i < ThreadCount; i++) {
if (i == ThreadCount - 1) {
@@ -26,15 +26,15 @@ void RenderThreadPool::SetJobs(ProgressiveRenderer* renderer, int w, int h) {
(w / ThreadCount) * i,
-((w / ThreadCount) * i - w)
));
RenderRegions.push_back({ ((w / ThreadCount) * i) * w,
(-((w / ThreadCount) * i - w)) * w });
//RenderRegions.push_back({ ((w / ThreadCount) * i) * w,
// (-((w / ThreadCount) * i - w)) * w });
} else {
Pool.push_back(new std::thread(workerThread, this, renderer, i,
(h / ThreadCount) * i,
(h / ThreadCount) * (i + 1) - (h / ThreadCount) * i
));
RenderRegions.push_back({ ((h / ThreadCount) * i) * w,
((h / ThreadCount) * (i + 1) - (h / ThreadCount) * i) * w });
//RenderRegions.push_back({ ((h / ThreadCount) * i) * w,
// ((h / ThreadCount) * (i + 1) - (h / ThreadCount) * i) * w });
}
}
}

View File

@@ -7,8 +7,8 @@
#include <tuple>
class ProgressiveRenderer;
class ToneMapFrameBuffer;
class FrameBuffer;
class MapBuffer;
class ThreadPool {
public:
@@ -31,8 +31,8 @@ public:
void MergeBuffers(uint32_t* framebuffer, int w, int h);
std::vector<std::tuple<int, int>> RenderRegions; // offest, size
MapBuffer* MappedThreadFrameBuffer;
// std::vector<std::tuple<int, int>> RenderRegions; // offest, size
ToneMapFrameBuffer* MappedThreadFrameBuffer;
FrameBuffer* ThreadFrameBuffer;
};

View File

@@ -1,4 +1,5 @@
#include <iostream>
#include "../src/inferno.hpp"
static const int width = 600;
@@ -17,21 +18,25 @@ int main(int argc, char** argv) {
Scene* scene = new Scene(width, height);
scene->camera = new Camera(width, height);
// scene->objects.push_back(new Sphere({0.0f, 0.0f, -4.0f}, 1.0f));
//scene->objects.push_back(new Sphere({ 0.0f, 0.0f, -8.0f }, 1.0f));
//scene->objects.push_back(new Sphere({ 2.0f, 0.0f, -8.0f }, 1.0f));
//scene->objects.push_back(new Sphere({ -2.0f, 0.0f, -8.0f }, 1.0f));
//scene->objects.push_back(new Plane( { 0.0f, -25.0f, 0.0f }, { 0.0f, -1.0f, 1.0f }));
// std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/dragon-normals.obj");
// for (const auto& object : tris)
// object->Translate({ 0.0f, -5.0f, -20.0f });
// std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/dragon-normals.obj");
//std::vector<Triangle*> tris = LoadTrianglesBasic("E:/Projects/Inferno/resources/dragon-normals.obj");
//for (const auto& object : tris)
// object->Translate({ 0.0f, -5.0f, -20.0f });
// std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/lucy-normals.obj");
// std::vector<Triangle*> tris = LoadTrianglesBasic("E:/Projects/Inferno/resources/lucy-normals.obj");
// for (const auto& object : tris)
// object->Translate({ 0.0f, -3.9f, -10.6f });
std::vector<Triangle*> tris = LoadTrianglesBasic("E:/Projects/Inferno/resources/lucy-normals.obj");
for (const auto& object : tris)
object->Translate({ 0.0f, -3.9f, -10.6f });
//std::vector<Triangle*> tris = LoadTrianglesBasic("/home/ben/programming/inferno/resources/cornell.obj");
std::vector<Triangle*> tris = LoadTrianglesBasic("E:/Projects/Inferno/resources/cornell.obj");
for (const auto& object : tris)
object->Translate({ 0.0f, -0.9f, -3.0f });
//std::vector<Triangle*> tris = LoadTrianglesBasic("E:/Projects/Inferno/resources/cornell.obj");
//for (const auto& object : tris)
// object->Translate({ 0.0f, -0.9f, -3.0f });
Mesh* mesh = new Mesh(tris);
mesh->Optimise();