diff --git a/src/gpu/CMakeLists.txt b/src/gpu/CMakeLists.txt index d65cbf6..c098f34 100644 --- a/src/gpu/CMakeLists.txt +++ b/src/gpu/CMakeLists.txt @@ -1,4 +1,4 @@ target_sources(RayTracer PRIVATE - Renderer.h - Renderer.cpp) \ No newline at end of file + GPURenderer.h + GPURenderer.cpp) \ No newline at end of file diff --git a/src/gpu/Renderer.cpp b/src/gpu/GPURenderer.cpp similarity index 93% rename from src/gpu/Renderer.cpp rename to src/gpu/GPURenderer.cpp index 9a31f67..22c9566 100644 --- a/src/gpu/Renderer.cpp +++ b/src/gpu/GPURenderer.cpp @@ -1,8 +1,8 @@ -#include "Renderer.h" +#include "GPURenderer.h" #include #include -Renderer::Renderer() +GPURenderer::GPURenderer() : instance(nullptr), physicalDevice(nullptr), device(nullptr), queue(nullptr), cmdPool(nullptr), cmdBuffers(nullptr), descriptorLayout(nullptr), descriptorSet(nullptr), descriptorPool(nullptr), pipelineLayout(nullptr), rayGen(nullptr), closestHit(nullptr), miss(nullptr), pipeline(nullptr) @@ -10,9 +10,9 @@ Renderer::Renderer() { } -Renderer::~Renderer() {} +GPURenderer::~GPURenderer() {} -void Renderer::createDevice() +void GPURenderer::createDevice() { vk::ApplicationInfo appInfo("RayTracer", 1, "RayTracer", 1, VK_API_VERSION_1_3); vk::InstanceCreateInfo instanceCreateInfo({}, &appInfo); @@ -45,7 +45,7 @@ void Renderer::createDevice() device = Device(physicalDevice, deviceCreateInfo); } -void Renderer::createCommands() +void GPURenderer::createCommands() { vk::CommandPoolCreateInfo commandPoolCreateInfo({}, computeQueueFamily); cmdPool = CommandPool(device, commandPoolCreateInfo); @@ -55,7 +55,7 @@ void Renderer::createCommands() cmdBuffers = vk::raii::CommandBuffers(device, commandBufferAllocateInfo); } -void Renderer::createDescriptors() +void GPURenderer::createDescriptors() { vk::DescriptorSetLayoutBinding descriptorSetLayoutBinding(0, vk::DescriptorType::eUniformBuffer, 1, vk::ShaderStageFlagBits::eVertex); vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo({}, descriptorSetLayoutBinding); @@ -68,7 +68,7 @@ void Renderer::createDescriptors() using namespace slang; -void Renderer::createShaders() +void GPURenderer::createShaders() { /* Slang::ComPtr globalSession; @@ -108,4 +108,4 @@ void Renderer::createShaders() */ } -void Renderer::render(Camera cam, RenderParameter param) {} \ No newline at end of file +void GPURenderer::render(Camera cam, RenderParameter param) {} \ No newline at end of file diff --git a/src/gpu/Renderer.h b/src/gpu/GPURenderer.h similarity index 86% rename from src/gpu/Renderer.h rename to src/gpu/GPURenderer.h index 290696b..461b142 100644 --- a/src/gpu/Renderer.h +++ b/src/gpu/GPURenderer.h @@ -1,15 +1,15 @@ #pragma once -#include "scene/Scene.h" +#include "scene/Renderer.h" #include #include using namespace vk::raii; -struct Renderer : public Scene +struct GPURenderer : public Renderer { public: - Renderer(); - virtual ~Renderer(); + GPURenderer(); + virtual ~GPURenderer(); private: void createDevice(); diff --git a/src/main.cpp b/src/main.cpp index 25f1ea9..6fee2ff 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,5 +1,4 @@ -#include "scene/BVH.h" -#include "scene/Scene.h" +#include "scene/Renderer.h" #include "util/ModelLoader.h" #include "window/Window.h" #include @@ -7,7 +6,7 @@ int main() { - Scene scene; + Renderer scene; Window window(1920, 1080); scene.startRender( Camera{ diff --git a/src/scene/BVH.cpp b/src/scene/BVH.cpp deleted file mode 100644 index 863e89a..0000000 --- a/src/scene/BVH.cpp +++ /dev/null @@ -1,170 +0,0 @@ -#include "BVH.h" -#include -#include - -void BVH::addModel(PModel model, glm::mat4 transform) -{ - model->transform(transform); - models.push_back(std::move(model)); -} - -void BVH::addModels(std::vector _models, glm::mat4 transform) -{ - for (auto& _model : _models) - { - _model->transform(transform); - models.push_back(std::move(_model)); - } -} - -void BVH::generate() -{ - std::vector pendingNodes; - while (!models.empty()) - { - auto& model = models.back(); - ModelReference ref = { - .positionOffset = (uint32_t)positionPool.size(), - .indicesOffset = (uint32_t)indicesPool.size(), - .numIndices = (uint32_t)model->indices.size(), - }; - for (uint32_t i = 0; i < model->positions.size(); ++i) - { - positionPool.push_back(model->positions[i]); - texCoordsPool.push_back(model->texCoords[i]); - } - for (uint32_t i = 0; i < model->indices.size(); ++i) - { - indicesPool.push_back(model->indices[i]); - edgesPool.push_back(model->edges[i]); - faceNormalsPool.push_back(model->faceNormals[i]); - } - pendingNodes.push_back(std::make_unique(model->boundingBox, ref)); - models.pop_back(); - } - while (pendingNodes.size() > 1) - { - int lhs = pendingNodes.size(); - int rhs = pendingNodes.size(); - float minSurface = std::numeric_limits::max(); - for (int i = 0; i < pendingNodes.size(); ++i) - { - for (int j = 0; j < pendingNodes.size(); ++j) - { - if (i == j) - continue; - AABB combined = AABB::combine(pendingNodes[i]->aabb, pendingNodes[j]->aabb); - float surface = combined.surfaceArea(); - if (minSurface > surface) - { - lhs = i; - rhs = j; - minSurface = surface; - } - } - } - PNode newNode = std::make_unique(AABB::combine(pendingNodes[lhs]->aabb, pendingNodes[rhs]->aabb)); - newNode->left = std::move(pendingNodes[lhs]); - newNode->right = std::move(pendingNodes[rhs]); - pendingNodes.erase(pendingNodes.begin() + lhs); - pendingNodes.erase(pendingNodes.begin() + rhs); - pendingNodes.push_back(std::move(newNode)); - } - hierarchy = std::move(pendingNodes[0]); -} - -std::optional BVH::traceRay(Ray ray) const -{ - auto results = generateIntersections(hierarchy, ray); - float closestT = std::numeric_limits::max(); - IntersectionInfo info; - for (uint32_t i = 0; i < results.size(); ++i) - { - if (results[i].t < closestT) - { - closestT = results[i].t; - info = results[i]; - } - } - if (closestT < std::numeric_limits::max()) - { - return info; - } - return {}; -} - -std::vector BVH::generateIntersections(const PNode& currentNode, Ray ray) const -{ - if (!currentNode->aabb.intersects(ray, 0, std::numeric_limits::max())) - { - return {}; - } - if (currentNode->model.numIndices > 0) - { - auto result = intersectModel(currentNode->model, ray); - if (result.has_value()) - { - return {*result}; - } - else - { - return {}; - } - } - auto leftResults = generateIntersections(currentNode->left, ray); - auto rightResults = generateIntersections(currentNode->right, ray); - - for (auto& it : rightResults) - { - leftResults.push_back(std::move(it)); - } - return leftResults; -} - -std::optional BVH::intersectModel(const ModelReference& reference, const Ray ray) const -{ - std::optional intersection = {}; - float distance = 0; - - for (size_t posIndex = 0, edgeIndex = 0, normalIndex = 0; posIndex < reference.numIndices; posIndex++, edgeIndex += 2, normalIndex++) - { - const auto p0 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].x]; - const auto p1 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].y]; - const auto p2 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].z]; - - const auto e0 = edgesPool[reference.indicesOffset + edgeIndex]; - const auto e1 = edgesPool[reference.indicesOffset + edgeIndex + 1]; - - const auto n = faceNormalsPool[reference.indicesOffset + normalIndex]; - - const auto s = ray.origin - p0; - const auto s1 = glm::cross(ray.direction, e1); - const auto s2 = glm::cross(s, e0); - - const float fraction = 1.0f / glm::dot(s1, e0); - const auto resultVector = glm::vec3(glm::dot(s2, e1), glm::dot(s1, s), glm::dot(s2, ray.direction)) * fraction; - - const float b3 = 1.0f - resultVector.y - resultVector.z; - - if (b3 < 0 || b3 > 1) - continue; - if (resultVector.y < 0 || resultVector.y > 1) - continue; - if (resultVector.z < 0 || resultVector.z > 1) - continue; - - if (resultVector.x < 1e-6) - continue; - - if (!intersection.has_value() || resultVector.x < distance) - { - intersection = IntersectionInfo{.position = ray.origin + ray.direction * resultVector.x, - .normal = n, - .albedo = glm::vec3(0.7f, 0.7f, 0.7f), - .emissive = glm::vec3(0.0f, 0.0f, 0.0f)}; - distance = resultVector.x; - } - } - - return intersection; -} diff --git a/src/scene/BVH.h b/src/scene/BVH.h deleted file mode 100644 index 20e94a7..0000000 --- a/src/scene/BVH.h +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once -#include "AABB.h" -#include "util/Model.h" -#include "util/Ray.h" -#include -#include -#include - -struct ModelReference -{ - uint32_t positionOffset = 0; - uint32_t indicesOffset = 0; - uint32_t numIndices = 0; -}; - -class BVH -{ -public: - void addModel(PModel model, glm::mat4 transform); - void addModels(std::vector models, glm::mat4 transform); - void generate(); - - std::optional traceRay(Ray ray) const; - -private: - std::vector positionPool; - std::vector texCoordsPool; - std::vector indicesPool; - std::vector edgesPool; - std::vector faceNormalsPool; - - DECLARE_REF(Node) - struct Node - { - PNode left; - PNode right; - AABB aabb; - ModelReference model; - Node(AABB aabb) : aabb(aabb) {} - Node(AABB aabb, ModelReference model) : aabb(aabb), model(model) {} - }; - PNode hierarchy; - std::vector models; - - std::vector generateIntersections(const PNode& currentNode, Ray ray) const; - std::optional intersectModel(const ModelReference& reference, Ray ray) const; -}; \ No newline at end of file diff --git a/src/scene/CMakeLists.txt b/src/scene/CMakeLists.txt index 2156aa6..d99a464 100644 --- a/src/scene/CMakeLists.txt +++ b/src/scene/CMakeLists.txt @@ -1,7 +1,7 @@ target_sources(RayTracer PRIVATE AABB.h - BVH.h - BVH.cpp Scene.h - Scene.cpp) \ No newline at end of file + Scene.cpp + Renderer.h + Renderer.cpp) \ No newline at end of file diff --git a/src/scene/Renderer.cpp b/src/scene/Renderer.cpp new file mode 100644 index 0000000..e4f1e7a --- /dev/null +++ b/src/scene/Renderer.cpp @@ -0,0 +1,104 @@ +#include "Renderer.h" +#include "util/ModelLoader.h" +#include +#include +#include + +Renderer::Renderer() +{ + bvh.addModels(ModelLoader::loadModel("../res/models/cube.fbx"), + glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), + glm::vec4(0.0f, 0.0f, 0.0f, 1.0f))); + bvh.generate(); +} + +Renderer::~Renderer() {} + +static bool firstTime = true; +void Renderer::startRender(Camera cam, RenderParameter params) +{ + threadPool.cancel(); + pendingCancel = true; + if (worker.joinable()) + worker.join(); + pendingCancel = false; + image.clear(); + accumulator.clear(); + image.resize(params.width * params.height); + accumulator.resize(params.width * params.height); + worker = std::thread(&Renderer::render, this, cam, params); +} + +glm::vec3 rand01(glm::uvec3 x) +{ // pseudo-random number generator + for (int i = 3; i-- > 0;) + x = ((x >> 8U) ^ glm::uvec3(x.y, x.z, x.x)) * 1103515245U; + return glm::vec3(x) * (1.0f / float(0xffffffffU)); +} + +void Renderer::render(Camera camera, RenderParameter params) +{ + for (int samp = 0; samp < params.numSamples; ++samp) + { + if (pendingCancel) + return; + Batch batch; + for (int w = 0; w < params.width; ++w) + { + batch.jobs.push_back( + [&](int w, int samp) -> Task + { + for (int h = 0; h < params.height; ++h) + { + Ray cam = Ray(camera.position, glm::normalize(camera.direction)); + glm::vec3 cx = + glm::normalize(glm::cross(cam.direction, abs(cam.direction.y) < 0.9 ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1))), + cy = glm::cross(cx, cam.direction); + const glm::vec2 sdim = camera.sensorSize; // sensor size (36 x 24 mm) + + float S_I = (camera.S_O * camera.f) / (camera.S_O - camera.f); + + //-- sample sensor + glm::uvec2 pix = glm::uvec2(w, h); + glm::vec3 rnd1 = rand01(glm::uvec3(pix, samp)); + glm::vec2 rnd2 = 2.0f * glm::vec2(rnd1); // vvv tent filter sample + glm::vec2 tent = + glm::vec2(rnd2.x < 1 ? sqrt(rnd2.x) - 1 : 1 - sqrt(2 - rnd2.x), rnd2.y < 1 ? sqrt(rnd2.y) - 1 : 1 - sqrt(2 - rnd2.y)); + glm::vec2 s = + ((glm::vec2(pix) + 0.5f * (0.5f + glm::vec2((samp / 2) % 2, samp % 2) + tent)) / glm::vec2(params.width, params.height) - + 0.5f) * + sdim; + glm::vec3 spos = cam.origin + cx * s.x + cy * s.y, lc = cam.origin + cam.direction * 0.035f; // sample on 3d sensor plane + glm::vec3 accrad = glm::vec3(0), accmat = glm::vec3(1); // initialize accumulated radiance and bxdf + Ray r = Ray(lc, normalize(lc - spos)); // construct ray + + //-- setup lens + glm::vec3 lensP = lc; + glm::vec3 lensN = -cam.direction; + glm::vec3 lensX = glm::cross(lensN, glm::vec3(0, 1, 0)); // the exact vector doesnt matter + glm::vec3 lensY = glm::cross(lensN, lensX); + + glm::vec3 lensSample = lensP + rnd1.x * camera.A * lensX + rnd1.y * camera.A * lensY; + + glm::vec3 focalPoint = cam.origin + (camera.S_O + S_I) * cam.direction; + float t = glm::dot(focalPoint - r.origin, lensN) / glm::dot(r.direction, lensN); + glm::vec3 focus = r.origin + t * r.direction; + r = Ray(lensSample, normalize(focus - lensSample)); // TODO: Fix lens + + auto intersection = bvh.traceRay(r); + + if (intersection.has_value()) + { + accumulator[w + h * params.width] = intersection->albedo; + } + } + co_return; + }(w, samp)); + } + auto start = std::chrono::high_resolution_clock::now(); + threadPool.runBatch(std::move(batch)); + auto end = std::chrono::high_resolution_clock::now(); + std::cout << std::chrono::duration_cast(end - start).count() << std::endl; + std::memcpy(image.data(), accumulator.data(), accumulator.size() * sizeof(glm::vec3)); + } +} diff --git a/src/scene/Renderer.h b/src/scene/Renderer.h new file mode 100644 index 0000000..f0b5f7e --- /dev/null +++ b/src/scene/Renderer.h @@ -0,0 +1,33 @@ +#pragma once +#include "Scene.h" +#include "window/Window.h" +#include "util/Camera.h" +#include "ThreadPool.h" + +struct RenderParameter +{ + int width; + int height; + int numSamples; +}; + +class Renderer +{ + public: + Renderer(); + virtual ~Renderer(); + void startRender(Camera cam, RenderParameter params); + constexpr const std::vector& getImage() const { return image; } + private: + virtual void render(Camera cam, RenderParameter params); + ThreadPool threadPool; + std::thread worker; + std::atomic_bool pendingCancel = false; + // the thing being displayed + std::vector image; + // radiance accumulator + std::vector accumulator; + std::vector pointLights; + std::vector directionalLights; + Scene bvh; +}; \ No newline at end of file diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp index c30ce97..a8848d7 100644 --- a/src/scene/Scene.cpp +++ b/src/scene/Scene.cpp @@ -1,104 +1,170 @@ #include "Scene.h" -#include "util/ModelLoader.h" -#include -#include -#include +#include +#include -Scene::Scene() +void Scene::addModel(PModel model, glm::mat4 transform) { - bvh.addModels(ModelLoader::loadModel("../res/models/cube.fbx"), - glm::mat4(glm::vec4(1.0f, 0.0f, 0.0f, 0.0f), glm::vec4(0.0f, 1.0f, 0.0f, 0.0f), glm::vec4(0.0f, 0.0f, 1.0f, 0.0f), - glm::vec4(0.0f, 0.0f, 0.0f, 1.0f))); - bvh.generate(); + model->transform(transform); + models.push_back(std::move(model)); } -Scene::~Scene() {} - -static bool firstTime = true; -void Scene::startRender(Camera cam, RenderParameter params) +void Scene::addModels(std::vector _models, glm::mat4 transform) { - threadPool.cancel(); - pendingCancel = true; - if (worker.joinable()) - worker.join(); - pendingCancel = false; - image.clear(); - accumulator.clear(); - image.resize(params.width * params.height); - accumulator.resize(params.width * params.height); - worker = std::thread(&Scene::render, this, cam, params); -} - -glm::vec3 rand01(glm::uvec3 x) -{ // pseudo-random number generator - for (int i = 3; i-- > 0;) - x = ((x >> 8U) ^ glm::uvec3(x.y, x.z, x.x)) * 1103515245U; - return glm::vec3(x) * (1.0f / float(0xffffffffU)); -} - -void Scene::render(Camera camera, RenderParameter params) -{ - for (int samp = 0; samp < params.numSamples; ++samp) + for (auto& _model : _models) { - if (pendingCancel) - return; - Batch batch; - for (int w = 0; w < params.width; ++w) - { - batch.jobs.push_back( - [&](int w, int samp) -> Task - { - for (int h = 0; h < params.height; ++h) - { - Ray cam = Ray(camera.position, glm::normalize(camera.direction)); - glm::vec3 cx = - glm::normalize(glm::cross(cam.direction, abs(cam.direction.y) < 0.9 ? glm::vec3(0, 1, 0) : glm::vec3(0, 0, 1))), - cy = glm::cross(cx, cam.direction); - const glm::vec2 sdim = camera.sensorSize; // sensor size (36 x 24 mm) - - float S_I = (camera.S_O * camera.f) / (camera.S_O - camera.f); - - //-- sample sensor - glm::uvec2 pix = glm::uvec2(w, h); - glm::vec3 rnd1 = rand01(glm::uvec3(pix, samp)); - glm::vec2 rnd2 = 2.0f * glm::vec2(rnd1); // vvv tent filter sample - glm::vec2 tent = - glm::vec2(rnd2.x < 1 ? sqrt(rnd2.x) - 1 : 1 - sqrt(2 - rnd2.x), rnd2.y < 1 ? sqrt(rnd2.y) - 1 : 1 - sqrt(2 - rnd2.y)); - glm::vec2 s = - ((glm::vec2(pix) + 0.5f * (0.5f + glm::vec2((samp / 2) % 2, samp % 2) + tent)) / glm::vec2(params.width, params.height) - - 0.5f) * - sdim; - glm::vec3 spos = cam.origin + cx * s.x + cy * s.y, lc = cam.origin + cam.direction * 0.035f; // sample on 3d sensor plane - glm::vec3 accrad = glm::vec3(0), accmat = glm::vec3(1); // initialize accumulated radiance and bxdf - Ray r = Ray(lc, normalize(lc - spos)); // construct ray - - //-- setup lens - glm::vec3 lensP = lc; - glm::vec3 lensN = -cam.direction; - glm::vec3 lensX = glm::cross(lensN, glm::vec3(0, 1, 0)); // the exact vector doesnt matter - glm::vec3 lensY = glm::cross(lensN, lensX); - - glm::vec3 lensSample = lensP + rnd1.x * camera.A * lensX + rnd1.y * camera.A * lensY; - - glm::vec3 focalPoint = cam.origin + (camera.S_O + S_I) * cam.direction; - float t = glm::dot(focalPoint - r.origin, lensN) / glm::dot(r.direction, lensN); - glm::vec3 focus = r.origin + t * r.direction; - r = Ray(lensSample, normalize(focus - lensSample)); // TODO: Fix lens - - auto intersection = bvh.traceRay(r); - - if (intersection.has_value()) - { - accumulator[w + h * params.width] = intersection->albedo; - } - } - co_return; - }(w, samp)); - } - auto start = std::chrono::high_resolution_clock::now(); - threadPool.runBatch(std::move(batch)); - auto end = std::chrono::high_resolution_clock::now(); - std::cout << std::chrono::duration_cast(end - start).count() << std::endl; - std::memcpy(image.data(), accumulator.data(), accumulator.size() * sizeof(glm::vec3)); + _model->transform(transform); + models.push_back(std::move(_model)); } } + +void Scene::generate() +{ + std::vector pendingNodes; + while (!models.empty()) + { + auto& model = models.back(); + ModelReference ref = { + .positionOffset = (uint32_t)positionPool.size(), + .indicesOffset = (uint32_t)indicesPool.size(), + .numIndices = (uint32_t)model->indices.size(), + }; + for (uint32_t i = 0; i < model->positions.size(); ++i) + { + positionPool.push_back(model->positions[i]); + texCoordsPool.push_back(model->texCoords[i]); + } + for (uint32_t i = 0; i < model->indices.size(); ++i) + { + indicesPool.push_back(model->indices[i]); + edgesPool.push_back(model->edges[i]); + faceNormalsPool.push_back(model->faceNormals[i]); + } + pendingNodes.push_back(std::make_unique(model->boundingBox, ref)); + models.pop_back(); + } + while (pendingNodes.size() > 1) + { + int lhs = pendingNodes.size(); + int rhs = pendingNodes.size(); + float minSurface = std::numeric_limits::max(); + for (int i = 0; i < pendingNodes.size(); ++i) + { + for (int j = 0; j < pendingNodes.size(); ++j) + { + if (i == j) + continue; + AABB combined = AABB::combine(pendingNodes[i]->aabb, pendingNodes[j]->aabb); + float surface = combined.surfaceArea(); + if (minSurface > surface) + { + lhs = i; + rhs = j; + minSurface = surface; + } + } + } + PNode newNode = std::make_unique(AABB::combine(pendingNodes[lhs]->aabb, pendingNodes[rhs]->aabb)); + newNode->left = std::move(pendingNodes[lhs]); + newNode->right = std::move(pendingNodes[rhs]); + pendingNodes.erase(pendingNodes.begin() + lhs); + pendingNodes.erase(pendingNodes.begin() + rhs); + pendingNodes.push_back(std::move(newNode)); + } + hierarchy = std::move(pendingNodes[0]); +} + +std::optional Scene::traceRay(Ray ray) const +{ + auto results = generateIntersections(hierarchy, ray); + float closestT = std::numeric_limits::max(); + IntersectionInfo info; + for (uint32_t i = 0; i < results.size(); ++i) + { + if (results[i].t < closestT) + { + closestT = results[i].t; + info = results[i]; + } + } + if (closestT < std::numeric_limits::max()) + { + return info; + } + return {}; +} + +std::vector Scene::generateIntersections(const PNode& currentNode, Ray ray) const +{ + if (!currentNode->aabb.intersects(ray, 0, std::numeric_limits::max())) + { + return {}; + } + if (currentNode->model.numIndices > 0) + { + auto result = intersectModel(currentNode->model, ray); + if (result.has_value()) + { + return {*result}; + } + else + { + return {}; + } + } + auto leftResults = generateIntersections(currentNode->left, ray); + auto rightResults = generateIntersections(currentNode->right, ray); + + for (auto& it : rightResults) + { + leftResults.push_back(std::move(it)); + } + return leftResults; +} + +std::optional Scene::intersectModel(const ModelReference& reference, const Ray ray) const +{ + std::optional intersection = {}; + float distance = 0; + + for (size_t posIndex = 0, edgeIndex = 0, normalIndex = 0; posIndex < reference.numIndices; posIndex++, edgeIndex += 2, normalIndex++) + { + const auto p0 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].x]; + const auto p1 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].y]; + const auto p2 = positionPool[reference.positionOffset + indicesPool[reference.indicesOffset + posIndex].z]; + + const auto e0 = edgesPool[reference.indicesOffset + edgeIndex]; + const auto e1 = edgesPool[reference.indicesOffset + edgeIndex + 1]; + + const auto n = faceNormalsPool[reference.indicesOffset + normalIndex]; + + const auto s = ray.origin - p0; + const auto s1 = glm::cross(ray.direction, e1); + const auto s2 = glm::cross(s, e0); + + const float fraction = 1.0f / glm::dot(s1, e0); + const auto resultVector = glm::vec3(glm::dot(s2, e1), glm::dot(s1, s), glm::dot(s2, ray.direction)) * fraction; + + const float b3 = 1.0f - resultVector.y - resultVector.z; + + if (b3 < 0 || b3 > 1) + continue; + if (resultVector.y < 0 || resultVector.y > 1) + continue; + if (resultVector.z < 0 || resultVector.z > 1) + continue; + + if (resultVector.x < 1e-6) + continue; + + if (!intersection.has_value() || resultVector.x < distance) + { + intersection = IntersectionInfo{.position = ray.origin + ray.direction * resultVector.x, + .normal = n, + .albedo = glm::vec3(0.7f, 0.7f, 0.7f), + .emissive = glm::vec3(0.0f, 0.0f, 0.0f)}; + distance = resultVector.x; + } + } + + return intersection; +} diff --git a/src/scene/Scene.h b/src/scene/Scene.h index d71f9ef..ee81805 100644 --- a/src/scene/Scene.h +++ b/src/scene/Scene.h @@ -1,14 +1,16 @@ #pragma once -#include "BVH.h" -#include "window/Window.h" -#include "util/Camera.h" -#include "ThreadPool.h" +#include "AABB.h" +#include "util/Model.h" +#include "util/Ray.h" +#include +#include +#include -struct RenderParameter +struct ModelReference { - int width; - int height; - int numSamples; + uint32_t positionOffset = 0; + uint32_t indicesOffset = 0; + uint32_t numIndices = 0; }; struct PointLight @@ -26,21 +28,38 @@ struct DirectionalLight class Scene { - public: - Scene(); - virtual ~Scene(); - void startRender(Camera cam, RenderParameter params); - constexpr const std::vector& getImage() const { return image; } - private: - virtual void render(Camera cam, RenderParameter params); - ThreadPool threadPool; - std::thread worker; - std::atomic_bool pendingCancel = false; - // the thing being displayed - std::vector image; - // radiance accumulator - std::vector accumulator; - std::vector pointLights; - std::vector directionalLights; - BVH bvh; +public: + void addPointLight(PointLight point) { points.push_back(point); } + void addDirectionalLight(DirectionalLight dir) { directionalLights.push_back(dir); } + void addModel(PModel model, glm::mat4 transform); + void addModels(std::vector models, glm::mat4 transform); + void generate(); + + std::optional traceRay(Ray ray) const; + +private: + std::vector positionPool; + std::vector texCoordsPool; + std::vector indicesPool; + std::vector edgesPool; + std::vector faceNormalsPool; + + std::vector points; + std::vector directionalLights; + + DECLARE_REF(Node) + struct Node + { + PNode left; + PNode right; + AABB aabb; + ModelReference model; + Node(AABB aabb) : aabb(aabb) {} + Node(AABB aabb, ModelReference model) : aabb(aabb), model(model) {} + }; + PNode hierarchy; + std::vector models; + + std::vector generateIntersections(const PNode& currentNode, Ray ray) const; + std::optional intersectModel(const ModelReference& reference, Ray ray) const; }; \ No newline at end of file