This commit is contained in:
2024-11-11 01:41:07 +01:00
parent 2f55e5b8d6
commit 8f8605b76f
8 changed files with 317 additions and 112 deletions

View File

@@ -8,6 +8,7 @@ set(SOURCES
"src/Vector2.cpp" "src/Vector2.cpp"
"src/Vector3.cpp" "src/Vector3.cpp"
"src/Vector4.cpp" "src/Vector4.cpp"
"src/HitTest.cpp"
) )
set(IMGUI_SOURCES set(IMGUI_SOURCES

View File

@@ -3,6 +3,7 @@
namespace dae namespace dae
{ {
struct ColorRGB struct ColorRGB
{ {
float r{}; float r{};

View File

@@ -1,4 +1,6 @@
#pragma once #pragma once
#include <cstdint>
#include "Maths.h" #include "Maths.h"
#include "vector" #include "vector"
@@ -8,7 +10,7 @@ namespace dae
{ {
Vector3 position{}; Vector3 position{};
ColorRGB color{colors::White}; ColorRGB color{colors::White};
//Vector2 uv{}; //W2 Vector2 uv{}; //W2
//Vector3 normal{}; //W4 //Vector3 normal{}; //W4
//Vector3 tangent{}; //W4 //Vector3 tangent{}; //W4
//Vector3 viewDirection{}; //W4 //Vector3 viewDirection{}; //W4
@@ -18,7 +20,7 @@ namespace dae
{ {
Vector4 position{}; Vector4 position{};
ColorRGB color{ colors::White }; ColorRGB color{ colors::White };
//Vector2 uv{}; Vector2 uv{};
//Vector3 normal{}; //Vector3 normal{};
//Vector3 tangent{}; //Vector3 tangent{};
//Vector3 viewDirection{}; //Vector3 viewDirection{};

78
project/src/HitTest.cpp Normal file
View File

@@ -0,0 +1,78 @@
//
// Created by Bram on 10/11/2024.
//
#include <cassert>
#include "HitTest.h"
float min(float a, float b, float c) {
return std::min(a, std::min(b, c));
}
float max(float a, float b, float c) {
return std::max(a, std::max(b, c));
}
float CrossZ(const Vector3& p0, const Vector3& p1, const Vector3& point)
{
return (point.x - p0.x) * (p1.y - p0.y)
- (point.y - p0.y) * (p1.x - p0.x);
}
HitTest::HitTestSample HitTest::Triangle(const Vector3 &pos, const std::vector<Vertex> &vertices) {
float minX = min(vertices[0].position.x, vertices[1].position.x, vertices[2].position.x);
float maxX = max(vertices[0].position.x, vertices[1].position.x, vertices[2].position.x);
float minY = min(vertices[0].position.y, vertices[1].position.y, vertices[2].position.y);
float maxY = max(vertices[0].position.y, vertices[1].position.y, vertices[2].position.y);
if (pos.x < minX || pos.x > maxX || pos.y < minY || pos.y > maxY) {
return HitTestSample{ColorRGB{1, 0, 0}, 0};
}
// first edge
double w0{ CrossZ(vertices[1].position, vertices[2].position, pos) };
const bool inEdge0 = w0 <= 0;
double w1{ CrossZ(vertices[2].position, vertices[0].position, pos) };
const bool inEdge1 = w1 <= 0;
double w2{ CrossZ(vertices[0].position, vertices[1].position, pos) };
const bool inEdge2 = w2 <= 0;
if(!inEdge0 || !inEdge1 || !inEdge2){
return {ColorRGB{0, 0, 1}, 0};
}
const double totalWeight{ w0 + w1 + w2 };
w0 /= totalWeight;
w1 /= totalWeight;
w2 /= totalWeight;
assert(std::abs(w0 + w1 + w2 - 1) < 0.0001);
float v0z = vertices[0].position.z;
float v1z = vertices[1].position.z;
float v2z = vertices[2].position.z;
float depth = w0 * v0z + w1 * v1z + w2 * v2z;
if(depth < 0 || depth > 1) {
return HitTestSample{ColorRGB{0, 0, 0}, 0};
}
Vector4 color{};
color += Vector4(float(w0) * Vector3(vertices[0].color.r, vertices[0].color.g, vertices[0].color.b) , 0);
color += Vector4(float(w1) * Vector3(vertices[1].color.r, vertices[1].color.g, vertices[1].color.b) , 0);
color += Vector4(float(w2) * Vector3(vertices[2].color.r, vertices[2].color.g, vertices[2].color.b) , 0);
return HitTestSample{ColorRGB{
static_cast<float>(color.x),
static_cast<float>(color.y),
static_cast<float>(color.z)
}, depth};
}

22
project/src/HitTest.h Normal file
View File

@@ -0,0 +1,22 @@
//
// Created by Bram on 10/11/2024.
//
#ifndef GP1_RASTERIZER_HITTEST_H
#define GP1_RASTERIZER_HITTEST_H
#include "DataTypes.h"
#include "Maths.h"
using namespace dae;
namespace HitTest {
struct HitTestSample{
ColorRGB color{0, 1, 1};
float depth{0};
};
HitTestSample Triangle(const Vector3& pos, const std::vector<Vertex>& vertices);
}
#endif //GP1_RASTERIZER_HITTEST_H

View File

@@ -1,9 +1,11 @@
//External includes //External includes
#include <iostream>
#include "SDL_surface.h" #include "SDL_surface.h"
//Project includes //Project includes
#include "Renderer.h" #include "Renderer.h"
#include "Maths.h" #include "Maths.h"
#include "HitTest.h"
using namespace dae; using namespace dae;
@@ -24,6 +26,8 @@ Renderer::Renderer(SDL_Window *pWindow) : m_pWindow(pWindow) {
//Initialize Camera //Initialize Camera
m_Camera.Initialize(60.f, {.0f, .0f, -10.f}); m_Camera.Initialize(60.f, {.0f, .0f, -10.f});
m_aspectRatio = static_cast<float>(m_Width) / static_cast<float>(m_Height); m_aspectRatio = static_cast<float>(m_Width) / static_cast<float>(m_Height);
m_pTexture = Texture::LoadFromFile("./Resources/uv_grid_2.png");
} }
Renderer::~Renderer() { Renderer::~Renderer() {
@@ -50,91 +54,154 @@ void Renderer::Render() {
constexpr int numVerticies = 3; constexpr int numVerticies = 3;
std::vector<Vertex> verticiesScreenSpace{}; std::vector<Vertex> verticiesScreenSpace{};
VertexTransformationFunction(m_verticiesWorld, verticiesScreenSpace); // VertexTransformationFunction(m_verticiesWorld, verticiesScreenSpace);
const int numTriangles{static_cast<int>(m_verticiesWorld.size()) / numVerticies};
for (const Mesh &currentMesh: m_meshesWorldStrip) {
VertexTransformationFunction(currentMesh.vertices, verticiesScreenSpace);
int numTriangles{};
switch (currentMesh.primitiveTopology) {
case PrimitiveTopology::TriangleList:
numTriangles = static_cast<int>(currentMesh.indices.size()) / numVerticies;
break;
case PrimitiveTopology::TriangleStrip:
numTriangles = static_cast<int>(currentMesh.indices.size()) - 2;
break;
}
for (int triangleIndex{}; triangleIndex < numTriangles; ++triangleIndex) { for (int triangleIndex{}; triangleIndex < numTriangles; ++triangleIndex) {
const Vector3 &vertexPos0{verticiesScreenSpace[triangleIndex * numVerticies].position}; Vertex vertex0, vertex1, vertex2;
const Vector3 &vertexPos1{verticiesScreenSpace[triangleIndex * numVerticies + 1].position};
const Vector3 &vertexPos2{verticiesScreenSpace[triangleIndex * numVerticies + 2].position};
const float minX{std::min(vertexPos0.x, std::min(vertexPos1.x, vertexPos2.x))}; switch (currentMesh.primitiveTopology) {
const float minY{std::min(vertexPos0.y, std::min(vertexPos1.y, vertexPos2.y))}; case PrimitiveTopology::TriangleList:
vertex0 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 0]];
vertex1 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 1]];
vertex2 = verticiesScreenSpace[currentMesh.indices[triangleIndex * numVerticies + 2]];
break;
case PrimitiveTopology::TriangleStrip:
vertex0 = verticiesScreenSpace[currentMesh.indices[triangleIndex + 0]];
vertex1 = verticiesScreenSpace[currentMesh.indices[triangleIndex + 1]];
vertex2 = verticiesScreenSpace[currentMesh.indices[triangleIndex + 2]];
const float maxX{std::max(vertexPos0.x, std::max(vertexPos1.x, vertexPos2.x))}; if (triangleIndex % 2 == 1) {
const float maxY{std::max(vertexPos0.y, std::max(vertexPos1.y, vertexPos2.y))}; std::swap(vertex1, vertex2);
}
const int indexOffset{triangleIndex * numVerticies}; if (vertex0.position == vertex1.position ||
vertex0.position == vertex2.position ||
vertex1.position == vertex2.position) {
continue;
}
break;
}
const Vector3 side1{ const float minX{std::min(vertex0.position.x, std::min(vertex1.position.x, vertex2.position.x))};
verticiesScreenSpace[indexOffset + 1].position - verticiesScreenSpace[indexOffset].position}; const float minY{std::min(vertex0.position.y, std::min(vertex1.position.y, vertex2.position.y))};
const Vector3 side2{
verticiesScreenSpace[indexOffset + 2].position - verticiesScreenSpace[indexOffset].position};
const float area{0.5f * Vector3::Cross(side1, side2).z}; const float maxX{std::max(vertex0.position.x, std::max(vertex1.position.x, vertex2.position.x))};
const float maxY{std::max(vertex0.position.y, std::max(vertex1.position.y, vertex2.position.y))};
const int startX = std::max(0, static_cast<int>(minX));
const int endX = std::min(m_Width - 1, static_cast<int>(maxX));
const int startY = std::max(0, static_cast<int>(minY)); const Vector3 side1{vertex1.position - vertex0.position};
const int endY = std::min(m_Height - 1, static_cast<int>(maxY)); const Vector3 side2{vertex2.position - vertex0.position};
const float totalArea{Vector3::Cross(side1, side2).z};
const int startX = std::max(0, static_cast<int>(minX)) - 1;
const int endX = std::min(m_Width - 1, static_cast<int>(maxX)) + 1;
const int startY = std::max(0, static_cast<int>(minY)) - 1;
const int endY = std::min(m_Height - 1, static_cast<int>(maxY)) + 1;
for (int px{startX}; px < endX; ++px) { for (int px{startX}; px < endX; ++px) {
for (int py{startY}; py < endY; ++py) { for (int py{startY}; py < endY; ++py) {
Vector3 P{px + 0.5f, py + 0.5f, 1}; if (px < 0 || px >= m_Width || py < 0 || py >= m_Height) {
//TEMP fix for out of bounds
bool inTriangle{true}; //This is to remove triangles having an incorrect edge on their bounding box
float currDepth{0.0f}; continue;
ColorRGB pointColor{};
for (int vertexIndex{0}; vertexIndex < numVerticies; ++vertexIndex) {
const Vertex &nextVert{verticiesScreenSpace[(vertexIndex + 1) % numVerticies + indexOffset]};
const Vector3 &currentVecPos{verticiesScreenSpace[vertexIndex + indexOffset].position};
const Vector3 eVec{nextVert.position - currentVecPos};
const Vector3 pVec{P - currentVecPos};
const float crossResult{Vector3::Cross(eVec, pVec).z};
if (crossResult < 0) {
inTriangle = false;
break;
} }
currDepth += Vector3 P{static_cast<float>(px) + 0.5f, static_cast<float>(py) + 0.5f, 1.f};
verticiesScreenSpace[((vertexIndex + 2) % numVerticies + indexOffset)].position.z *
((crossResult * 0.5f) / area);
pointColor += verticiesScreenSpace[((vertexIndex + 2) % numVerticies + indexOffset)].color *
((crossResult * 0.5f) / area);
}
if (!inTriangle) { const Vector3 cross0{Vector3::Cross(vertex2.position - vertex1.position, P - vertex1.position)};
if (cross0.z < 0) {
continue;
}
const Vector3 cross1{Vector3::Cross(vertex0.position - vertex2.position, P - vertex2.position)};
if (cross1.z < 0) {
continue;
}
const Vector3 cross2{Vector3::Cross(vertex1.position - vertex0.position, P - vertex0.position)};
if (cross2.z < 0) {
continue; continue;
} }
const int depthBufferIndex{px + (py * m_Width)}; const int depthBufferIndex{px + (py * m_Width)};
if (m_pDepthBufferPixels[depthBufferIndex] >= currDepth) { float totalWeight{cross0.z + cross1.z + cross2.z};
m_pDepthBufferPixels[depthBufferIndex] = currDepth; Vector3 weights{
cross0.z / totalWeight,
cross1.z / totalWeight,
cross2.z / totalWeight,
};
finalColor = pointColor; const float currentDepth =
1 / (weights.x / vertex0.position.z +
weights.y / vertex1.position.z +
weights.z / vertex2.position.z);
const Vector2 UvCoords =
vertex0.uv * currentDepth * weights.x / vertex0.position.z +
vertex1.uv * currentDepth * weights.y / vertex1.position.z +
vertex2.uv * currentDepth * weights.z / vertex2.position.z;
if (m_pDepthBufferPixels[depthBufferIndex] >= currentDepth) {
m_pDepthBufferPixels[depthBufferIndex] = currentDepth;
//Update Color in Buffer
finalColor = m_pTexture->Sample(UvCoords);
finalColor.MaxToOne(); finalColor.MaxToOne();
m_pBackBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBackBuffer->format, m_pBackBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
static_cast<uint8_t>(finalColor.r * 255), static_cast<uint8_t>(finalColor.r * 255),
static_cast<uint8_t>(finalColor.g * 255), static_cast<uint8_t>(finalColor.g * 255),
static_cast<uint8_t>(finalColor.b * 255)); static_cast<uint8_t>(finalColor.b * 255));
}
} }
} }
} }
} }
//RENDER LOGIC int x, y{0};
//For loop over all the pixels
for (int px{}; px < 100; ++px) {
for (int py{}; py < 100; ++py) {
//Get the pixel position
x = px;
y = py;
ColorRGB test = m_pTexture->Sample(Vector2{static_cast<float>(px) / 100, static_cast<float>(py) / 100});
m_pBackBufferPixels[x + (y * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
static_cast<uint8_t>(test.r * 255),
static_cast<uint8_t>(test.g * 255),
static_cast<uint8_t>(test.b * 255));
}
}
//RENDER LOGIC
SDL_UnlockSurface(m_pBackBuffer); SDL_UnlockSurface(m_pBackBuffer);
SDL_BlitSurface(m_pBackBuffer, nullptr, m_pFrontBuffer, nullptr); SDL_BlitSurface(m_pBackBuffer,
nullptr, m_pFrontBuffer, nullptr);
SDL_UpdateWindowSurface(m_pWindow); SDL_UpdateWindowSurface(m_pWindow);
} }
@@ -148,7 +215,7 @@ void Renderer::VertexTransformationFunction(const std::vector<Vertex> &vertices_
vertPos.x = (vertPos.x + 1) / 2 * static_cast<float>(m_Width); vertPos.x = (vertPos.x + 1) / 2 * static_cast<float>(m_Width);
vertPos.y = (1 - vertPos.y) / 2 * static_cast<float>(m_Height); vertPos.y = (1 - vertPos.y) / 2 * static_cast<float>(m_Height);
vertices_out.push_back(Vertex{vertPos, vert.color}); vertices_out.push_back(Vertex{vertPos, vert.color, vert.uv});
} }
} }

View File

@@ -5,12 +5,13 @@
#include "Camera.h" #include "Camera.h"
#include "DataTypes.h" #include "DataTypes.h"
#include "Maths.h"
#include "Texture.h"
struct SDL_Window; struct SDL_Window;
struct SDL_Surface; struct SDL_Surface;
namespace dae { namespace dae {
class Texture;
struct Mesh; struct Mesh;
@@ -55,32 +56,34 @@ namespace dae {
float m_aspectRatio{}; float m_aspectRatio{};
std::vector<Vertex> m_verticiesWorld{ Texture* m_pTexture{ nullptr };
//Triangle 0 //
{{0.f,2.f,0.f}, {1,0,0}}, // std::vector<Vertex> m_verticiesWorld{
{{1.5f,-1.f,0.f}, {1,0,0}}, // //Triangle 0
{{-1.5f,-1.f,0.f}, {1,0,0}}, // {{0.f,2.f,0.f}, {1,0,0}},
// {{1.5f,-1.f,0.f}, {1,0,0}},
//Triangle 1 // {{-1.5f,-1.f,0.f}, {1,0,0}},
{{0.f,4.f,2.f}, {1,0,0}}, //
{{3.f,-2.f,2.f}, {0,1,0}}, // //Triangle 1
{{-3.f,-2.f,2.f}, {0,0,1}} // {{0.f,4.f,2.f}, {1,0,0}},
}; // {{3.f,-2.f,2.f}, {0,1,0}},
// {{-3.f,-2.f,2.f}, {0,0,1}}
// };
std::vector<Mesh> m_meshesWorldList std::vector<Mesh> m_meshesWorldList
{ {
Mesh Mesh
{ {
{ {
Vertex{ {-3, 3, -2 }, { 1, 1, 1 }, }, Vertex{ {-3, 3, -2 }, { 1, 1, 1 }, { 0.0f, 0.0f } },
Vertex{ { 0, 3, -2 }, { 1, 1, 1 }, }, Vertex{ { 0, 3, -2 }, { 1, 1, 1 }, { 0.5f, 0.0f } },
Vertex{ { 3, 3, -2 }, { 1, 1, 1 }, }, Vertex{ { 3, 3, -2 }, { 1, 1, 1 }, { 1.0f, 0.0f } },
Vertex{ {-3, 0, -2 }, { 1, 1, 1 }, }, Vertex{ {-3, 0, -2 }, { 1, 1, 1 }, { 0.0f, 0.5f } },
Vertex{ { 0, 0, -2 }, { 1, 1, 1 }, }, Vertex{ { 0, 0, -2 }, { 1, 1, 1 }, { 0.5f, 0.5f } },
Vertex{ { 3, 0, -2 }, { 1, 1, 1 }, }, Vertex{ { 3, 0, -2 }, { 1, 1, 1 }, { 1.0f, 0.5f } },
Vertex{ {-3, -3, -2 }, { 1, 1, 1 }, }, Vertex{ {-3, -3, -2 }, { 1, 1, 1 }, { 0.0f, 1.0f } },
Vertex{ { 0, -3, -2 }, { 1, 1, 1 }, }, Vertex{ { 0, -3, -2 }, { 1, 1, 1 }, { 0.5f, 1.0f } },
Vertex{ { 3, -3, -2 }, { 1, 1, 1 }, } Vertex{ { 3, -3, -2 }, { 1, 1, 1 }, { 1.0f, 1.0f } }
}, },
{ {
3, 0, 1, 1, 4, 3, 4, 1 ,2, 3, 0, 1, 1, 4, 3, 4, 1 ,2,
@@ -95,27 +98,41 @@ namespace dae {
{ {
Mesh Mesh
{ {
{ std::vector<Vertex>{
Vertex{ {-3, 3, -2 }, { 1, 1, 1 }, }, {{-3, 3, -2}, {colors::White}, {0,0} },
Vertex{ { 0, 3, -2 }, { 1, 1, 1 }, }, {{0, 3, -2}, {colors::Red}, {0.5,0}},
Vertex{ { 3, 3, -2 }, { 1, 1, 1 }, }, {{3, 3, -2}, {colors::Blue}, {1,0}},
Vertex{ {-3, 0, -2 }, { 1, 1, 1 }, }, {{-3, 0, -2}, {colors::Red}, {0,0.5}},
Vertex{ { 0, 0, -2 }, { 1, 1, 1 }, }, {{0, 0, -2}, {colors::Yellow}, {0.5, 0.5}},
Vertex{ { 3, 0, -2 }, { 1, 1, 1 }, }, {{3, 0, -2}, {colors::White}, {1, 0.5}},
Vertex{ {-3, -3, -2 }, { 1, 1, 1 }, }, {{-3, -3, -2}, {colors::White}, {0,1}},
Vertex{ { 0, -3, -2 }, { 1, 1, 1 }, }, {{0, -3, -2}, {colors::White}, {0.5,1}},
Vertex{ { 3, -3, -2 }, { 1, 1, 1 }, } {{3, -3, -2}, {colors::White}, {1,1}},
}, },
{ std::vector<uint32_t>{
3, 0, 4, 1, 5, 2, 3, 0, 4, 1, 5, 2,
2, 6, 2, 6,
6, 3, 7, 4, 8, 5 6, 3, 7, 4, 8, 5,
}, },
PrimitiveTopology::TriangleStrip PrimitiveTopology::TriangleStrip,
} }
}; };
//square
std::vector<Vertex> m_verticies_screenSpace{}; std::vector<Mesh> testMesh{
Mesh{
std::vector<Vertex>{
{{-1, 1, 0}, {colors::White}, {0, 0}},
{{1, 1, 0}, {colors::White}, {1, 0}},
{{-1, -1, 0}, {colors::White}, {0, 1}},
{{1, -1, 0}, {colors::White}, {1, 1}},
},
std::vector<uint32_t>{
0, 1, 2, 1, 3, 2
},
PrimitiveTopology::TriangleList
}
};
// std::vector<Vertex> m_verticies_screenSpace{};
float* m_pDepthBufferPixels{}; float* m_pDepthBufferPixels{};

View File

@@ -1,6 +1,8 @@
#include "Texture.h" #include "Texture.h"
#include "Vector2.h" #include "Vector2.h"
#include <SDL_image.h> #include <SDL_image.h>
#include <iostream>
#include <algorithm>
namespace dae namespace dae
{ {
@@ -21,18 +23,33 @@ namespace dae
Texture* Texture::LoadFromFile(const std::string& path) Texture* Texture::LoadFromFile(const std::string& path)
{ {
//TODO SDL_Surface* pSurface = IMG_Load(path.c_str());
//Load SDL_Surface using IMG_LOAD if (pSurface == nullptr)
//Create & Return a new Texture Object (using SDL_Surface) {
std::cerr << "Texture::LoadFromFile > Failed to load texture: " << path << " Error: " << IMG_GetError() << std::endl;
throw std::runtime_error("Failed to load texture");
}
return nullptr; if (pSurface->format->BytesPerPixel != 4)
{
std::cerr << "Texture::LoadFromFile > Texture is not in the right format: " << path << std::endl;
throw std::runtime_error("Texture is not in the right format");
}
return new Texture(pSurface);
} }
ColorRGB Texture::Sample(const Vector2& uv) const ColorRGB Texture::Sample(const Vector2& uv) const
{ {
//TODO Uint8 red, green, blue;
//Sample the correct texel for the given uv Vector2 clamped = {std::clamp(uv.x, 0.0f, 1.0f), std::clamp(uv.y, 0.0f, 1.0f)};
return {}; SDL_GetRGB(m_pSurfacePixels[
static_cast<int>(clamped.x * static_cast<float>(m_pSurface->w - 1)) +
static_cast<int>(clamped.y * static_cast<float>(m_pSurface->h - 1)) * (m_pSurface->w)
], m_pSurface->format, &red, &green, &blue);
return ColorRGB{static_cast<float>(red)/255.0f, static_cast<float>(green)/255.0f, static_cast<float>(blue)/255.0f};
} }
} }