From cc2f92aaab8b77f067ab806267aee8a9af89e738 Mon Sep 17 00:00:00 2001 From: Dynamitos Date: Sun, 26 Jan 2025 19:43:06 +0100 Subject: [PATCH] it does something --- src/main.cpp | 8 +- src/scene/Renderer.cpp | 23 +++--- src/scene/Scene.cpp | 184 ++++++++++++++++++++++++++++++++--------- src/scene/Scene.h | 23 +++--- src/util/BRDF.cpp | 6 +- src/util/BRDF.h | 11 +-- src/util/Material.cpp | 10 ++- src/util/Material.h | 9 +- src/util/Model.h | 2 +- src/util/Ray.h | 9 +- src/window/Window.cpp | 2 +- src/window/Window.h | 1 - 12 files changed, 199 insertions(+), 89 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 6fee2ff..79abbf5 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,8 +10,8 @@ int main() Window window(1920, 1080); scene.startRender( Camera{ - .position = glm::vec3(0, 10, 10), - .direction = glm::vec3(0, -1, -1), + .position = glm::vec3(5, 5, 5), + .direction = glm::vec3(-1, -1, -1), }, RenderParameter{ .width = 1920, @@ -25,8 +25,8 @@ int main() { scene.startRender( Camera{ - .position = glm::vec3(0, 10, 10), - .direction = glm::vec3(0, -1, -1), + .position = glm::vec3(5, 5, 5), + .direction = glm::vec3(-1, -1, -1), }, RenderParameter{ .width = 1920, diff --git a/src/scene/Renderer.cpp b/src/scene/Renderer.cpp index 6b84ae6..89a3640 100644 --- a/src/scene/Renderer.cpp +++ b/src/scene/Renderer.cpp @@ -6,9 +6,9 @@ Renderer::Renderer() { - bvh.addPointLight(PointLight{ - .position = glm::vec3(2, 0, 2), - .color = glm::vec3(0, 1, 0), + bvh.addDirectionalLight(DirectionalLight{ + .direction = glm::normalize(glm::vec3(-0.4f, -0.2f, -0.6f)), + .color = glm::vec3(1, 1, 1), }); 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), @@ -40,6 +40,8 @@ glm::vec3 rand01(glm::uvec3 x) return glm::vec3(x) * (1.0f / float(0xffffffffU)); } +thread_local glm::vec3 rnd01; + void Renderer::render(Camera camera, RenderParameter params) { for (int samp = 0; samp < params.numSamples; ++samp) @@ -64,8 +66,8 @@ void Renderer::render(Camera camera, RenderParameter params) //-- 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 + rnd01 = rand01(glm::uvec3(pix, samp)); + glm::vec2 rnd2 = 2.0f * glm::vec2(rnd01); // 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 = @@ -82,20 +84,17 @@ void Renderer::render(Camera camera, RenderParameter params) 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 lensSample = lensP + rnd01.x * camera.A * lensX + rnd01.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); + Payload payload; + bvh.traceRay(r, payload, 1e-4, 1e20); - if (intersection.has_value()) - { - - accumulator[w + h * params.width] = intersection->shadingInfo.albedo; - } + accumulator[w + h * params.width] += payload.accumulatedRadiance / float(params.numSamples); } co_return; }(w, samp)); diff --git a/src/scene/Scene.cpp b/src/scene/Scene.cpp index 7215bf8..f3151c4 100644 --- a/src/scene/Scene.cpp +++ b/src/scene/Scene.cpp @@ -1,5 +1,6 @@ #include "Scene.h" #include +#include #include void Scene::addModel(PModel model, glm::mat4 transform) @@ -36,7 +37,8 @@ void Scene::generate() for (uint32_t i = 0; i < model->indices.size(); ++i) { indicesPool.push_back(model->indices[i]); - edgesPool.push_back(model->edges[i]); + edgesPool.push_back(model->edges[i * 2 + 0]); + edgesPool.push_back(model->edges[i * 2 + 1]); faceNormalsPool.push_back(model->faceNormals[i]); } pendingNodes.push_back(std::make_unique(model->boundingBox, ref)); @@ -73,9 +75,11 @@ void Scene::generate() hierarchy = std::move(pendingNodes[0]); } -std::optional Scene::traceRay(Ray ray) const +extern glm::vec3 rnd01; + +void Scene::traceRay(Ray ray, Payload& payload, const float tmin, const float tmax) const noexcept { - auto results = generateIntersections(hierarchy, ray); + auto results = generateIntersections(hierarchy, ray, tmin, tmax); float closestT = std::numeric_limits::max(); IntersectionInfo info; for (uint32_t i = 0; i < results.size(); ++i) @@ -88,31 +92,85 @@ std::optional Scene::traceRay(Ray ray) const } if (closestT < std::numeric_limits::max()) { - return info; + // russian roulette ray termination + float p = std::max(std::max(info.brdf.albedo.x, info.brdf.albedo.y), info.brdf.albedo.z); + if (payload.depth > 5) + { + if (rnd01.z >= p) + return; + else + payload.accumulatedMaterial /= p; + } + // emissive + payload.accumulatedRadiance += payload.accumulatedMaterial * info.brdf.emissive * payload.emissive; + payload.accumulatedMaterial *= info.brdf.albedo; + + // direct lighting + for (const auto& d : directionalLights) + { + // if there is an intersection, the light is occluded so no lighting + if (!testIntersection(hierarchy, Ray(info.hitInfo.position, -d.direction), 1e-4, 1e20)) + { + payload.accumulatedRadiance += info.brdf.evaluate(info.hitInfo, -ray.direction, d.direction, d.color); + } + } + for (const auto& p : pointLights) + { + glm::vec3 lightDir = p.position - info.hitInfo.position; + // if (!testIntersection(hierarchy, Ray(info.hitInfo.position, -lightDir), 1e-4, 1)) + { + float d = glm::length(lightDir); + float illuminance = std::max(1 - d / p.attenuation, 0.0f); + + payload.accumulatedRadiance += illuminance * info.brdf.evaluate(info.hitInfo, -ray.direction, lightDir, p.color); + } + } + + // TODO: Next Event Estimation for mesh lights + + // indirect lighting + float r1 = 2 * std::numbers::pi * rnd01.x; + float r2 = rnd01.y; + float r2s = sqrt(r2); + glm::vec3 w = info.hitInfo.normalLight; + glm::vec3 u = glm::normalize(glm::cross(std::abs(w.x) > 0.1 ? glm::vec3(0, 1, 0) : glm::vec3(1, 0, 0), w)); + glm::vec3 v = glm::cross(w, u); + ray = Ray(info.hitInfo.position, glm::normalize(u * cos(r1) * r2s + v * sin(r1) * r2s + w * sqrt(1 - r2))); + payload.emissive = 0; + payload.depth++; + traceRay(ray, payload, tmin, tmax); } - return {}; } -std::vector Scene::generateIntersections(const PNode& currentNode, Ray ray) const +bool Scene::testIntersection(const PNode& currentNode, const Ray ray, const float tmin, float tmax) const noexcept { - if (!currentNode->aabb.intersects(ray, 0, std::numeric_limits::max())) + if (!currentNode->aabb.intersects(ray, tmin, tmax)) + { + return false; + } + if (currentNode->model.numIndices > 0) + { + return testModel(currentNode->model, ray, tmin, tmax); + } + auto leftResults = testIntersection(currentNode->left, ray, tmin, tmax); + auto rightResults = testIntersection(currentNode->right, ray, tmin, tmax); + + return leftResults || rightResults; +} + +std::vector Scene::generateIntersections(const PNode& currentNode, const Ray ray, const float tmin, + float tmax) const noexcept +{ + if (!currentNode->aabb.intersects(ray, tmin, tmax)) { return {}; } if (currentNode->model.numIndices > 0) { - auto result = intersectModel(currentNode->model, ray); - if (result.has_value()) - { - return {*result}; - } - else - { - return {}; - } + return intersectModel(currentNode->model, ray, tmin, tmax); } - auto leftResults = generateIntersections(currentNode->left, ray); - auto rightResults = generateIntersections(currentNode->right, ray); + auto leftResults = generateIntersections(currentNode->left, ray, tmin, tmax); + auto rightResults = generateIntersections(currentNode->right, ray, tmin, tmax); for (auto& it : rightResults) { @@ -121,9 +179,8 @@ std::vector Scene::generateIntersections(const PNode& currentN return leftResults; } -std::optional Scene::intersectModel(const ModelReference& reference, const Ray ray) const +bool Scene::testModel(const ModelReference& reference, const Ray ray, const float tmin, float tmax) const noexcept { - std::optional intersection = {}; float distance = 0; for (size_t posIndex = 0, edgeIndex = 0, normalIndex = 0; posIndex < reference.numIndices; posIndex++, edgeIndex += 2, normalIndex++) @@ -163,28 +220,75 @@ std::optional Scene::intersectModel(const ModelReference& refe if (resultVector.z < 0 || resultVector.z > 1) continue; - if (resultVector.x < 1e-6) + if (resultVector.x < tmin || resultVector.x > tmax) + continue; + return true; + } + + return false; +} +std::vector Scene::intersectModel(const ModelReference& reference, const Ray ray, const float tmin, + float tmax) const noexcept +{ + std::vector intersection = {}; + float distance = 0; + + for (size_t posIndex = 0, edgeIndex = 0, normalIndex = 0; posIndex < reference.numIndices; posIndex++, edgeIndex += 2, normalIndex++) + { + const auto i0 = indicesPool[reference.indicesOffset + posIndex].x; + const auto i1 = indicesPool[reference.indicesOffset + posIndex].y; + const auto i2 = indicesPool[reference.indicesOffset + posIndex].z; + + const auto& p0 = positionPool[reference.positionOffset + i0]; + const auto& p1 = positionPool[reference.positionOffset + i1]; + const auto& p2 = positionPool[reference.positionOffset + i2]; + + const auto& t0 = texCoordsPool[reference.positionOffset + i0]; + const auto& t1 = texCoordsPool[reference.positionOffset + i1]; + const auto& t2 = texCoordsPool[reference.positionOffset + i2]; + + 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; + + const auto texCoords = t0 * resultVector.y + t1 * resultVector.z + t2 * b3; + + if (b3 < 0 || b3 > 1) + continue; + if (resultVector.y < 0 || resultVector.y > 1) + continue; + if (resultVector.z < 0 || resultVector.z > 1) continue; - if (!intersection.has_value() || resultVector.x < distance) - { - intersection = IntersectionInfo{ - .hitInfo = - { - .t = resultVector.x, - .position = ray.origin + ray.direction * resultVector.x, - .normal = n, - .texCoords = texCoords, - }, - .shadingInfo = - { - .albedo = glm::vec3(texCoords, 0.0f), - .emissive = glm::vec3(0.0f, 0.0f, 0.0f), - .type = MaterialType::DIFFUSE, - }, - }; - distance = resultVector.x; - } + if (resultVector.x < tmin || resultVector.x > tmax) + continue; + + intersection.push_back(IntersectionInfo{ + .hitInfo = + { + .t = resultVector.x, + .position = ray.origin + ray.direction * resultVector.x, + .normal = n, + .normalLight = n, // todo: flip based on something, idk what + .texCoords = texCoords, + }, + .brdf = + { + .albedo = glm::vec3(texCoords, 0.0f), + .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 ee81805..0fa5101 100644 --- a/src/scene/Scene.h +++ b/src/scene/Scene.h @@ -15,27 +15,27 @@ struct ModelReference struct PointLight { - glm::vec3 position; - glm::vec3 color; - float attenuation; + glm::vec3 position = glm::vec3(0, 0, 0); + glm::vec3 color = glm::vec3(1, 1, 1); + float attenuation = 1; }; struct DirectionalLight { - glm::vec3 direction; - glm::vec3 color; + glm::vec3 direction = glm::vec3(0, 1, 0); + glm::vec3 color = glm::vec3(1, 1, 1); }; class Scene { public: - void addPointLight(PointLight point) { points.push_back(point); } + void addPointLight(PointLight point) { pointLights.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; + void traceRay(Ray ray, Payload& payload, const float tmin, const float tmax) const noexcept; private: std::vector positionPool; @@ -44,7 +44,7 @@ private: std::vector edgesPool; std::vector faceNormalsPool; - std::vector points; + std::vector pointLights; std::vector directionalLights; DECLARE_REF(Node) @@ -60,6 +60,9 @@ private: PNode hierarchy; std::vector models; - std::vector generateIntersections(const PNode& currentNode, Ray ray) const; - std::optional intersectModel(const ModelReference& reference, Ray ray) const; + // tests if a ray intersects any geometry, no hit information, for shadow rays + bool testIntersection(const PNode& currentNode, const Ray ray, const float tmin, const float tmax) const noexcept; + std::vector generateIntersections(const PNode& currentNode, const Ray ray, const float tmin, const float tmax) const noexcept; + bool testModel(const ModelReference& reference, const Ray ray, const float tmin, const float tmax) const noexcept; + std::vector intersectModel(const ModelReference& reference, const Ray ray, const float tmin, const float tmax) const noexcept; }; \ No newline at end of file diff --git a/src/util/BRDF.cpp b/src/util/BRDF.cpp index 4087c7f..89a12a9 100644 --- a/src/util/BRDF.cpp +++ b/src/util/BRDF.cpp @@ -1,11 +1,13 @@ #include "BRDF.h" +#include "Model.h" -glm::vec3 BlinnPhong::evaluate(HitInfo hit, glm::vec3 viewDir, glm::vec3 lightDir, glm::vec3 lightColor) +glm::vec3 BRDF::evaluate(HitInfo hit, glm::vec3 viewDir, glm::vec3 lightDir, glm::vec3 lightColor) { + // todo: switch for materialtype, this is blinnphong glm::vec3 normal = hit.normal; float diffuse = std::max(glm::dot(normal, lightDir), 0.0f); glm::vec3 h = glm::normalize(lightDir + viewDir); float specular = pow(std::min(std::max(glm::dot(normal, h), 0.0f), 1.0f), shininess); return (albedo * diffuse * lightColor) + (specularColor * specular); -} \ No newline at end of file +} diff --git a/src/util/BRDF.h b/src/util/BRDF.h index 2cff04a..fb57b78 100644 --- a/src/util/BRDF.h +++ b/src/util/BRDF.h @@ -1,17 +1,18 @@ #pragma once -#include "Material.h" +#include -class BRDF +enum class MaterialType { - virtual glm::vec3 evaluate(HitInfo hit, glm::vec3 viewDir, glm::vec3 lightDir, glm::vec3 lightColor) = 0; + BlinnPhong }; -class BlinnPhong : public BRDF +struct BRDF { glm::vec3 albedo = glm::vec3(1, 1, 1); float alpha = 1; glm::vec3 specularColor = glm::vec3(1, 1, 1); float shininess = 0; glm::vec3 emissive = glm::vec3(0, 0, 0); - virtual glm::vec3 evaluate(HitInfo hit, glm::vec3 viewDir, glm::vec3 lightDir, glm::vec3 lightColor); + MaterialType materialType; + glm::vec3 evaluate(struct HitInfo hit, glm::vec3 viewDir, glm::vec3 lightDir, glm::vec3 lightColor); }; \ No newline at end of file diff --git a/src/util/Material.cpp b/src/util/Material.cpp index 7622417..0a0327b 100644 --- a/src/util/Material.cpp +++ b/src/util/Material.cpp @@ -1,6 +1,12 @@ #include "Material.h" +#include "BRDF.h" -std::unique_ptr Material::evaluate(HitInfo hitInfo) +BRDF Material::evaluate(HitInfo hitInfo) { - return std::make_unique(); + return BRDF{ + .albedo = glm::vec3(hitInfo.texCoords, 0), + .alpha = 1, + .emissive = glm::vec3(0, 0, 0), + .materialType = MaterialType::BlinnPhong, + }; } diff --git a/src/util/Material.h b/src/util/Material.h index 684f427..7fee039 100644 --- a/src/util/Material.h +++ b/src/util/Material.h @@ -4,17 +4,10 @@ #include "Ray.h" #include "BRDF.h" -enum class MaterialType -{ - DIFFUSE, - SPECULAR, - REFRACTIVE, -}; - class Material { public: - std::unique_ptr evaluate(HitInfo hitInfo); + BRDF evaluate(HitInfo hitInfo); PTexture albedoTexture; PTexture emissiveTexture; }; \ No newline at end of file diff --git a/src/util/Model.h b/src/util/Model.h index eee1ce6..21364c4 100644 --- a/src/util/Model.h +++ b/src/util/Model.h @@ -8,7 +8,7 @@ struct IntersectionInfo { HitInfo hitInfo; - MaterialInfo shadingInfo; + BRDF brdf; }; class Model { diff --git a/src/util/Ray.h b/src/util/Ray.h index d7ae4db..47f73d9 100644 --- a/src/util/Ray.h +++ b/src/util/Ray.h @@ -4,15 +4,15 @@ struct Payload { glm::vec3 accumulatedRadiance = glm::vec3(0); - glm::vec3 accumulatedMaterial = glm::vec3(0); + glm::vec3 accumulatedMaterial = glm::vec3(1); + uint32_t depth = 0; + float emissive = 1; }; struct Ray { glm::vec3 origin; glm::vec3 direction; - uint32_t depth = 0; - Payload payload; }; struct HitInfo @@ -20,5 +20,8 @@ struct HitInfo float t; glm::vec3 position; glm::vec3 normal; + // not entirely sure what that does + // its the normal being flipped based on some dot product + glm::vec3 normalLight; glm::vec2 texCoords; }; \ No newline at end of file diff --git a/src/window/Window.cpp b/src/window/Window.cpp index d242e2f..1d5d00c 100644 --- a/src/window/Window.cpp +++ b/src/window/Window.cpp @@ -51,7 +51,7 @@ Window::Window(int width, int height) : width(width), height(height) uniform sampler2D tex; - void main() { color = texture(tex, texcoords); }); + void main() { color = texture(tex, vec2(texcoords.x, -texcoords.y)); }); glShaderSource(vertShader, 1, &vertCode, nullptr); glCompileShader(vertShader); int success; diff --git a/src/window/Window.h b/src/window/Window.h index 5c1a4be..c31d48b 100644 --- a/src/window/Window.h +++ b/src/window/Window.h @@ -20,6 +20,5 @@ class Window GLuint program; GLuint vertShader; GLuint fragShader; - GLuint textureLocation; GLFWwindow* window; }; \ No newline at end of file