diff --git a/src/color_space.cpp b/src/color_space.cpp index 36c1acf..f0d8ed5 100644 --- a/src/color_space.cpp +++ b/src/color_space.cpp @@ -12,12 +12,20 @@ ColorSpace::~ColorSpace() void ColorSpace::DrawScene() { - - + for (unsigned short x = 0; x < width; x++) { + for (unsigned short y = 0; y < height; y++) { + SetPixel(x, y, color{ + static_cast(y * 255. / height), + static_cast(x * 255. / width), + 0 + }); + } + } } -void ColorSpace::SetPixel(unsigned short x, unsigned short y, color color) -{ - +void ColorSpace::SetPixel(unsigned short x, unsigned short y, color color) { + auto ind = x + y * width; + if (ind >= 0 && ind < frame_buffer.size()) + frame_buffer[ind] = color; } diff --git a/src/draw_line.cpp b/src/draw_line.cpp index 5cbc211..88d6fb3 100644 --- a/src/draw_line.cpp +++ b/src/draw_line.cpp @@ -12,18 +12,69 @@ LineDrawing::LineDrawing(unsigned short width, unsigned short height): ColorSpac } -LineDrawing::~LineDrawing() -{ +LineDrawing::~LineDrawing() { } -void LineDrawing::DrawLine(unsigned short x_begin, unsigned short y_begin, unsigned short x_end, unsigned short y_end, color color) -{ +template +int sign(T x) { + if (x < 0) return -1; + if (x > 0) return 1; + return 0; +} + +void LineDrawing::DrawLine(unsigned short x_begin, unsigned short y_begin, unsigned short x_end, unsigned short y_end, + color color) { + bool rotate = abs(x_end - x_begin) < abs(y_begin - y_end); + + if (rotate) { + std::swap(x_begin, y_begin); + std::swap(x_end, y_end); + } + if (x_end < x_begin) { + std::swap(x_begin, x_end); + std::swap(y_begin, y_end); + } + + + auto deltax = static_cast(x_end - x_begin); + auto deltay = static_cast(y_end - y_begin); + float deltaerr = abs(deltay / deltax); + float error = 0.0; + int y = y_begin; + for (auto x = x_begin; x <= x_end; x++) { + if (rotate) + SetPixel(y, x, color); + else + SetPixel(x, y, color); + error += deltaerr; + if (error >= 0.5) { + y += sign(deltay); + error -= 1.f; + } + } } -void LineDrawing::DrawScene() -{ +void LineDrawing::DrawScene() { + + for (auto angle = 0; angle < 360; angle += 5) { + int radius = std::min(width, height) / 2 - 40; + + int x_center = width / 2; + int y_center = height / 2; + auto x_end = static_cast(cos(angle * M_PI / 180.) * radius + x_center); + auto y_end = static_cast(sin(angle * M_PI / 180.) * radius + y_center); + DrawLine(static_cast(x_center), + static_cast(y_center), + static_cast(x_end), + static_cast(y_end), + color{ + static_cast(255 * sin(angle * M_PI / 180.0)), + static_cast(255 * cos(angle * M_PI / 180.0)), + 255 + }); + } } diff --git a/src/projection.cpp b/src/projection.cpp index e2a6197..81d610e 100644 --- a/src/projection.cpp +++ b/src/projection.cpp @@ -14,9 +14,88 @@ Projection::~Projection() { } -void Projection::DrawScene() -{ +void Projection::DrawScene() { + parser->Parse(); + auto faces = parser->GetFaces(); + + + float3 eye = {0, 0, 2}; + float3 at{0, 0, 0}; + float3 up{0, 1, 0}; + + for (auto &f: project(faces, getWorld(), getView(eye, at, up), getProjection())) { + DrawTriangle(f); + } +} + +float4x4 Projection::getProjection() { + float radius = static_cast(std::min(width, height) / 2); + float zf = 100; + float zn = 1; + float fov_coef = 1 / 0.8; + + return { + {radius / fov_coef, 0, 0, 0}, + {0, radius / fov_coef, 0, 0}, + {0, 0, (zf + zn) / (zf - zn), zf * zn / (zn - zf)}, + {0, 0, -1, 0} + }; +} + +float4x4 Projection::getView(float3 eye, float3 at, float3 up) { + float3 z = linalg::normalize(eye - at); + float3 x = linalg::normalize(linalg::cross(up, z)); + float3 y = linalg::cross(z, x); + + return linalg::transpose(float4x4{ + {x, -linalg::dot(x, eye)}, + {y, -linalg::dot(y, eye)}, + {z, -linalg::dot(z, eye)}, + {0, 0, 0, 1} + }); +} +float4x4 Projection::getWorld() { + return linalg::identity_t(4); } +std::vector Projection::project(std::vector &faces, float4x4 world, float4x4 view, float4x4 projection) { + std::vector proj; + auto transform = mul(projection, view, world); + for (auto f: faces) { + float4 v[3]; + + for (int i = 0; i < 3; i++) { + v[i] = mul(transform, f.vertexes[i]); + } + proj.emplace_back(); + std::copy_n(v, 3, proj.back().vertexes); + } + + return proj; +} + +void Projection::DrawTriangle(face f) { + linalg::vec center{static_cast(width/2), static_cast(height/2)}; + + + DrawLine(center.x + f.vertexes[0].x / f.vertexes[0].w, + center.y + f.vertexes[0].y / f.vertexes[0].w, + center.x + f.vertexes[1].x / f.vertexes[1].w, + center.y + f.vertexes[1].y / f.vertexes[1].w, + color{255, 0, 0}); + + DrawLine(center.x + f.vertexes[1].x / f.vertexes[1].w, + center.y + f.vertexes[1].y / f.vertexes[1].w, + center.x + f.vertexes[2].x / f.vertexes[2].w, + center.y + f.vertexes[2].y / f.vertexes[2].w, + color{0, 255, 0}); + + DrawLine(center.x + f.vertexes[2].x / f.vertexes[2].w, + center.y + f.vertexes[2].y / f.vertexes[2].w, + center.x + f.vertexes[0].x / f.vertexes[0].w, + center.y + f.vertexes[0].y / f.vertexes[0].w, + color{0, 0, 255}); + +} diff --git a/src/projection.h b/src/projection.h index 066bd3f..ef3b4a6 100644 --- a/src/projection.h +++ b/src/projection.h @@ -12,4 +12,12 @@ class Projection: public ReadObj void DrawScene(); +protected: + virtual float4x4 getWorld(); + virtual float4x4 getView(float3 eye, float3 at, float3 up); + virtual float4x4 getProjection(); + virtual std::vector project(std::vector &faces, float4x4 world, float4x4 view, float4x4 projection); + +private: + void DrawTriangle(face f); }; diff --git a/src/read_obj.cpp b/src/read_obj.cpp index deb6c91..3eee122 100644 --- a/src/read_obj.cpp +++ b/src/read_obj.cpp @@ -20,8 +20,55 @@ ObjParser::~ObjParser() int ObjParser::Parse() { - - return 0; + std::ifstream file(filename, std::ifstream::in); + + if (file.fail()) + return -1; + + std::string line; + while (std::getline(file, line)) { + if (line.rfind("v ", 0) == 0) { + auto tokens = Split(line, ' '); + + float4 ver{0, 0, 0, 1}; + for (auto i = 1; i < tokens.size(); i++) { + if (!tokens[i].empty()) { + float v = std::stof(tokens[i]); + ver[i - 1] = v; + } + } + vertexes.push_back(ver); + } else if (line.rfind("f ", 0) == 0) { + auto tokens = Split(line, ' '); + + float4 first; + auto ind0 = std::stoi(Split(tokens[1], '/')[0]); + first = vertexes[vertexes.size() + ind0]; + + float4 last; + bool was = false; + float4 curr; + + for (auto i = 2; i < tokens.size(); i++) { + if (!tokens[i].empty()) { + auto ind = std::stoi(Split(tokens[i], '/')[0]); + + curr = vertexes[vertexes.size() + ind]; + + if (was) { + faces.emplace_back({ + first, last, curr + }); + } + last = curr; + was = true; + } + } + + } + } + + return 0; } const std::vector& ObjParser::GetFaces() @@ -42,18 +89,41 @@ std::vector ObjParser::Split(const std::string& s, char delimiter) } -ReadObj::ReadObj(unsigned short width, unsigned short height, std::string obj_file): LineDrawing(width, height) -{ - parser = new ObjParser(obj_file); +ReadObj::ReadObj(unsigned short width, unsigned short height, std::string obj_file) : LineDrawing(width, height) { + parser = new ObjParser(obj_file); } -ReadObj::~ReadObj() -{ - delete parser; +ReadObj::~ReadObj() { + delete parser; } -void ReadObj::DrawScene() -{ +void ReadObj::DrawTriangle(face const &f, color c) { + auto x_center = width / 2; + auto y_center = height / 2; + auto radius = std::min(width, height) / 2; + + float4 vertices[3]; + + for (int i = 0; i < 3; i++) { + vertices[i] = float4{ + f.vertexes[i].x * radius + x_center, + f.vertexes[i].y * radius + y_center, + 0, + 1 + }; + } + + DrawLine(vertices[0].x, vertices[0].y, vertices[1].x, vertices[1].y, color{255, 0, 0}); + DrawLine(vertices[0].x, vertices[0].y, vertices[2].x, vertices[2].y, color{0, 0, 255}); + DrawLine(vertices[2].x, vertices[2].y, vertices[1].x, vertices[1].y, color{0, 255, 0}); +} + +void ReadObj::DrawScene() { + parser->Parse(); + auto faces = parser->GetFaces(); + for (auto const &f: faces) { + DrawTriangle(f, color{255, 255, 255}); + } } diff --git a/src/read_obj.h b/src/read_obj.h index ca0b193..02d6501 100644 --- a/src/read_obj.h +++ b/src/read_obj.h @@ -34,4 +34,6 @@ class ReadObj: public LineDrawing protected: ObjParser* parser; + + void DrawTriangle(face const &f, color c); }; diff --git a/src/triangle_rasterization.cpp b/src/triangle_rasterization.cpp index ce63524..a17c8ed 100644 --- a/src/triangle_rasterization.cpp +++ b/src/triangle_rasterization.cpp @@ -16,16 +16,72 @@ TriangleRasterization::~TriangleRasterization() void TriangleRasterization::DrawScene() { + parser->Parse(); + auto faces = parser->GetFaces(); + float2 center = {float(width) / 2, float(height) / 2}; + + float3 eye = {0, 0, 2}; + float3 at{0, 0, 0}; + float3 up{0, 1, 0}; + auto proj = project(faces, getWorld(), getView(eye, at, up), getProjection()); + + for (auto &f: proj) { + + float2 triangle[3]; + for (int i = 0; i < 3; i++) { + triangle[i] = f.vertexes[i].xy() / f.vertexes[i].w + center; + } + + DrawTriangle(triangle); + } + + for (auto &f: proj) { + + float2 triangle[3]; + for (int i = 0; i < 3; i++) { + triangle[i] = f.vertexes[i].xy() / f.vertexes[i].w + center; + } + DrawLine(triangle[0].x, triangle[0].y, triangle[1].x, triangle[1].y, color{255, 255, 255}); + DrawLine(triangle[1].x, triangle[1].y, triangle[2].x, triangle[2].y, color{255, 255, 255}); + DrawLine(triangle[2].x, triangle[2].y, triangle[0].x, triangle[0].y, color{255, 255, 255}); + } } -void TriangleRasterization::DrawTriangle(float4 triangle[3]) -{ - +void TriangleRasterization::DrawTriangle(float2 triangle[3]) { + float2 begin{ + std::min(triangle[0].x, std::min(triangle[1].x, triangle[2].x)), + std::min(triangle[0].y, std::min(triangle[1].y, triangle[2].y)) + }; + float2 end{ + std::max(triangle[0].x, std::max(triangle[1].x, triangle[2].x)), + std::max(triangle[0].y, std::max(triangle[1].y, triangle[2].y)) + }; + + auto ef = EdgeFunction(triangle[0], triangle[1], triangle[2]); + + for (int x = begin.x; x < int(ceil(end.x)); x++) { + for (int y = begin.y; y < int(ceil(end.y)); y++) { + auto ef01 = EdgeFunction({ static_cast(x), static_cast(y)}, triangle[0], triangle[1]); + auto ef12 = EdgeFunction({static_cast(x), static_cast(y)}, triangle[1], triangle[2]); + auto ef20 = EdgeFunction({ static_cast(x), static_cast(y)}, triangle[2], triangle[0]); + + auto w0 = std::abs(ef12 / ef); + auto w1 = std::abs(ef20 / ef); + auto w2 = std::abs(ef01 / ef); + + if ((ef01 >= 0 && ef12 >= 0 && ef20 >= 0) || (ef01 <= 0 && ef12 <= 0 && ef20 <= 0)) { + SetPixel(x, y, color{ + 255, 0, 0 + }); + } + } + } } float TriangleRasterization::EdgeFunction(float2 a, float2 b, float2 c) { + return (a.x - b.x) * (c.y - b.y) - (a.y - b.y) * (c.x - b.x); } diff --git a/src/triangle_rasterization.h b/src/triangle_rasterization.h index 7937b3e..7d42264 100644 --- a/src/triangle_rasterization.h +++ b/src/triangle_rasterization.h @@ -13,7 +13,7 @@ class TriangleRasterization: public Projection void DrawScene(); protected: - void DrawTriangle(float4 traingle[3]); + void DrawTriangle(float2 traingle[3]); float EdgeFunction(float2 a, float2 b, float2 c); }; diff --git a/src/z_buffer_culling.cpp b/src/z_buffer_culling.cpp index ceb04db..fc5be9d 100644 --- a/src/z_buffer_culling.cpp +++ b/src/z_buffer_culling.cpp @@ -6,32 +6,138 @@ #include -ZCulling::ZCulling(unsigned short width, unsigned short height, std::string obj_file) : TriangleRasterization(width, height, obj_file) +ZCulling::ZCulling(unsigned short width, unsigned short height, std::string obj_file) + : TriangleRasterization(width, + height, + obj_file), + depth_buffer(static_cast(width) * static_cast(height)) { } -ZCulling::~ZCulling() -{ +ZCulling::~ZCulling() { } -void ZCulling::DrawScene() -{ +void ZCulling::Clear() { + BlackImage::Clear(); + for (auto &z: depth_buffer) + z = INFINITY; } -void ZCulling::Clear() -{ +bool ZCulling::TestDepth(unsigned short x, unsigned short y, float depth) { + auto ind = y * width + x; + if (ind < 0 || ind >= depth_buffer.size()) + return false; + + if (depth_buffer[ind] > depth) { + depth_buffer[ind] = depth; + + return true; + } + return false; } -void ZCulling::DrawTriangle(float4 triangle[3]) -{ +std::vector ZCulling::clone(std::vector &what, float dx, float dy, float dz) { + return project(what, { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {dx, dy, dz, 1} + }, linalg::identity_t(4), linalg::identity_t(4)); +} + +void ZCulling::DrawScene() { + parser->Parse(); + auto faces = parser->GetFaces(); + + float2 center = {float(width) / 2, float(height) / 2}; + + std::vector> to_append; + + to_append.push_back(clone(faces, -2, -2, 0)); + to_append.push_back(clone(faces, -2, 2, 0)); + to_append.push_back(clone(faces, 2, -2, 0)); + to_append.push_back(clone(faces, 2, 2, 0)); + + for (auto &append: to_append) + for (auto f: append) + faces.push_back(f); + float3 eye = {5, 0, 5}; + float3 at{0, 1, 0}; + float3 up{-.75, -1, 0}; + face c_plane1{ + {{-2, -2, 0, 1}, + {2, -2, 0, 1}, + {2, 2, 0, 1}} + }; + faces.push_back(c_plane1); + face c_plane2{ + {{-2, -2, 0, 1}, + {-2, 2, 0, 1}, + {2, 2, 0, 1}} + }; + faces.push_back(c_plane2); + + + auto proj = project(faces, getWorld(), getView(eye, at, up), getProjection()); + for (auto &f: proj) { + + float3 triangle[3]; + for (int i = 0; i < 3; i++) { + triangle[i] = f.vertexes[i].xyz() / f.vertexes[i].w + float3{center, 0}; + } + + DrawTriangle(triangle); + } } -void ZCulling::SetDepth(unsigned short x, unsigned short y, float depth) -{ +float4x4 ZCulling::getWorld() { + return { + {1, 0, 0, 0}, + {0, 1, 0, 0}, + {0, 0, 1, 0}, + {0, 0, 0, 1} + }; } +void ZCulling::DrawTriangle(float3 triangle[3]) { + float2 begin{ + std::min(triangle[0].x, std::min(triangle[1].x, triangle[2].x)), + std::min(triangle[0].y, std::min(triangle[1].y, triangle[2].y)) + }; + float2 end{ + std::max(triangle[0].x, std::max(triangle[1].x, triangle[2].x)), + std::max(triangle[0].y, std::max(triangle[1].y, triangle[2].y)) + }; + + auto ef = EdgeFunction(triangle[0].xy(), triangle[1].xy(), triangle[2].xy()); + + for (int x = begin.x; x < int(ceil(end.x)); x++) { + for (int y = begin.y; y < int(ceil(end.y)); y++) { + auto ef01 = EdgeFunction({ static_cast(x), static_cast(y)}, triangle[0].xy(), triangle[1].xy()); + auto ef12 = EdgeFunction({ static_cast(x), static_cast(y)}, triangle[1].xy(), triangle[2].xy()); + auto ef20 = EdgeFunction({ static_cast(x), static_cast(y)}, triangle[2].xy(), triangle[0].xy()); + + auto w0 = std::abs(ef12 / ef); + auto w1 = std::abs(ef20 / ef); + auto w2 = std::abs(ef01 / ef); + + if ((ef01 >= 0 && ef12 >= 0 && ef20 >= 0) || (ef01 <= 0 && ef12 <= 0 && ef20 <= 0)) { + + auto z = w0 * triangle[0].z + w1 * triangle[1].z + w2 * triangle[2].z; + + if (TestDepth(x, y, z)) { + SetPixel(x, y, color{ + static_cast(w0 * 255), + static_cast(w1 * 255), + static_cast(w2 * 255), + }); + } + } + } + } +} diff --git a/src/z_buffer_culling.h b/src/z_buffer_culling.h index b7df9b0..33b297f 100644 --- a/src/z_buffer_culling.h +++ b/src/z_buffer_culling.h @@ -14,7 +14,12 @@ class ZCulling: public TriangleRasterization void Clear(); protected: - void DrawTriangle(float4 triangle[3]); - void SetDepth(unsigned short x, unsigned short y, float depth); + void DrawTriangle(float3 triangle[3]); + bool TestDepth(unsigned short x, unsigned short y, float depth); std::vector depth_buffer; + + float4x4 getWorld() override; + +private: + std::vector clone(std::vector &what, float dx, float dy, float dz); };