Last week of rasterizer

:p silly cats FTW
This commit is contained in:
2024-12-02 03:47:25 +01:00
parent 8f8605b76f
commit 98f3fc50cd
13 changed files with 636 additions and 475 deletions

View File

@@ -21,8 +21,6 @@ set(IMGUI_SOURCES
"imgui/backends/imgui_impl_sdlrenderer2.cpp"
)
# Create the executable
add_executable(${PROJECT_NAME} ${SOURCES})
#add_executable(${PROJECT_NAME} ${SOURCES} ${IMGUI_SOURCES})

BIN
project/resources/cat.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@@ -40,9 +40,10 @@ namespace dae {
float zNear{.1f};
float zFar{100.f};
void Initialize(float _fovAngle = 90.f, Vector3 _origin = {0.f, 0.f, 0.f}) {
void Initialize(float _fovAngle = 90.f, Vector3 _origin = {0.f, 0.f, 0.f}, float _aspect = 1.f) {
fovAngle = _fovAngle;
fov = tanf((fovAngle * TO_RADIANS) / 2.f);
aspect = _aspect;
origin = _origin;
}
@@ -54,15 +55,14 @@ namespace dae {
void CalculateViewMatrix() {
viewMatrix = Matrix::CreateLookAtLH(origin, forward, up);
invViewMatrix = Matrix::Inverse(viewMatrix);
viewMatrix = Matrix::CreateLookAtLH(origin, origin + forward, Vector3::UnitY);
Matrix temp = viewMatrix;
//TODO W1
//ONB => invViewMatrix
//Inverse(ONB) => ViewMatrix
invViewMatrix = temp.Inverse();
//ViewMatrix => Matrix::CreateLookAtLH(...) [not implemented yet]
//DirectX Implementation => https://learn.microsoft.com/en-us/windows/win32/direct3d9/d3dxmatrixlookatlh
right = invViewMatrix.GetAxisX();
up = invViewMatrix.GetAxisY();
forward = invViewMatrix.GetAxisZ();
}
void CalculateProjectionMatrix() {
@@ -77,43 +77,50 @@ namespace dae {
const float deltaTime = pTimer->GetElapsed();
//Keyboard Input
const uint8_t *pKeyboardState = SDL_GetKeyboardState(nullptr);
Vector3 zDirection{0.f, 0.f, 0.f};
const Vector3 xDirection{0.f, 0.f, 0.f};
const Vector3 yDirection{0.f, 0.f, 0.f};
const uint8_t* pKeyboardState = SDL_GetKeyboardState(nullptr);
Vector3 zDirection{ 0.f,0.f,0.f };
const Vector3 xDirection{ 0.f,0.f,0.f };
const Vector3 yDirection{ 0.f,0.f,0.f };
float MovementSpeed{10.f};
float MovementSpeed{ 10.f };
if (pKeyboardState[SDL_SCANCODE_LSHIFT]) {
if (pKeyboardState[SDL_SCANCODE_LSHIFT])
{
MovementSpeed *= 2;
}
if (pKeyboardState[SDL_SCANCODE_W]) {
if (pKeyboardState[SDL_SCANCODE_W])
{
origin += forward * deltaTime * MovementSpeed;
}
if (pKeyboardState[SDL_SCANCODE_A]) {
if (pKeyboardState[SDL_SCANCODE_A])
{
origin -= right * deltaTime * MovementSpeed;
}
if (pKeyboardState[SDL_SCANCODE_S]) {
if (pKeyboardState[SDL_SCANCODE_S])
{
origin -= forward * deltaTime * MovementSpeed;
}
if (pKeyboardState[SDL_SCANCODE_D]) {
if (pKeyboardState[SDL_SCANCODE_D])
{
origin += right * deltaTime * MovementSpeed;
}
if (pKeyboardState[SDL_SCANCODE_E]) {
if (pKeyboardState[SDL_SCANCODE_E])
{
origin += up * deltaTime * MovementSpeed;
}
if (pKeyboardState[SDL_SCANCODE_Q]) {
if (pKeyboardState[SDL_SCANCODE_Q])
{
origin -= up * deltaTime * MovementSpeed;
}
//Mouse Input
bool mousePosChange{false};
bool mousePosChange{ false };
int mouseX{}, mouseY{};
const uint32_t mouseState = SDL_GetRelativeMouseState(&mouseX, &mouseY);
mouseY *= -1;
@@ -125,13 +132,16 @@ namespace dae {
totalPitch += (static_cast<float>(mouseY) * mouseSens) * TO_RADIANS;
mousePosChange = true;
} else if (mouseState == SDL_BUTTON_LEFT) {
}
else if (mouseState == SDL_BUTTON_LEFT)
{
zDirection = forward.Normalized() * static_cast<float>(-mouseY);
totalYaw += static_cast<float>(mouseX) * mouseSens * TO_RADIANS;
mousePosChange = true;
} else if (mouseState == SDL_BUTTON_X2) //lmb + rmb
}
else if (mouseState == SDL_BUTTON_X2) //lmb + rmb
{
origin.y += static_cast<float>(mouseY) / 2;
@@ -140,11 +150,12 @@ namespace dae {
origin += ((zDirection + xDirection + yDirection) * cameraSpeed * deltaTime);
if (mousePosChange) {
const Matrix yawMatrix{Matrix::CreateRotationY(totalYaw)};
const Matrix pitchMatrix{Matrix::CreateRotationX(totalPitch)};
if (mousePosChange)
{
const Matrix yawMatrix{ Matrix::CreateRotationY(totalYaw) };
const Matrix pitchMatrix{ Matrix::CreateRotationX(totalPitch) };
const Matrix finalRotation{pitchMatrix * yawMatrix};
const Matrix finalRotation{ pitchMatrix * yawMatrix };
forward = finalRotation.TransformVector(Vector3::UnitZ);
forward.Normalize();
}
@@ -152,6 +163,7 @@ namespace dae {
//Update Matrices
CalculateViewMatrix();
CalculateProjectionMatrix(); //Try to optimize this - should only be called once or when fov/aspectRatio changes
}
};
}

View File

@@ -1,5 +1,6 @@
#pragma once
#include "MathHelpers.h"
#include <algorithm>
namespace dae
{

View File

@@ -8,22 +8,23 @@ namespace dae
{
struct Vertex
{
Vector3 position{};
Vector4 position{};
ColorRGB color{colors::White};
Vector2 uv{}; //W2
//Vector3 normal{}; //W4
//Vector3 tangent{}; //W4
//Vector3 viewDirection{}; //W4
Vector2 uv{};
bool valid{ true };
Vector3 normal{};
Vector3 tangent{};
Vector3 viewDir{};
};
struct Vertex_Out
struct Sample
{
Vector4 position{};
ColorRGB color{ colors::White };
Vector2 uv{};
//Vector3 normal{};
//Vector3 tangent{};
//Vector3 viewDirection{};
Vector3 normal{};
Vector3 tangent{};
Vector3 viewDirection{};
float depth{};
Vector3 weight{};
};
enum class PrimitiveTopology
@@ -36,9 +37,9 @@ namespace dae
{
std::vector<Vertex> vertices{};
std::vector<uint32_t> indices{};
PrimitiveTopology primitiveTopology{ PrimitiveTopology::TriangleStrip };
PrimitiveTopology primitiveTopology{ PrimitiveTopology::TriangleList };
std::vector<Vertex_Out> vertices_out{};
std::vector<Vertex> vertices_out{};
Matrix worldMatrix{};
};
}

View File

@@ -15,11 +15,84 @@ float max(float a, float b, float 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);
return (p1.x - p0.x) * (point.y - p0.y)
- (p1.y - p0.y) * (point.x - p0.x);
}
template<typename Type>
Type interpolate(const Type& val0, const Type& val1, const Type& val2, float depth, float weight)
{
return {
val0 * depth * weight +
val1 * depth * weight +
val2 * depth * weight
};
}
std::optional<Sample> HitTest::TriangleHitTest(const Vector3& fragPos, const Vertex& v0, const Vertex& v1, const Vertex& v2)
{
Vector3 weights;
weights.x = CrossZ(v2.position, v1.position, fragPos);
if ( weights.x > 0 )
return std::nullopt;
weights.y = CrossZ(v0.position, v2.position, fragPos);
if ( weights.y > 0 )
return std::nullopt;
weights.z = CrossZ(v1.position, v0.position, fragPos);
if ( weights.z > 0 )
return std::nullopt;
const float totalWeight{ weights.x + weights.y + weights.z };
const float invTotalWeight{ 1.0f / totalWeight };
const Vector3 normWeights{
weights.x * invTotalWeight,
weights.y * invTotalWeight,
weights.z * invTotalWeight
};
const float depth =
1 / (normWeights.x / v0.position.w +
normWeights.y / v1.position.w +
normWeights.z / v2.position.w);
const Vector2 uv =
v0.uv * depth * normWeights.x / v0.position.w +
v1.uv * depth * normWeights.y / v1.position.w +
v2.uv * depth * normWeights.z / v2.position.w;
const float interpolatedDepth = 1 / (normWeights.x / v0.position.w + normWeights.y / v1.position.w + normWeights.z / v2.position.w);
auto interpolate =
[&]<typename Type>(const Type& val0, const Type& val1, const Type& val2) -> Type
{
return (val0 / v0.position.w * normWeights.x +
val1 / v1.position.w * normWeights.y +
val2 / v2.position.w * normWeights.z) * interpolatedDepth;
};
//Invert the normal
const Vector3 normal = interpolate(v0.normal, v1.normal, v2.normal).Normalized();
const Vector3 tangent = interpolate(v0.tangent, v1.tangent, v2.tangent).Normalized();
const Vector3 viewDir = interpolate(v0.viewDir, v1.viewDir, v2.viewDir).Normalized();
return Sample{
.uv = uv,
.normal = normal,
.tangent = tangent,
.viewDirection = viewDir,
.depth = depth,
.weight = normWeights
};
}
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);
@@ -46,9 +119,10 @@ HitTest::HitTestSample HitTest::Triangle(const Vector3 &pos, const std::vector<V
}
const double totalWeight{ w0 + w1 + w2 };
w0 /= totalWeight;
w1 /= totalWeight;
w2 /= totalWeight;
const float invTotalWeight{ static_cast<float>(1.0f / totalWeight) };
w0 *= invTotalWeight;
w1 *= invTotalWeight;
w2 *= invTotalWeight;
assert(std::abs(w0 + w1 + w2 - 1) < 0.0001);

View File

@@ -5,6 +5,7 @@
#ifndef GP1_RASTERIZER_HITTEST_H
#define GP1_RASTERIZER_HITTEST_H
#include <optional>
#include "DataTypes.h"
#include "Maths.h"
@@ -17,6 +18,9 @@ namespace HitTest {
};
HitTestSample Triangle(const Vector3& pos, const std::vector<Vertex>& vertices);
std::optional<Sample> TriangleHitTest(const Vector3& fragPos, const Vertex& v0, const Vertex& v1, const Vertex& v2);
}
#endif //GP1_RASTERIZER_HITTEST_H

View File

@@ -6,34 +6,29 @@
#include <cmath>
namespace dae {
Matrix::Matrix(const Vector3& xAxis, const Vector3& yAxis, const Vector3& zAxis, const Vector3& t) :
Matrix({ xAxis, 0 }, { yAxis, 0 }, { zAxis, 0 }, { t, 1 })
{
Matrix::Matrix(const Vector3 &xAxis, const Vector3 &yAxis, const Vector3 &zAxis, const Vector3 &t) :
Matrix({xAxis, 0}, {yAxis, 0}, {zAxis, 0}, {t, 1}) {
}
Matrix::Matrix(const Vector4& xAxis, const Vector4& yAxis, const Vector4& zAxis, const Vector4& t)
{
Matrix::Matrix(const Vector4 &xAxis, const Vector4 &yAxis, const Vector4 &zAxis, const Vector4 &t) {
data[0] = xAxis;
data[1] = yAxis;
data[2] = zAxis;
data[3] = t;
}
Matrix::Matrix(const Matrix& m)
{
Matrix::Matrix(const Matrix &m) {
data[0] = m[0];
data[1] = m[1];
data[2] = m[2];
data[3] = m[3];
}
Vector3 Matrix::TransformVector(const Vector3& v) const
{
Vector3 Matrix::TransformVector(const Vector3 &v) const {
return TransformVector(v.x, v.y, v.z);
}
Vector3 Matrix::TransformVector(float x, float y, float z) const
{
Vector3 Matrix::TransformVector(float x, float y, float z) const {
return Vector3{
data[0].x * x + data[1].x * y + data[2].x * z,
data[0].y * x + data[1].y * y + data[2].y * z,
@@ -41,13 +36,11 @@ namespace dae {
};
}
Vector3 Matrix::TransformPoint(const Vector3& p) const
{
Vector3 Matrix::TransformPoint(const Vector3 &p) const {
return TransformPoint(p.x, p.y, p.z);
}
Vector3 Matrix::TransformPoint(float x, float y, float z) const
{
Vector3 Matrix::TransformPoint(float x, float y, float z) const {
return Vector3{
data[0].x * x + data[1].x * y + data[2].x * z + data[3].x,
data[0].y * x + data[1].y * y + data[2].y * z + data[3].y,
@@ -55,13 +48,11 @@ namespace dae {
};
}
Vector4 Matrix::TransformPoint(const Vector4& p) const
{
Vector4 Matrix::TransformPoint(const Vector4 &p) const {
return TransformPoint(p.x, p.y, p.z, p.w);
}
Vector4 Matrix::TransformPoint(float x, float y, float z, float w) const
{
Vector4 Matrix::TransformPoint(float x, float y, float z, float w) const {
return Vector4{
data[0].x * x + data[1].x * y + data[2].x * z + data[3].x,
data[0].y * x + data[1].y * y + data[2].y * z + data[3].y,
@@ -70,13 +61,10 @@ namespace dae {
};
}
const Matrix& Matrix::Transpose()
{
const Matrix &Matrix::Transpose() {
Matrix result{};
for (int r{ 0 }; r < 4; ++r)
{
for (int c{ 0 }; c < 4; ++c)
{
for (int r{0}; r < 4; ++r) {
for (int c{0}; c < 4; ++c) {
result[r][c] = data[c][r];
}
}
@@ -89,13 +77,12 @@ namespace dae {
return *this;
}
const Matrix& Matrix::Inverse()
{
const Matrix &Matrix::Inverse() {
//Optimized Inverse as explained in FGED1 - used widely in other libraries too.
const Vector3& a = data[0];
const Vector3& b = data[1];
const Vector3& c = data[2];
const Vector3& d = data[3];
const Vector3 &a = data[0];
const Vector3 &b = data[1];
const Vector3 &c = data[2];
const Vector3 &d = data[3];
const float x = data[0][3];
const float y = data[1][3];
@@ -111,89 +98,103 @@ namespace dae {
assert((!AreEqual(det, 0.f)) && "ERROR: determinant is 0, there is no INVERSE!");
float invDet = 1.f / det;
s *= invDet; t *= invDet; u *= invDet; v *= invDet;
s *= invDet;
t *= invDet;
u *= invDet;
v *= invDet;
Vector3 r0 = Vector3::Cross(b, v) + t * y;
Vector3 r1 = Vector3::Cross(v, a) - t * x;
Vector3 r2 = Vector3::Cross(d, u) + s * w;
Vector3 r3 = Vector3::Cross(u, c) - s * z;
data[0] = Vector4{ r0.x, r1.x, r2.x, 0.f };
data[1] = Vector4{ r0.y, r1.y, r2.y, 0.f };
data[2] = Vector4{ r0.z, r1.z, r2.z, 0.f };
data[3] = { { -Vector3::Dot(b, t)},{Vector3::Dot(a, t)},{-Vector3::Dot(d, s)},{Vector3::Dot(c, s)} };
data[0] = Vector4{r0.x, r1.x, r2.x, 0.f};
data[1] = Vector4{r0.y, r1.y, r2.y, 0.f};
data[2] = Vector4{r0.z, r1.z, r2.z, 0.f};
data[3] = {{-Vector3::Dot(b, t)},
{Vector3::Dot(a, t)},
{-Vector3::Dot(d, s)},
{Vector3::Dot(c, s)}};
return *this;
}
Matrix Matrix::Transpose(const Matrix& m)
{
Matrix out{ m };
Matrix Matrix::Transpose(const Matrix &m) {
Matrix out{m};
out.Transpose();
return out;
}
Matrix Matrix::Inverse(const Matrix& m)
{
Matrix out{ m };
Matrix Matrix::Inverse(const Matrix &m) {
Matrix out{m};
out.Inverse();
return out;
}
Matrix Matrix::CreateLookAtLH(const Vector3& origin, const Vector3& forward, const Vector3& up)
{
Vector3 zAxis = forward.Normalized();
Matrix Matrix::CreateLookAtLH(const Vector3 &origin, const Vector3 &forward, const Vector3 &up) {
Vector3 zAxis = (forward - origin).Normalized();
Vector3 xAxis = Vector3::Cross(up, zAxis).Normalized();
Vector3 yAxis = Vector3::Cross(zAxis, xAxis);
return { xAxis, yAxis, zAxis, origin };
}
Matrix Matrix::CreatePerspectiveFovLH(float fov, float aspect, float zn, float zf) {
const float yScale = 1.f / tanf(fov / 2.f);
const float xScale = yScale / aspect;
Vector3 yAxis = Vector3::Cross(zAxis, xAxis).Normalized();
Vector3 trans =
{
-Vector3::Dot(xAxis, origin),
-Vector3::Dot(yAxis, origin),
-Vector3::Dot(zAxis, origin)
};
return {
{ xScale, 0, 0, 0 },
{ 0, yScale, 0, 0 },
{ 0, 0, zf / (zf - zn), 1 },
{ 0, 0, -zn * zf / (zf - zn), 0 }
{xAxis.x, yAxis.x, zAxis.x},
{xAxis.y, yAxis.y, zAxis.y},
{xAxis.z, yAxis.z, zAxis.z},
{trans.x, trans.y, trans.z}
};
}
Vector3 Matrix::GetAxisX() const
{
Matrix Matrix::CreatePerspectiveFovLH(float fovy, float aspect, float zn, float zf) {
// const float yScale = 1.f / tanf(fovy / 2.f);
// const float xScale = yScale / aspect;
//
// return {
// {xScale, 0, 0, 0},
// {0, yScale, 0, 0},
// {0, 0, zf / (zf - zn), 1},
// {0, 0, -zn * zf / (zf - zn), 0}
// };
return Matrix(
{ 1.f / (aspect * fovy), 0, 0, 0 },
{ 0, 1.f / fovy, 0, 0 },
{ 0, 0, (zf) / (zf - zn), 1 },
{ 0, 0, -(zf * zn) / (zf - zn), 0 }
);
}
Vector3 Matrix::GetAxisX() const {
return data[0];
}
Vector3 Matrix::GetAxisY() const
{
Vector3 Matrix::GetAxisY() const {
return data[1];
}
Vector3 Matrix::GetAxisZ() const
{
Vector3 Matrix::GetAxisZ() const {
return data[2];
}
Vector3 Matrix::GetTranslation() const
{
Vector3 Matrix::GetTranslation() const {
return data[3];
}
Matrix Matrix::CreateTranslation(float x, float y, float z)
{
return CreateTranslation({ x, y, z });
Matrix Matrix::CreateTranslation(float x, float y, float z) {
return CreateTranslation({x, y, z});
}
Matrix Matrix::CreateTranslation(const Vector3& t)
{
return { Vector3::UnitX, Vector3::UnitY, Vector3::UnitZ, t };
Matrix Matrix::CreateTranslation(const Vector3 &t) {
return {Vector3::UnitX, Vector3::UnitY, Vector3::UnitZ, t};
}
Matrix Matrix::CreateRotationX(float pitch)
{
Matrix Matrix::CreateRotationX(float pitch) {
return {
{1, 0, 0, 0},
{0, cos(pitch), -sin(pitch), 0},
@@ -202,8 +203,7 @@ namespace dae {
};
}
Matrix Matrix::CreateRotationY(float yaw)
{
Matrix Matrix::CreateRotationY(float yaw) {
return {
{cos(yaw), 0, -sin(yaw), 0},
{0, 1, 0, 0},
@@ -212,8 +212,7 @@ namespace dae {
};
}
Matrix Matrix::CreateRotationZ(float roll)
{
Matrix Matrix::CreateRotationZ(float roll) {
return {
{cos(roll), sin(roll), 0, 0},
{-sin(roll), cos(roll), 0, 0},
@@ -222,48 +221,40 @@ namespace dae {
};
}
Matrix Matrix::CreateRotation(float pitch, float yaw, float roll)
{
return CreateRotation({ pitch, yaw, roll });
Matrix Matrix::CreateRotation(float pitch, float yaw, float roll) {
return CreateRotation({pitch, yaw, roll});
}
Matrix Matrix::CreateRotation(const Vector3& r)
{
Matrix Matrix::CreateRotation(const Vector3 &r) {
return CreateRotationX(r[0]) * CreateRotationY(r[1]) * CreateRotationZ(r[2]);
}
Matrix Matrix::CreateScale(float sx, float sy, float sz)
{
return { {sx, 0, 0}, {0, sy, 0}, {0, 0, sz}, Vector3::Zero };
Matrix Matrix::CreateScale(float sx, float sy, float sz) {
return {{sx, 0, 0}, {0, sy, 0}, {0, 0, sz}, Vector3::Zero};
}
Matrix Matrix::CreateScale(const Vector3& s)
{
Matrix Matrix::CreateScale(const Vector3 &s) {
return CreateScale(s[0], s[1], s[2]);
}
#pragma region Operator Overloads
Vector4& Matrix::operator[](int index)
{
Vector4 &Matrix::operator[](int index) {
assert(index <= 3 && index >= 0);
return data[index];
}
Vector4 Matrix::operator[](int index) const
{
Vector4 Matrix::operator[](int index) const {
assert(index <= 3 && index >= 0);
return data[index];
}
Matrix Matrix::operator*(const Matrix& m) const
{
Matrix Matrix::operator*(const Matrix &m) const {
Matrix result{};
Matrix m_transposed = Transpose(m);
for (int r{ 0 }; r < 4; ++r)
{
for (int c{ 0 }; c < 4; ++c)
{
for (int r{0}; r < 4; ++r) {
for (int c{0}; c < 4; ++c) {
result[r][c] = Vector4::Dot(data[r], m_transposed[c]);
}
}
@@ -271,15 +262,12 @@ namespace dae {
return result;
}
const Matrix& Matrix::operator*=(const Matrix& m)
{
Matrix copy{ *this };
const Matrix &Matrix::operator*=(const Matrix &m) {
Matrix copy{*this};
Matrix m_transposed = Transpose(m);
for (int r{ 0 }; r < 4; ++r)
{
for (int c{ 0 }; c < 4; ++c)
{
for (int r{0}; r < 4; ++r) {
for (int c{0}; c < 4; ++c) {
data[r][c] = Vector4::Dot(copy[r], m_transposed[c]);
}
}
@@ -287,8 +275,7 @@ namespace dae {
return *this;
}
bool Matrix::operator==(const Matrix& m) const
{
bool Matrix::operator==(const Matrix &m) const {
return data[0] == m.data[0]
&& data[1] == m.data[1]
&& data[2] == m.data[2]

View File

@@ -1,11 +1,14 @@
//External includes
#include <iostream>
#include <SDL.h>
#include <array>
#include "SDL_surface.h"
//Project includes
#include "Renderer.h"
#include "Maths.h"
#include "HitTest.h"
#include "utils.h"
using namespace dae;
@@ -21,13 +24,16 @@ Renderer::Renderer(SDL_Window *pWindow) : m_pWindow(pWindow) {
m_pDepthBufferPixels = new float[m_Width * m_Height];
//Initialize Camera
m_Camera.Initialize(60.f, {.0f, .0f, -10.f});
//Initialize Camera
m_Camera.Initialize(60.f, {.0f, .0f, -10.f});
m_aspectRatio = static_cast<float>(m_Width) / static_cast<float>(m_Height);
m_Camera.Initialize(45.f, {.0f, 5.0f, -64.f}, m_aspectRatio);
m_pTexture = Texture::LoadFromFile("./Resources/uv_grid_2.png");
m_currentDiffuse = Texture::LoadFromFile("./Resources/vehicle_diffuse.png");
m_currentGloss = Texture::LoadFromFile("./Resources/vehicle_gloss.png");
m_currentNormal = Texture::LoadFromFile("./Resources/vehicle_normal.png");
m_currentSpecular = Texture::LoadFromFile("./Resources/vehicle_specular.png");
Utils::ParseOBJ("./Resources/vehicle.obj", m_mesh.vertices, m_mesh.indices, false);
m_worldMeshes.push_back(m_mesh);
}
Renderer::~Renderer() {
@@ -36,6 +42,11 @@ Renderer::~Renderer() {
void Renderer::Update(Timer *pTimer) {
m_Camera.Update(pTimer);
//Rotate the mesh
if(m_isRotating){
m_worldMeshes[0].worldMatrix = Matrix::CreateRotationY(SDL_GetTicks() / 1000.f);
}
}
void Renderer::Render() {
@@ -54,10 +65,11 @@ void Renderer::Render() {
constexpr int numVerticies = 3;
std::vector<Vertex> verticiesScreenSpace{};
// VertexTransformationFunction(m_verticiesWorld, verticiesScreenSpace);
for (const Mesh &currentMesh: m_meshesWorldStrip) {
VertexTransformationFunction(currentMesh.vertices, verticiesScreenSpace);
for (const Mesh &currentMesh: m_worldMeshes) {
const Matrix worldViewProjectionMatrix{ currentMesh.worldMatrix * m_Camera.viewMatrix * m_Camera.ProjectionMatrix };
VertexTransformationFunction(worldViewProjectionMatrix, currentMesh, currentMesh.vertices, verticiesScreenSpace);
int numTriangles{};
@@ -97,6 +109,10 @@ void Renderer::Render() {
}
break;
}
if (!vertex0.valid and !vertex1.valid and !vertex2.valid) {
continue;
}
const float minX{std::min(vertex0.position.x, std::min(vertex1.position.x, vertex2.position.x))};
const float minY{std::min(vertex0.position.y, std::min(vertex1.position.y, vertex2.position.y))};
@@ -127,44 +143,31 @@ void Renderer::Render() {
Vector3 P{static_cast<float>(px) + 0.5f, static_cast<float>(py) + 0.5f, 1.f};
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) {
auto sample{ HitTest::TriangleHitTest(P, vertex0, vertex1, vertex2) };
if (!sample.has_value()) {
continue;
}
const int depthBufferIndex{px + (py * m_Width)};
const Vector3 fragPos{ static_cast<float>(px) + 0.5f, static_cast<float>(py) + 0.5f, 1.f };
float totalWeight{cross0.z + cross1.z + cross2.z};
Vector3 weights{
cross0.z / totalWeight,
cross1.z / totalWeight,
cross2.z / totalWeight,
};
int depthBufferIndex{ px + (py * m_Width) };
const float currentDepth =
1 / (weights.x / vertex0.position.z +
weights.y / vertex1.position.z +
weights.z / vertex2.position.z);
float min{.985f};
float max{1.f};
float depthBuffer{(sample.value().depth - min) * (max - min)};
float currentDepth = sample.value().depth;
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) {
if (m_pDepthBufferPixels[depthBufferIndex] > currentDepth) {
m_pDepthBufferPixels[depthBufferIndex] = currentDepth;
//Update Color in Buffer
finalColor = m_pTexture->Sample(UvCoords);
if (m_isDepthBuffer) {
finalColor = ColorRGB{depthBuffer, depthBuffer, depthBuffer};
} else {
// finalColor = m_pTexture->Sample(UvCoords);
finalColor = shadePixel(sample.value());
}
finalColor.MaxToOne();
m_pBackBufferPixels[px + (py * m_Width)] = SDL_MapRGB(m_pBackBuffer->format,
@@ -174,28 +177,14 @@ void Renderer::Render() {
}
}
}
}
}
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
@@ -205,20 +194,117 @@ void Renderer::Render() {
SDL_UpdateWindowSurface(m_pWindow);
}
void Renderer::VertexTransformationFunction(const std::vector<Vertex> &vertices_in,
void Renderer::VertexTransformationFunction(const Matrix& WorldViewProjectionMatrix, const Mesh& mesh, const std::vector<Vertex> &vertices_in,
std::vector<Vertex> &vertices_out) const {
for (const Vertex &vert: vertices_in) {
Vector3 vertPos{m_Camera.invViewMatrix.TransformPoint(vert.position)};
// vertices_out.clear();
for (const Vertex& vert : vertices_in)
{
Vertex vertex_out{};
vertPos.x = (vertPos.x / vertPos.z) / (m_aspectRatio * m_Camera.fov);
vertPos.y = (vertPos.y / vertPos.z) / m_Camera.fov;
Vector4 vertPos{ WorldViewProjectionMatrix.TransformPoint({vert.position.GetXYZ(), 1}) };
vertPos.x = (vertPos.x + 1) / 2 * static_cast<float>(m_Width);
vertPos.y = (1 - vertPos.y) / 2 * static_cast<float>(m_Height);
vertices_out.push_back(Vertex{vertPos, vert.color, vert.uv});
const Vector3 normal{ mesh.worldMatrix.TransformVector(vert.normal) };
const Vector3 tangent{ mesh.worldMatrix.TransformVector(vert.tangent) };
vertPos.x /= vertPos.w;
vertPos.y /= vertPos.w;
vertPos.z /= vertPos.w;
bool isValid{ true };
//Check if the vertex is inside the screen
if (vertPos.x < -1.f || vertPos.x > 1.f ||
vertPos.y < -1.f || vertPos.y > 1.f ||
vertPos.z < 0.f || vertPos.z > 1.f)
isValid = false;
vertPos.x = ((vertPos.x + 1.f) / 2.f) * static_cast<float>(m_Width);
vertPos.y = ((1.f - vertPos.y) / 2.f) * static_cast<float>(m_Height);
vertex_out.position = vertPos;
vertex_out.color = vert.color;
vertex_out.uv = vert.uv;
vertex_out.normal = normal;
vertex_out.tangent = tangent;
vertex_out.viewDir = WorldViewProjectionMatrix.TransformVector(vert.viewDir);
vertex_out.valid = isValid;
vertices_out.push_back(vertex_out);
}
}
bool Renderer::SaveBufferToImage() const {
return SDL_SaveBMP(m_pBackBuffer, "Rasterizer_ColorBuffer.bmp");
}
ColorRGB Renderer::shadePixel(const Sample &sample) {
Vector3 lightDirection = { .577f, -.577f, .577f};
Vector3 normal = sample.normal.Normalized();
constexpr float lightIntensity{ 7.f };
ColorRGB color{ 1, 1, 1 };
constexpr ColorRGB ambient{ .03f, .03f, .03f};
if(m_useNormals){
const ColorRGB normalSample{ m_currentNormal->Sample(sample.uv) };
const Vector4 normalMapSample{
2.f * normalSample.r - 1.f,
2.f * normalSample.g - 1.f,
2.f * normalSample.b - 1.f,
0.f
};
const Vector3 biNormal{ Vector3::Cross(normal, sample.tangent) };
const Matrix tangentToWorld{
Vector4{ sample.tangent, 0.f },
Vector4{ biNormal, 0.f },
Vector4{ normal, 0.f },
Vector4{ 0.f, 0.f, 0.f, 1.f }
};
normal = tangentToWorld.TransformVector(normalMapSample).Normalized();
}
const ColorRGB diffuseSample{ m_currentDiffuse->Sample(sample.uv) };
double invPi = 1.0 / PI;
const ColorRGB lambert{ diffuseSample * lightIntensity * invPi };
//TODO: ask why deviding by PI causses Segmentation fault
// const ColorRGB lambert{ diffuseSample * lightIntensity / PI };
float specularReflectance{ 1.f };
float shininess{ 25.f };
specularReflectance *= m_currentGloss->Sample(sample.uv).r;
shininess *= m_currentSpecular->Sample(sample.uv).r;
const float cosAngle = Vector3::Dot(normal, -lightDirection);
const ColorRGB specular = specularReflectance * powf(cosAngle, shininess) * colors::White;
if (cosAngle < 0) {
return ambient;
}
switch(m_ShadeMode){
case ShadeMode::ObservedArea:
break;
case ShadeMode::Diffuse:
color = lambert;
break;
case ShadeMode::Specular:
color = specular;
break;
case ShadeMode::Combined:
color = lambert + specular + ambient;
break;
}
color *= ColorRGB{ cosAngle, cosAngle, cosAngle };
return color;
}

View File

@@ -11,8 +11,16 @@
struct SDL_Window;
struct SDL_Surface;
namespace dae {
enum class ShadeMode{
ObservedArea,
Diffuse,
Specular,
Combined
};
struct Mesh;
class Timer;
@@ -40,7 +48,48 @@ namespace dae {
bool SaveBufferToImage() const;
void
VertexTransformationFunction(const std::vector<Vertex> &vertices_in, std::vector<Vertex> &vertices_out) const;
VertexTransformationFunction(const Matrix& WorldViewProjectionMatrix, const Mesh& mesh, const std::vector<Vertex> &vertices_in, std::vector<Vertex> &vertices_out) const;
ColorRGB shadePixel(const Sample& sample);
void SwitchDepthBuffer(){
m_isDepthBuffer = !m_isDepthBuffer;
}
void SetShadeMode(ShadeMode mode) {
m_ShadeMode = mode;
}
void ToggleRotation(){
m_isRotating = !m_isRotating;
std::cout << "Rotation: " << m_isRotating << std::endl;
}
void ToggleNormals(){
m_useNormals = !m_useNormals;
std::cout << "Use Normals: " << m_useNormals << std::endl;
}
void CycleRenderingMode(){
switch (m_ShadeMode) {
case ShadeMode::ObservedArea:
m_ShadeMode = ShadeMode::Diffuse;
std::cout << "Diffuse" << std::endl;
break;
case ShadeMode::Diffuse:
m_ShadeMode = ShadeMode::Specular;
std::cout << "Specular" << std::endl;
break;
case ShadeMode::Specular:
m_ShadeMode = ShadeMode::Combined;
std::cout << "Combined" << std::endl;
break;
case ShadeMode::Combined:
m_ShadeMode = ShadeMode::ObservedArea;
std::cout << "Observed Area" << std::endl;
break;
}
}
private:
SDL_Window *m_pWindow{};
@@ -56,83 +105,22 @@ namespace dae {
float m_aspectRatio{};
Texture* m_pTexture{ nullptr };
//
// std::vector<Vertex> m_verticiesWorld{
// //Triangle 0
// {{0.f,2.f,0.f}, {1,0,0}},
// {{1.5f,-1.f,0.f}, {1,0,0}},
// {{-1.5f,-1.f,0.f}, {1,0,0}},
//
// //Triangle 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}}
// };
Texture* m_currentDiffuse{ nullptr };
Texture* m_currentGloss{ nullptr };
Texture* m_currentNormal{ nullptr };
Texture* m_currentSpecular{ nullptr };
std::vector<Mesh> m_meshesWorldList
{
Mesh
{
{
Vertex{ {-3, 3, -2 }, { 1, 1, 1 }, { 0.0f, 0.0f } },
Vertex{ { 0, 3, -2 }, { 1, 1, 1 }, { 0.5f, 0.0f } },
Vertex{ { 3, 3, -2 }, { 1, 1, 1 }, { 1.0f, 0.0f } },
Vertex{ {-3, 0, -2 }, { 1, 1, 1 }, { 0.0f, 0.5f } },
Vertex{ { 0, 0, -2 }, { 1, 1, 1 }, { 0.5f, 0.5f } },
Vertex{ { 3, 0, -2 }, { 1, 1, 1 }, { 1.0f, 0.5f } },
Vertex{ {-3, -3, -2 }, { 1, 1, 1 }, { 0.0f, 1.0f } },
Vertex{ { 0, -3, -2 }, { 1, 1, 1 }, { 0.5f, 1.0f } },
Vertex{ { 3, -3, -2 }, { 1, 1, 1 }, { 1.0f, 1.0f } }
},
{
3, 0, 1, 1, 4, 3, 4, 1 ,2,
2, 5, 4, 6, 3, 4, 4, 7, 6,
7, 4, 5, 5, 8, 7
},
PrimitiveTopology::TriangleList
}
};
std::vector<Mesh> m_meshesWorldStrip
{
Mesh
{
std::vector<Vertex>{
{{-3, 3, -2}, {colors::White}, {0,0} },
{{0, 3, -2}, {colors::Red}, {0.5,0}},
{{3, 3, -2}, {colors::Blue}, {1,0}},
{{-3, 0, -2}, {colors::Red}, {0,0.5}},
{{0, 0, -2}, {colors::Yellow}, {0.5, 0.5}},
{{3, 0, -2}, {colors::White}, {1, 0.5}},
{{-3, -3, -2}, {colors::White}, {0,1}},
{{0, -3, -2}, {colors::White}, {0.5,1}},
{{3, -3, -2}, {colors::White}, {1,1}},
},
std::vector<uint32_t>{
3, 0, 4, 1, 5, 2,
2, 6,
6, 3, 7, 4, 8, 5,
},
PrimitiveTopology::TriangleStrip,
}
};
//square
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{};
ShadeMode m_ShadeMode{ ShadeMode::Combined };
bool m_isRotating{ true };
bool m_useNormals{ true };
Mesh m_mesh{};
bool m_isDepthBuffer{ false };
std::vector<Mesh> m_worldMeshes{};
float* m_pDepthBufferPixels{};

View File

@@ -4,7 +4,7 @@
#include "Maths.h"
#include "DataTypes.h"
#define DISABLE_OBJ
//#define DISABLE_OBJ
namespace dae
{
@@ -84,7 +84,7 @@ namespace dae
{
// OBJ format uses 1-based arrays
file >> iPosition;
vertex.position = positions[iPosition - 1];
vertex.position = positions[iPosition - 1].ToVector4();
if ('/' == file.peek())//is next in buffer == '/' ?
{

View File

@@ -6,10 +6,10 @@ namespace dae
struct Vector3;
struct Vector4
{
float x;
float y;
float z;
float w;
float x{};
float y{};
float z{};
float w{};
Vector4() = default;
Vector4(float _x, float _y, float _z, float _w);
@@ -20,8 +20,8 @@ namespace dae
float Normalize();
Vector4 Normalized() const;
Vector2 GetXY() const;
Vector3 GetXYZ() const;
[[nodiscard]] Vector2 GetXY() const;
[[nodiscard]] Vector3 GetXYZ() const;
static float Dot(const Vector4& v1, const Vector4& v2);

View File

@@ -2,8 +2,10 @@
#ifdef ENABLE_VLD
#include "vld.h"
#endif
#include "SDL.h"
#include "SDL_surface.h"
#undef main
//Standard includes
@@ -15,17 +17,15 @@
using namespace dae;
void ShutDown(SDL_Window* pWindow)
{
void ShutDown(SDL_Window *pWindow) {
SDL_DestroyWindow(pWindow);
SDL_Quit();
}
int main(int argc, char* args[])
{
int main(int argc, char *args[]) {
//Unreferenced parameters
(void)argc;
(void)args;
(void) argc;
(void) args;
//Create window + surfaces
SDL_Init(SDL_INIT_VIDEO);
@@ -33,8 +33,8 @@ int main(int argc, char* args[])
const uint32_t width = 640;
const uint32_t height = 480;
SDL_Window* pWindow = SDL_CreateWindow(
"Rasterizer - **Insert Name**",
SDL_Window *pWindow = SDL_CreateWindow(
"Rasterizer - Bram Verhulst",
SDL_WINDOWPOS_UNDEFINED,
SDL_WINDOWPOS_UNDEFINED,
width, height, 0);
@@ -55,14 +55,11 @@ int main(int argc, char* args[])
float printTimer = 0.f;
bool isLooping = true;
bool takeScreenshot = false;
while (isLooping)
{
while (isLooping) {
//--------- Get input events ---------
SDL_Event e;
while (SDL_PollEvent(&e))
{
switch (e.type)
{
while (SDL_PollEvent(&e)) {
switch (e.type) {
case SDL_QUIT:
isLooping = false;
break;
@@ -70,6 +67,21 @@ int main(int argc, char* args[])
if (e.key.keysym.scancode == SDL_SCANCODE_X)
takeScreenshot = true;
break;
case SDL_KEYDOWN: {
auto key = e.key.keysym.scancode;
if (key == SDL_SCANCODE_F4) {
pRenderer->SwitchDepthBuffer();
}
if (key == SDL_SCANCODE_F5) {
pRenderer->ToggleRotation();
}
if (key == SDL_SCANCODE_F6) {
pRenderer->ToggleNormals();
}
if (key == SDL_SCANCODE_F7) {
pRenderer->CycleRenderingMode();
}
}
}
}
@@ -82,15 +94,13 @@ int main(int argc, char* args[])
//--------- Timer ---------
pTimer->Update();
printTimer += pTimer->GetElapsed();
if (printTimer >= 1.f)
{
if (printTimer >= 1.f) {
printTimer = 0.f;
std::cout << "dFPS: " << pTimer->GetdFPS() << std::endl;
}
//Save screenshot after full render
if (takeScreenshot)
{
if (takeScreenshot) {
if (!pRenderer->SaveBufferToImage())
std::cout << "Screenshot saved!" << std::endl;
else