From a83ecb32bb43bf5c73f2335bfc004a68394b05ac Mon Sep 17 00:00:00 2001 From: Bram Verhulst Date: Sat, 9 Nov 2024 21:03:54 +0100 Subject: [PATCH] Final Commit for now This adds model support, AABB, and other optimisations --- .gitignore | 10 +- project/src/DataTypes.h | 112 +++++++++++- project/src/Renderer.cpp | 255 ++++++++++----------------- project/src/Renderer.h | 3 + project/src/Scene.cpp | 74 +++++++- project/src/Scene.h | 23 ++- project/src/Utils.h | 369 +++++++++++++++++++++++++-------------- project/src/Vector3.cpp | 9 + project/src/Vector3.h | 3 + project/src/main.cpp | 2 +- 10 files changed, 558 insertions(+), 302 deletions(-) diff --git a/.gitignore b/.gitignore index ec389d5..c6c435b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,9 @@ bld/ # Visual Studio .vs/ +# Clion +.idea + # Do not ignore libs !project/libs/** @@ -27,4 +30,9 @@ bld/ *.ipr *.iws *.iws.* -cmake-build-debug/** \ No newline at end of file +cmake-build-debug/** + +cmake-build-default/** +cmake-build-minsizerel/** +cmake-build-relwithdebinfo/** +cmake-build-release/** \ No newline at end of file diff --git a/project/src/DataTypes.h b/project/src/DataTypes.h index 9ebdd04..3e4a417 100644 --- a/project/src/DataTypes.h +++ b/project/src/DataTypes.h @@ -85,9 +85,17 @@ namespace dae Matrix translationTransform{}; Matrix scaleTransform{}; + Vector3 minAABB{}; + Vector3 maxAABB{}; + + Vector3 transformedMinAABB{}; + Vector3 transformedMaxAABB{}; + std::vector transformedPositions{}; std::vector transformedNormals{}; + + void Translate(const Vector3& translation) { translationTransform = Matrix::CreateTranslation(translation); @@ -124,21 +132,107 @@ namespace dae void CalculateNormals() { - throw std::runtime_error("Not Implemented Yet"); - } + constexpr int triSides{ 3 }; + const size_t nTris{ indices.size() / triSides }; + normals.resize(nTris); + + for(size_t triIndex{}; triIndex < nTris; ++triIndex) + { + const Vector3& v0{ positions[indices[triIndex * triSides + 0]] }; + const Vector3& v1{ positions[indices[triIndex * triSides + 1]] }; + const Vector3& v2{ positions[indices[triIndex * triSides + 2]] }; + + const Vector3 v0v1{ v1 - v0 }; + const Vector3 v0v2{ v2 - v0 }; + + const Vector3 normal{ Vector3::Cross(v0v1, v0v2).Normalized() }; + + normals[triIndex] = normal; + } + } void UpdateTransforms() { - throw std::runtime_error("Not Implemented Yet"); - //Calculate Final Transform - //const auto finalTransform = ... + const size_t vertexCount = positions.size(); + const size_t normalCount = normals.size(); + const Matrix finalTransform{ scaleTransform * rotationTransform * translationTransform }; + transformedPositions.resize(vertexCount); + transformedNormals.resize(normalCount); - //Transform Positions (positions > transformedPositions) - //... - //Transform Normals (normals > transformedNormals) - //... + for (size_t i = 0; i < vertexCount; ++i) + { + transformedPositions[i] = finalTransform.TransformPoint(positions[i]); + } + for(size_t i = 0; i < normalCount; ++i){ + transformedNormals[i] = finalTransform.TransformVector(normals[i]); + } + + UpdateTransformedAABB(finalTransform); } + + void UpdateAABB(){ + if(positions.size() > 0){ + minAABB = positions[0]; + maxAABB = positions[0]; + for (auto& p : positions) + { + minAABB = Vector3::Min(minAABB, p); + maxAABB = Vector3::Max(maxAABB, p); + } + } + } + + void UpdateTransformedAABB(const Matrix& finalTransform) { + // AABB update: be careful -> transform the 8 vertices of the aabb + // and calculate new min and max. + Vector3 tMinAABB = finalTransform.TransformPoint(minAABB); + Vector3 tMaxAABB = tMinAABB; + + // (xmax, ymin, zmin) + Vector3 tAABB = finalTransform.TransformPoint(maxAABB.x, minAABB.y, minAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmax, ymin, zmax) + tAABB = finalTransform.TransformPoint(maxAABB.x, minAABB.y, maxAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmin, ymax, zmax) + tAABB = finalTransform.TransformPoint(minAABB.x, maxAABB.y, maxAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmin, ymin, zmax) + tAABB = finalTransform.TransformPoint(minAABB.x, minAABB.y, maxAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmax, ymax, zmin) + tAABB = finalTransform.TransformPoint(maxAABB.x, maxAABB.y, minAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmin, ymax, zmin) + tAABB = finalTransform.TransformPoint(minAABB.x, maxAABB.y, minAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmax, ymax, zmax) + tAABB = finalTransform.TransformPoint(maxAABB.x, maxAABB.y, maxAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + // (xmin, ymin, zmax) + tAABB = finalTransform.TransformPoint(minAABB.x, minAABB.y, maxAABB.z); + tMinAABB = Vector3::Min(tAABB, tMinAABB); + tMaxAABB = Vector3::Max(tAABB, tMaxAABB); + + transformedMinAABB = tMinAABB; + transformedMaxAABB = tMaxAABB; + } + }; #pragma endregion #pragma region LIGHT diff --git a/project/src/Renderer.cpp b/project/src/Renderer.cpp index 4f9d047..5bf57a8 100644 --- a/project/src/Renderer.cpp +++ b/project/src/Renderer.cpp @@ -2,6 +2,8 @@ #include #include "SDL.h" #include "SDL_surface.h" +#include +#include //Project includes #include "Renderer.h" @@ -21,10 +23,10 @@ Renderer::Renderer(SDL_Window *pWindow) : m_pBufferPixels = static_cast(m_pBuffer->pixels); m_xvalues.reserve(m_Width); - for (uint16_t x{}; x < m_Width; ++x) { + for(uint16_t x{}; x < m_Width; ++x) + { m_xvalues.push_back(x); } - } void Renderer::Render(Scene *pScene) const { @@ -36,168 +38,17 @@ void Renderer::Render(Scene *pScene) const { float apsectRatio = static_cast(m_Width) / m_Height; float fov = tan((camera.fovAngle * (PI / 180.f)) / 2); - for (int px{}; px < m_Width; px += 1) { - for (int py{}; py < m_Height; py += 1) { - int actualPx = px; -// if (py % 2 != 0) { -// actualPx = px + 1; -// } - - float NDCx = ((2 * ((actualPx + 0.5) / m_Width) - 1) * apsectRatio) * fov; - float NDCy = (1 - 2 * ((py + 0.5) / m_Height)) * fov; - - - Vector3 rayDir = Vector3{NDCx, NDCy, 1}; - rayDir = CameraToWorld.TransformVector((rayDir)); - rayDir.Normalize(); - - Ray ray{camera.origin, rayDir}; - - - //set the color based on the ray direction - ColorRGB finalColor{0.f, 0.f, 0.f}; - - HitRecord closestHit{}; - pScene->GetClosestHit(ray, closestHit); - - - if (closestHit.didHit) { - for (const Light &light: lights) { - Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)}; - float rayMaxValue = lightDirection.Normalize(); - Ray lightRay = Ray{closestHit.origin + closestHit.normal * 0.01f, lightDirection, 0.0001f, - rayMaxValue}; - bool doesLightHit = pScene->DoesHit(lightRay); - - - if (m_CurrentLightingMode == LightingMode::Radiance) { - if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { - auto radiance = LightUtils::GetRadiance(light, closestHit.origin); - finalColor += radiance; - } - - - } else if (m_CurrentLightingMode == LightingMode::ObservedArea) { - if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { -// float observedArea = std::clamp( -// LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); -// finalColor.r += observedArea; -// finalColor.g += observedArea; -// finalColor.b += observedArea; - } - } else if (m_CurrentLightingMode == LightingMode::Combined) { - if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { - auto radiance = LightUtils::GetRadiance(light, closestHit.origin); - float observedArea = std::clamp( - LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); - - finalColor += radiance * observedArea * - materials[closestHit.materialIndex]->Shade(closestHit, lightDirection, - rayDir); - } - } else if (m_CurrentLightingMode == LightingMode::BRDF) { - if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { - auto radiance = LightUtils::GetRadiance(light, closestHit.origin); - float observedArea = std::clamp( - LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); - - finalColor += materials[closestHit.materialIndex]->Shade(closestHit, lightDirection, ray.direction); - } - } - - } - } - - -// if (m_CurrentLightingMode == LightingMode::ObservedArea) { -// if (closestHit.didHit) { -// for (const Light &light: lights) { -// Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)}; -// const float lightDistance = lightDirection.Normalize(); -// -// float law = std::clamp(LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); -//// finalColor += materials[closestHit.materialIndex]->Shade() * law; -// finalColor += ColorRGB{law, law, law}; -// -// } -// } -// } -// -// if (m_ShadowsEnabled) { -// -// if (closestHit.didHit) { -//// finalColor = materials[closestHit.materialIndex]->Shade(); -// if (m_ShadowsEnabled) { -// for (const Light &light: lights) { -// Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)}; -// const float lightDistance = lightDirection.Normalize(); -// -// const Ray lightRay{ -// closestHit.origin + closestHit.normal * 0.01f, -// lightDirection, 0.0001f, lightDistance -// }; -// -// if (pScene->DoesHit(lightRay)) { -// finalColor *= 0.5; -// continue; -// } -// } -// } -// } -// } - - finalColor.MaxToOne(); - - m_pBufferPixels[actualPx + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format, - static_cast(finalColor.r * 255), - static_cast(finalColor.g * 255), - static_cast(finalColor.b * 255)); - - } + uint32_t amountOfPixels = m_Width * m_Height; + std::vector pixelIndices{}; + pixelIndices.reserve(amountOfPixels); + for (uint32_t i{}; i < amountOfPixels; ++i) { + pixelIndices.push_back(i); } - // go over the pixels and avg the pixels i skipped -// for (int px2{1}; px2 < m_Width; px2 += 2) { -// for (int py{1}; py < m_Height; py += 1) { -// if (px2 == m_Width - 1 || py == m_Height - 1) continue; -// if (px2 == 0 || py == 0) continue; -// -// int px = px2; -// if (py % 2 != 0) { -// px = px2 + 1; -// } -// -// -// uint8_t r = 0; -// uint8_t g = 0; -// uint8_t b = 0; -// SDL_GetRGB(m_pBufferPixels[px + (py * m_Width)], m_pBuffer->format, &r, &g, &b); -// ColorRGB currentColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f}; -// -// SDL_GetRGB(m_pBufferPixels[(px - 1) + (py * m_Width)], m_pBuffer->format, &r, &g, &b); -// ColorRGB leftColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f}; -// -// SDL_GetRGB(m_pBufferPixels[(px + 1) + (py * m_Width)], m_pBuffer->format, &r, &g, &b); -// ColorRGB rightColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f}; -// -// SDL_GetRGB(m_pBufferPixels[px + ((py - 1) * m_Width)], m_pBuffer->format, &r, &g, &b); -// ColorRGB upColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f}; -// -// SDL_GetRGB(m_pBufferPixels[px + ((py + 1) * m_Width)], m_pBuffer->format, &r, &g, &b); -// ColorRGB downColor = ColorRGB{r / 255.f, g / 255.f, b / 255.f}; -// -// float avgR = (leftColor.r + rightColor.r + upColor.r + downColor.r) / 4; -// float avgG = (leftColor.g + rightColor.g + upColor.g + downColor.g) / 4; -// float avgB = (leftColor.b + rightColor.b + upColor.b + downColor.b) / 4; -// ColorRGB avgColor = ColorRGB{avgR, avgG, avgB}; -// -// m_pBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBuffer->format, -// static_cast(avgColor.r * 255), -// static_cast(avgColor.g * 255), -// static_cast(avgColor.b * 255)); -// } -// } + std::for_each(std::execution::par, pixelIndices.begin(), pixelIndices.end(), [&](uint32_t& pixelIndex) { + this->RenderPixel(pScene, pixelIndex, fov, apsectRatio, CameraToWorld, camera.origin); + }); //@END //Update SDL Surface @@ -266,3 +117,85 @@ void Renderer::CycleLightingMode() { } } + +void Renderer::RenderPixel(Scene *pScene, uint32_t pixelIndex, float fov, float aspectRatio, const Matrix cameraToWorld, + const Vector3 &cameraPosition) const { + + auto materials = pScene->GetMaterials(); + auto lights = pScene->GetLights(); + + const uint32_t px{ pixelIndex % m_Width}, py{ pixelIndex / m_Width }; + + float NDCx{px + 0.5f }, NDCy{py + 0.5f }; + float cx{(2.f * NDCx / m_Width - 1.f) * aspectRatio * fov }; + float cy{(1.f - 2.f * NDCy / m_Height) * fov }; + + Vector3 rayDir{ cx, cy, 1.f }; + rayDir = cameraToWorld.TransformVector((rayDir)); + rayDir.Normalize(); + + Ray ray{cameraPosition, rayDir}; + + + //set the color based on the ray direction + ColorRGB finalColor{0.f, 0.f, 0.f}; + + HitRecord closestHit{}; + pScene->GetClosestHit(ray, closestHit); + + + if (closestHit.didHit) { + for (const Light &light: lights) { + Vector3 lightDirection{LightUtils::GetDirectionToLight(light, closestHit.origin)}; + float rayMaxValue = lightDirection.Normalize(); + Ray lightRay = Ray{closestHit.origin + closestHit.normal * 0.01f, lightDirection, 0.0001f, + rayMaxValue}; + bool doesLightHit = pScene->DoesHit(lightRay); + + + if (m_CurrentLightingMode == LightingMode::Radiance) { + if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { + auto radiance = LightUtils::GetRadiance(light, closestHit.origin); + finalColor += radiance; + } + + + } else if (m_CurrentLightingMode == LightingMode::ObservedArea) { + if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { + float observedArea = std::clamp( + LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); + finalColor.r += observedArea; + finalColor.g += observedArea; + finalColor.b += observedArea; + } + } else if (m_CurrentLightingMode == LightingMode::Combined) { + if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { + auto radiance = LightUtils::GetRadiance(light, closestHit.origin); + float observedArea = std::clamp( + LightUtils::LambertCosineLaw(closestHit.normal, lightDirection), 0.0f, 1.0f); + + finalColor += radiance * observedArea * + materials[closestHit.materialIndex]->Shade(closestHit, lightDirection, + rayDir); + } + } else if (m_CurrentLightingMode == LightingMode::BRDF) { + if (!m_ShadowsEnabled or (m_ShadowsEnabled and !doesLightHit)) { + + finalColor += materials[closestHit.materialIndex]->Shade(closestHit, lightDirection, + ray.direction); + } + } + + } + } + + finalColor.MaxToOne(); + + m_pBufferPixels[pixelIndex] = SDL_MapRGB(m_pBuffer->format, + static_cast(finalColor.r * 255), + static_cast(finalColor.g * 255), + static_cast(finalColor.b * 255)); + + + +} diff --git a/project/src/Renderer.h b/project/src/Renderer.h index 8936b02..c63c037 100644 --- a/project/src/Renderer.h +++ b/project/src/Renderer.h @@ -23,6 +23,9 @@ namespace dae Renderer& operator=(Renderer&&) noexcept = delete; void Render(Scene* pScene) const; + + void RenderPixel(Scene* pScene, uint32_t pixelIndex, float fov, float aspectRatio, const Matrix cameraToWorld, const Vector3& cameraPosition) const; + bool SaveBufferToImage() const; void CycleLightingMode(); diff --git a/project/src/Scene.cpp b/project/src/Scene.cpp index f2e217a..268e911 100644 --- a/project/src/Scene.cpp +++ b/project/src/Scene.cpp @@ -25,7 +25,7 @@ namespace dae { } void dae::Scene::GetClosestHit(const Ray &ray, HitRecord &closestHit) const { - closestHit.t = FLT_MAX; + closestHit.t = std::numeric_limits::max(); for (const Plane &plane: m_PlaneGeometries) { HitRecord hitRecord{}; @@ -43,6 +43,21 @@ namespace dae { } } + for(const TriangleMesh& mesh : m_TriangleMeshGeometries) + { + HitRecord hit{}; + GeometryUtils::HitTest_TriangleMesh(mesh, ray, hit); + if(hit.t < closestHit.t) + closestHit = hit; + } + +// for(const Triangle& mesh : m_Triangles) +// { +// HitRecord hit{}; +// GeometryUtils::HitTest_Triangle(mesh, ray, hit); +// if(hit.t < closestHit.t) +// closestHit = hit; +// } } @@ -61,6 +76,18 @@ namespace dae { if(GeometryUtils::HitTest_Plane(plane, ray, hit, true)) return true; } + for(const TriangleMesh& mesh : m_TriangleMeshGeometries) + { + if(GeometryUtils::HitTest_TriangleMesh(mesh, ray, hit, true)) + return true; + } + +// for(const Triangle& mesh : m_Triangles) +// { +// if(GeometryUtils::HitTest_Triangle(mesh, ray, hit, true)) +// return true; +// } + return false; } @@ -214,4 +241,49 @@ namespace dae { AddPointLight(Vector3{ -2.5f, 5.f, -5.f }, 70.f, ColorRGB{ 1.f, .8f, .45f }); //Front Light Left AddPointLight(Vector3{ 2.5f, 2.5f, -5.f }, 50.f, ColorRGB{ .34f, .47f, .68f }); } + + void Scene_W4_TestScene::Initialize() { + m_Camera.origin = { 0.f, 1.f, -5.f }; + m_Camera.SetFOV(45.f); + + // Materials + const auto matLambert_GrayBlue = AddMaterial(new Material_Lambert({ 0.49f, 0.57f, 0.57f }, 1.f)); + const auto matLambert_White = AddMaterial(new Material_Lambert(colors::White, 1.f)); + + // Planes + AddPlane(Vector3{ 0.f, 0.f, 10.f }, Vector3{ 0.f, 0.f, -1.f }, matLambert_GrayBlue); // BACK + AddPlane(Vector3{ 0.f, 0.f, 0.f }, Vector3{ 0.f, 1.f, 0.f }, matLambert_GrayBlue); // BOTTOM + AddPlane(Vector3{ 0.f, 10.f, 0.f }, Vector3{ 0.f, -1.f, 0.f }, matLambert_GrayBlue); // TOP + AddPlane(Vector3{ 5.f, 0.f, 0.f }, Vector3{ -1.f, 0.f, 0.f }, matLambert_GrayBlue); // RIGHT + AddPlane(Vector3{ -5.f, 0.f, 0.f }, Vector3{ 1.f, 0.f, 0.f }, matLambert_GrayBlue); // LEFT + +// // Triangle (Temp) + auto triangle = Triangle{ { -.75f, .5f, 0.f }, { -.75f, 2.f, 0.f }, { .75f, .5f, 0.f } }; + triangle.cullMode = TriangleCullMode::NoCulling; + triangle.materialIndex = matLambert_White; + + m_Triangles.emplace_back(triangle); + +// + pMesh = AddTriangleMesh(TriangleCullMode::NoCulling, matLambert_White); + Utils::ParseOBJ("Resources/lowpoly_bunny.obj", pMesh->positions, pMesh->normals, pMesh->indices); +// pMesh->Scale({2.f, 2.f, 2.f}); +// pMesh->Scale({2.f, 2.f, 2.f}); + pMesh->RotateY(PI_DIV_2); + + pMesh->UpdateAABB(); + pMesh->UpdateTransforms(); + + // Light + AddPointLight(Vector3{ 0.f, 5.f, 5.f }, 50.f, ColorRGB{ 1.f, .61f, .45f }); // Backlight + AddPointLight(Vector3{ -2.5f, 5.f, -5.f }, 70.f, ColorRGB{ 1.f, .8f, .45f }); // Front Light Left + AddPointLight(Vector3{ 2.5f, 5.f, -5.f }, 50.f, ColorRGB{ .34f, .47f, .68f }); // Front Light Right + } + + void Scene_W4_TestScene::Update(Timer *timer) { + Scene::Update(timer); + +// pMesh->RotateY(PI_DIV_2 * timer->GetTotal()); +// pMesh->UpdateTransforms(); + } } diff --git a/project/src/Scene.h b/project/src/Scene.h index 1abb68a..b8a4171 100644 --- a/project/src/Scene.h +++ b/project/src/Scene.h @@ -51,6 +51,8 @@ namespace dae std::vector m_Lights{}; std::vector m_Materials{}; + std::vector m_Triangles{}; + Camera m_Camera{}; Sphere* AddSphere(const Vector3& origin, float radius, unsigned char materialIndex = 0); @@ -94,7 +96,6 @@ namespace dae void Initialize() override; }; - class Scene_W3 final : public Scene { public: @@ -108,4 +109,24 @@ namespace dae void Initialize() override; }; + + + class Scene_W4_TestScene final : public Scene + { + public: + Scene_W4_TestScene() = default; + ~Scene_W4_TestScene() override = default; + + Scene_W4_TestScene(const Scene_W4_TestScene&) = delete; + Scene_W4_TestScene(Scene_W4_TestScene&&) noexcept = delete; + Scene_W4_TestScene& operator=(const Scene_W4_TestScene&) = delete; + Scene_W4_TestScene& operator=(Scene_W4_TestScene&&) noexcept = delete; + + void Initialize() override; + void Update(Timer* timer) override; + + private: + TriangleMesh* pMesh{ nullptr }; + }; + } diff --git a/project/src/Utils.h b/project/src/Utils.h index 1a7b5dc..1faeaf0 100644 --- a/project/src/Utils.h +++ b/project/src/Utils.h @@ -1,24 +1,25 @@ #pragma once + #include +#include #include "Maths.h" #include "DataTypes.h" -namespace dae -{ - namespace GeometryUtils - { +namespace dae { + namespace GeometryUtils { #pragma region Sphere HitTest - //SPHERE HIT-TESTS - inline bool HitTest_Sphere(const Sphere& sphere, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false) - { + + //SPHERE HIT-TESTS + inline bool + HitTest_Sphere(const Sphere &sphere, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) { const float radius2 = sphere.radius * sphere.radius; const Vector3 CameraToOrigin = sphere.origin - ray.origin; const float tInSphere = Vector3::Dot(CameraToOrigin, ray.direction); - if(tInSphere < 0) return false; //Looking away from sphere + if (tInSphere < 0) return false; //Looking away from sphere float originToInsideDist2 = CameraToOrigin.SqrMagnitude() - tInSphere * tInSphere; - if(originToInsideDist2 > radius2) + if (originToInsideDist2 > radius2) return false; // 'inside' point is outside of sphere float tDiff = std::sqrt(radius2 - originToInsideDist2); @@ -27,7 +28,7 @@ namespace dae const bool hit = t > ray.min && t < ray.max; - if (!ignoreHitRecord && hit){ + if (!ignoreHitRecord && hit) { hitRecord.t = t; hitRecord.origin = ray.origin + ray.direction * t; hitRecord.normal = (hitRecord.origin - sphere.origin).Normalized(); @@ -38,27 +39,28 @@ namespace dae return hit; - } + } + + inline bool HitTest_Sphere(const Sphere &sphere, const Ray &ray) { + HitRecord temp{}; + return HitTest_Sphere(sphere, ray, temp, true); + } - inline bool HitTest_Sphere(const Sphere& sphere, const Ray& ray) - { - HitRecord temp{}; - return HitTest_Sphere(sphere, ray, temp, true); - } #pragma endregion #pragma region Plane HitTest - //PLANE HIT-TESTS - inline bool HitTest_Plane(const Plane& plane, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false) - { + + //PLANE HIT-TESTS + inline bool + HitTest_Plane(const Plane &plane, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) { float dot = Vector3::Dot(plane.normal, ray.direction); - if(dot < 0) { + if (dot < 0) { // float t = Vector3::Dot(plane.origin - ray.origin, plane.normal) / dot; float t = Vector3::Dot(plane.origin - ray.origin, plane.normal); t /= dot; const bool hit = t > ray.min && t < ray.max; - if(hit && !ignoreHitRecord){ + if (hit && !ignoreHitRecord) { hitRecord.t = t; hitRecord.origin = ray.origin + ray.direction * t; hitRecord.normal = plane.normal; @@ -70,142 +72,253 @@ namespace dae } return false; - } + } + + inline bool HitTest_Plane(const Plane &plane, const Ray &ray) { + HitRecord temp{}; + return HitTest_Plane(plane, ray, temp, true); + } - inline bool HitTest_Plane(const Plane& plane, const Ray& ray) - { - HitRecord temp{}; - return HitTest_Plane(plane, ray, temp, true); - } #pragma endregion #pragma region Triangle HitTest - //TRIANGLE HIT-TESTS - inline bool HitTest_Triangle(const Triangle& triangle, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false) - { - //todo W5 - throw std::runtime_error("Not Implemented Yet"); - return false; - } - inline bool HitTest_Triangle(const Triangle& triangle, const Ray& ray) - { - HitRecord temp{}; - return HitTest_Triangle(triangle, ray, temp, true); - } + //TRIANGLE HIT-TESTS + inline bool + HitTest_Triangle(const Triangle &triangle, const Ray &ray, HitRecord &hitRecord, bool ignoreHitRecord = false) { + const Vector3 &n = triangle.normal; + const float dot = Vector3::Dot(n, ray.direction); + + switch (triangle.cullMode) { + case TriangleCullMode::BackFaceCulling: { + if (ignoreHitRecord) { + if (dot >= FLT_EPSILON) { + return false; + } + } else { + if (dot <= FLT_EPSILON) { + return false; + } + } + break; + } + case TriangleCullMode::FrontFaceCulling: { + if (ignoreHitRecord) { + if (dot <= FLT_EPSILON) { + return false; + } + } else { + if (dot >= FLT_EPSILON) { + return false; + } + } + break; + } + case TriangleCullMode::NoCulling: { + if (dot == 0.f) + return false; + break; + } + } + + const Vector3 L = triangle.v0 - ray.origin; + const float t = Vector3::Dot(L, n) / dot; + + if ( + t < ray.min || t > ray + .max) + return false; + + const Vector3 hitPoint = ray.origin + ray.direction * t; + const Vector3 *verts[3] = {&triangle.v0, &triangle.v1, &triangle.v2}; + + for ( + int index{}; + index < 3; ++index) { + const Vector3 e = *verts[(index + 1) % 3] - *verts[index]; + const Vector3 p = hitPoint - *verts[index]; + + const Vector3 c = Vector3::Cross(e, p); + if ( + Vector3::Dot(Vector3::Cross(e, p), n + ) < 0) + return false; + } + + if (!ignoreHitRecord) { + hitRecord. + t = t; + hitRecord. + origin = hitPoint; + hitRecord. + normal = triangle.normal; + hitRecord. + materialIndex = triangle.materialIndex; + hitRecord. + didHit = true; + } + return true; + } + + inline bool HitTest_Triangle(const Triangle &triangle, const Ray &ray) { + HitRecord temp{}; + return HitTest_Triangle(triangle, ray, temp, true); + } + #pragma endregion #pragma region TriangeMesh HitTest - inline bool HitTest_TriangleMesh(const TriangleMesh& mesh, const Ray& ray, HitRecord& hitRecord, bool ignoreHitRecord = false) - { - //todo W5 - throw std::runtime_error("Not Implemented Yet"); - return false; - } - inline bool HitTest_TriangleMesh(const TriangleMesh& mesh, const Ray& ray) - { - HitRecord temp{}; - return HitTest_TriangleMesh(mesh, ray, temp, true); - } + inline bool SlabTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray) { + float tx1 = (mesh.transformedMinAABB.x - ray.origin.x) / ray.direction.x; + float tx2 = (mesh.transformedMaxAABB.x - ray.origin.x) / ray.direction.x; +// + float tmin = std::min(tx1, tx2); + float tmax = std::max(tx1, tx2); +// + float ty1 = (mesh.transformedMinAABB.y - ray.origin.y) / ray.direction.y; + float ty2 = (mesh.transformedMaxAABB.y - ray.origin.y) / ray.direction.y; +// + tmin = std::max(tmin, std::min(ty1, ty2)); + tmax = std::min(tmax, std::max(ty1, ty2)); + + float tz1 = (mesh.transformedMinAABB.z - ray.origin.z) / ray.direction.z; + float tz2 = (mesh.transformedMaxAABB.z - ray.origin.z) / ray.direction.z; + + tmin = std::max(tmin, std::min(tz1, tz2)); + tmax = std::min(tmax, std::max(tz1, tz2)); +// + return tmax > 0 && tmax >= tmin; + } + + inline bool HitTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray, HitRecord &hitRecord, + bool ignoreHitRecord = false) { + if (!SlabTest_TriangleMesh(mesh, ray)) + return false; + + constexpr int triSides{3}; + const size_t nTris{mesh.indices.size() / triSides}; + bool hit{false}; + + for (int i{}; i < nTris; ++i) { + const Vector3 &v0{mesh.transformedPositions[mesh.indices[i * triSides + 0]]}; + const Vector3 &v1{mesh.transformedPositions[mesh.indices[i * triSides + 1]]}; + const Vector3 &v2{mesh.transformedPositions[mesh.indices[i * triSides + 2]]}; + + const Vector3 norm{mesh.transformedNormals[i]}; + + Triangle tri{ + v0, v1, v2, norm + }; + + tri.cullMode = mesh.cullMode; + tri.materialIndex = mesh.materialIndex; + + HitRecord curHit; + if (HitTest_Triangle(tri, ray, curHit, ignoreHitRecord)) { + hit = true; + + if (curHit.t < hitRecord.t) { + hitRecord = curHit; + } + } + } + + return hit; + } + + inline bool HitTest_TriangleMesh(const TriangleMesh &mesh, const Ray &ray) { + HitRecord temp{}; + return HitTest_TriangleMesh(mesh, ray, temp, true); + } + + #pragma endregion - } + } - namespace LightUtils - { - //Direction from target to light - inline Vector3 GetDirectionToLight(const Light& light, const Vector3 origin) - { - return { light.origin - origin }; - } + namespace LightUtils { + //Direction from target to light + inline Vector3 GetDirectionToLight(const Light &light, const Vector3 origin) { + return {light.origin - origin}; + } - inline ColorRGB GetRadiance(const Light& light, const Vector3& target) - { - switch(light.type) - { + inline ColorRGB GetRadiance(const Light &light, const Vector3 &target) { + switch (light.type) { case LightType::Point: return light.color * light.intensity / (light.origin - target).SqrMagnitude(); case LightType::Directional: return light.color * light.intensity; } - } + } - inline float LambertCosineLaw(const Vector3& normal, const Vector3& lightDirection) - { + inline float LambertCosineLaw(const Vector3 &normal, const Vector3 &lightDirection) { return Vector3::Dot(normal, lightDirection); } - } + } - namespace Utils - { - //Just parses vertices and indices + namespace Utils { + //Just parses vertices and indices #pragma warning(push) #pragma warning(disable : 4505) //Warning unreferenced local function - static bool ParseOBJ(const std::string& filename, std::vector& positions, std::vector& normals, std::vector& indices) - { - std::ifstream file(filename); - if (!file) - return false; - std::string sCommand; - // start a while iteration ending when the end of file is reached (ios::eof) - while (!file.eof()) - { - //read the first word of the string, use the >> operator (istream::operator>>) - file >> sCommand; - //use conditional statements to process the different commands - if (sCommand == "#") - { - // Ignore Comment - } - else if (sCommand == "v") - { - //Vertex - float x, y, z; - file >> x >> y >> z; - positions.push_back({ x, y, z }); - } - else if (sCommand == "f") - { - float i0, i1, i2; - file >> i0 >> i1 >> i2; + static bool + ParseOBJ(const std::string &filename, std::vector &positions, std::vector &normals, + std::vector &indices) { + std::ifstream file(filename); + if (!file) + return false; - indices.push_back((int)i0 - 1); - indices.push_back((int)i1 - 1); - indices.push_back((int)i2 - 1); - } - //read till end of line and ignore all remaining chars - file.ignore(1000, '\n'); + std::string sCommand; + // start a while iteration ending when the end of file is reached (ios::eof) + while (!file.eof()) { + //read the first word of the string, use the >> operator (istream::operator>>) + file >> sCommand; + //use conditional statements to process the different commands + if (sCommand == "#") { + // Ignore Comment + } else if (sCommand == "v") { + //Vertex + float x, y, z; + file >> x >> y >> z; + positions.push_back({x, y, z}); + } else if (sCommand == "f") { + float i0, i1, i2; + file >> i0 >> i1 >> i2; - if (file.eof()) - break; - } + indices.push_back((int) i0 - 1); + indices.push_back((int) i1 - 1); + indices.push_back((int) i2 - 1); + } + //read till end of line and ignore all remaining chars + file.ignore(1000, '\n'); - //Precompute normals - for (uint64_t index = 0; index < indices.size(); index += 3) - { - uint32_t i0 = indices[index]; - uint32_t i1 = indices[index + 1]; - uint32_t i2 = indices[index + 2]; + if (file.eof()) + break; + } - Vector3 edgeV0V1 = positions[i1] - positions[i0]; - Vector3 edgeV0V2 = positions[i2] - positions[i0]; - Vector3 normal = Vector3::Cross(edgeV0V1, edgeV0V2); + //Precompute normals + for (uint64_t index = 0; index < indices.size(); index += 3) { + uint32_t i0 = indices[index]; + uint32_t i1 = indices[index + 1]; + uint32_t i2 = indices[index + 2]; - if (std::isnan(normal.x)) - { - int k = 0; - } + Vector3 edgeV0V1 = positions[i1] - positions[i0]; + Vector3 edgeV0V2 = positions[i2] - positions[i0]; + Vector3 normal = Vector3::Cross(edgeV0V1, edgeV0V2); - normal.Normalize(); - if (std::isnan(normal.x)) - { - int k = 0; - } + if (std::isnan(normal.x)) { + int k = 0; + } - normals.push_back(normal); - } + normal.Normalize(); + if (std::isnan(normal.x)) { + int k = 0; + } + + normals.push_back(normal); + } + + return true; + } - return true; - } #pragma warning(pop) - } + } } \ No newline at end of file diff --git a/project/src/Vector3.cpp b/project/src/Vector3.cpp index f7f4dcf..ae4ca4c 100644 --- a/project/src/Vector3.cpp +++ b/project/src/Vector3.cpp @@ -162,5 +162,14 @@ namespace dae { { return AreEqual(x, v.x) && AreEqual(y, v.y) && AreEqual(z, v.z); } + + Vector3 Vector3::Max(const Vector3 &v1, const Vector3 &v2) { + return { std::max(v1.x, v2.x), std::max(v1.y, v2.y), std::max(v1.z, v2.z) }; + } + + Vector3 Vector3::Min(const Vector3 &v1, const Vector3 &v2) { + return { std::min(v1.x, v2.x), std::min(v1.y, v2.y), std::min(v1.z, v2.z) }; + } + #pragma endregion } \ No newline at end of file diff --git a/project/src/Vector3.h b/project/src/Vector3.h index 63dc426..c452fdb 100644 --- a/project/src/Vector3.h +++ b/project/src/Vector3.h @@ -26,6 +26,9 @@ namespace dae static Vector3 Reflect(const Vector3& v1, const Vector3& v2); static Vector3 Lico(float f1, const Vector3& v1, float f2, const Vector3& v2, float f3, const Vector3& v3); + static Vector3 Max(const Vector3& v1, const Vector3& v2); + static Vector3 Min(const Vector3& v1, const Vector3& v2); + Vector4 ToPoint4() const; Vector4 ToVector4() const; diff --git a/project/src/main.cpp b/project/src/main.cpp index e560d46..0a5e144 100644 --- a/project/src/main.cpp +++ b/project/src/main.cpp @@ -47,7 +47,7 @@ int main(int argc, char* args[]) const auto pTimer = new Timer(); const auto pRenderer = new Renderer(pWindow); - const auto pScene = new Scene_W3(); + const auto pScene = new Scene_W4_TestScene(); pScene->Initialize(); //Start loop