Final Commit for now

This adds model support, AABB, and other optimisations
This commit is contained in:
2024-11-09 21:03:54 +01:00
parent 18845abe1c
commit a83ecb32bb
10 changed files with 558 additions and 302 deletions

10
.gitignore vendored
View File

@@ -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/**
cmake-build-debug/**
cmake-build-default/**
cmake-build-minsizerel/**
cmake-build-relwithdebinfo/**
cmake-build-release/**

View File

@@ -85,9 +85,17 @@ namespace dae
Matrix translationTransform{};
Matrix scaleTransform{};
Vector3 minAABB{};
Vector3 maxAABB{};
Vector3 transformedMinAABB{};
Vector3 transformedMaxAABB{};
std::vector<Vector3> transformedPositions{};
std::vector<Vector3> 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

View File

@@ -2,6 +2,8 @@
#include <iostream>
#include "SDL.h"
#include "SDL_surface.h"
#include <algorithm>
#include <execution>
//Project includes
#include "Renderer.h"
@@ -21,10 +23,10 @@ Renderer::Renderer(SDL_Window *pWindow) :
m_pBufferPixels = static_cast<uint32_t *>(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<float>(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<uint8_t>(finalColor.r * 255),
static_cast<uint8_t>(finalColor.g * 255),
static_cast<uint8_t>(finalColor.b * 255));
}
uint32_t amountOfPixels = m_Width * m_Height;
std::vector<uint32_t> 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<uint8_t>(avgColor.r * 255),
// static_cast<uint8_t>(avgColor.g * 255),
// static_cast<uint8_t>(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<uint8_t>(finalColor.r * 255),
static_cast<uint8_t>(finalColor.g * 255),
static_cast<uint8_t>(finalColor.b * 255));
}

View File

@@ -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();

View File

@@ -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<float>::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();
}
}

View File

@@ -51,6 +51,8 @@ namespace dae
std::vector<Light> m_Lights{};
std::vector<Material*> m_Materials{};
std::vector<Triangle> 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 };
};
}

View File

@@ -1,24 +1,25 @@
#pragma once
#include <fstream>
#include <memory>
#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<Vector3>& positions, std::vector<Vector3>& normals, std::vector<int>& 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<Vector3> &positions, std::vector<Vector3> &normals,
std::vector<int> &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)
}
}
}

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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