diff --git a/Frame/Source/Assignment/Assignment2/Triangle.cpp b/Frame/Source/Assignment/Assignment2/Triangle.cpp new file mode 100644 index 00000000..c61c4638 --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/Triangle.cpp @@ -0,0 +1,51 @@ +// +// Created by LEI XU on 4/11/19. +// + +#include "Triangle.hpp" +#include +#include + + +Triangle::Triangle() { + v[0] << 0,0,0; + v[1] << 0,0,0; + v[2] << 0,0,0; + + color[0] << 0.0, 0.0, 0.0; + color[1] << 0.0, 0.0, 0.0; + color[2] << 0.0, 0.0, 0.0; + + tex_coords[0] << 0.0, 0.0; + tex_coords[1] << 0.0, 0.0; + tex_coords[2] << 0.0, 0.0; +} + +void Triangle::setVertex(int ind, Vector3f ver){ + v[ind] = ver; +} +void Triangle::setNormal(int ind, Vector3f n){ + normal[ind] = n; +} +void Triangle::setColor(int ind, float r, float g, float b) { + if((r<0.0) || (r>255.) || + (g<0.0) || (g>255.) || + (b<0.0) || (b>255.)) { + fprintf(stderr, "ERROR! Invalid color values"); + fflush(stderr); + exit(-1); + } + + color[ind] = Vector3f((float)r/255.,(float)g/255.,(float)b/255.); + return; +} +void Triangle::setTexCoord(int ind, float s, float t) { + tex_coords[ind] = Vector2f(s,t); +} + +std::array Triangle::toVector4() const +{ + std::array res; + std::transform(std::begin(v), std::end(v), res.begin(), [](auto& vec) { return Eigen::Vector4f(vec.x(), vec.y(), vec.z(), 1.f); }); + return res; +} diff --git a/Frame/Source/Assignment/Assignment2/Triangle.hpp b/Frame/Source/Assignment/Assignment2/Triangle.hpp new file mode 100644 index 00000000..0f14436b --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/Triangle.hpp @@ -0,0 +1,37 @@ +// +// Created by LEI XU on 4/11/19. +// + +#ifndef RASTERIZER_TRIANGLE_H +#define RASTERIZER_TRIANGLE_H + +#include + + +using namespace Eigen; +class Triangle{ + +public: + Vector3f v[3]; /*the original coordinates of the triangle, v0, v1, v2 in counter clockwise order*/ + /*Per vertex values*/ + Vector3f color[3]; //color at each vertex; + Vector2f tex_coords[3]; //texture u,v + Vector3f normal[3]; //normal vector for each vertex + + //Texture *tex; + Triangle(); + + void setVertex(int ind, Vector3f ver); /*set i-th vertex coordinates */ + void setNormal(int ind, Vector3f n); /*set i-th vertex normal vector*/ + void setColor(int ind, float r, float g, float b); /*set i-th vertex color*/ + Vector3f getColor() const { return color[0]*255; } // Only one color per triangle. + void setTexCoord(int ind, float s, float t); /*set i-th vertex texture coordinate*/ + std::array toVector4() const; +}; + + + + + + +#endif //RASTERIZER_TRIANGLE_H diff --git a/Frame/Source/Assignment/Assignment2/global.hpp b/Frame/Source/Assignment/Assignment2/global.hpp new file mode 100644 index 00000000..ab87ebc1 --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/global.hpp @@ -0,0 +1,12 @@ +// +// Created by LEI XU on 4/9/19. +// + +#ifndef RASTERIZER_GLOBAL_H +#define RASTERIZER_GLOBAL_H + +//#define MY_PI 3.1415926 +//#define TWO_PI (2.0* MY_PI) + + +#endif //RASTERIZER_GLOBAL_H diff --git a/Frame/Source/Assignment/Assignment2/main.cpp b/Frame/Source/Assignment/Assignment2/main.cpp new file mode 100644 index 00000000..7bb5225d --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/main.cpp @@ -0,0 +1,128 @@ +// clang-format off +#include +#include +#include "rasterizer.hpp" +#include "global.hpp" +#include "Triangle.hpp" + +constexpr double MY_PI = 3.1415926; + +Eigen::Matrix4f get_view_matrix(Eigen::Vector3f eye_pos) +{ + Eigen::Matrix4f view = Eigen::Matrix4f::Identity(); + + Eigen::Matrix4f translate; + translate << 1,0,0,-eye_pos[0], + 0,1,0,-eye_pos[1], + 0,0,1,-eye_pos[2], + 0,0,0,1; + + view = translate*view; + + return view; +} + +Eigen::Matrix4f get_model_matrix(float rotation_angle) +{ + Eigen::Matrix4f model = Eigen::Matrix4f::Identity(); + return model; +} + +Eigen::Matrix4f get_projection_matrix(float eye_fov, float aspect_ratio, float zNear, float zFar) +{ + // TODO: Copy-paste your implementation from the previous assignment. + Eigen::Matrix4f projection; + + return projection; +} + +int main(int argc, const char** argv) +{ + float angle = 0; + bool command_line = false; + std::string filename = "output.png"; + + if (argc == 2) + { + command_line = true; + filename = std::string(argv[1]); + } + + rst::rasterizer r(700, 700); + + Eigen::Vector3f eye_pos = {0,0,5}; + + + std::vector pos + { + {2, 0, -2}, + {0, 2, -2}, + {-2, 0, -2}, + {3.5, -1, -5}, + {2.5, 1.5, -5}, + {-1, 0.5, -5} + }; + + std::vector ind + { + {0, 1, 2}, + {3, 4, 5} + }; + + std::vector cols + { + {217.0, 238.0, 185.0}, + {217.0, 238.0, 185.0}, + {217.0, 238.0, 185.0}, + {185.0, 217.0, 238.0}, + {185.0, 217.0, 238.0}, + {185.0, 217.0, 238.0} + }; + + auto pos_id = r.load_positions(pos); + auto ind_id = r.load_indices(ind); + auto col_id = r.load_colors(cols); + + int key = 0; + int frame_count = 0; + + if (command_line) + { + r.clear(rst::Buffers::Color | rst::Buffers::Depth); + + r.set_model(get_model_matrix(angle)); + r.set_view(get_view_matrix(eye_pos)); + r.set_projection(get_projection_matrix(45, 1, 0.1, 50)); + + r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle); + cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data()); + image.convertTo(image, CV_8UC3, 1.0f); + cv::cvtColor(image, image, cv::COLOR_RGB2BGR); + + cv::imwrite(filename, image); + + return 0; + } + + while(key != 27) + { + r.clear(rst::Buffers::Color | rst::Buffers::Depth); + + r.set_model(get_model_matrix(angle)); + r.set_view(get_view_matrix(eye_pos)); + r.set_projection(get_projection_matrix(45, 1, 0.1, 50)); + + r.draw(pos_id, ind_id, col_id, rst::Primitive::Triangle); + + cv::Mat image(700, 700, CV_32FC3, r.frame_buffer().data()); + image.convertTo(image, CV_8UC3, 1.0f); + cv::cvtColor(image, image, cv::COLOR_RGB2BGR); + cv::imshow("image", image); + key = cv::waitKey(10); + + std::cout << "frame count: " << frame_count++ << '\n'; + } + + return 0; +} +// clang-format on \ No newline at end of file diff --git a/Frame/Source/Assignment/Assignment2/rasterizer.cpp b/Frame/Source/Assignment/Assignment2/rasterizer.cpp new file mode 100644 index 00000000..ac151f9a --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/rasterizer.cpp @@ -0,0 +1,167 @@ +// clang-format off +// +// Created by goksu on 4/6/19. +// + +#include +#include +#include "rasterizer.hpp" +#include +#include + + +rst::pos_buf_id rst::rasterizer::load_positions(const std::vector &positions) +{ + auto id = get_next_id(); + pos_buf.emplace(id, positions); + + return {id}; +} + +rst::ind_buf_id rst::rasterizer::load_indices(const std::vector &indices) +{ + auto id = get_next_id(); + ind_buf.emplace(id, indices); + + return {id}; +} + +rst::col_buf_id rst::rasterizer::load_colors(const std::vector &cols) +{ + auto id = get_next_id(); + col_buf.emplace(id, cols); + + return {id}; +} + +auto to_vec4(const Eigen::Vector3f& v3, float w = 1.0f) +{ + return Vector4f(v3.x(), v3.y(), v3.z(), w); +} + + +static bool insideTriangle(int x, int y, const Vector3f* _v) +{ + // TODO : Implement this function to check if the point (x, y) is inside the triangle represented by _v[0], _v[1], _v[2] +} + +static std::tuple computeBarycentric2D(float x, float y, const Vector3f* v) +{ + float c1 = (x*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*y + v[1].x()*v[2].y() - v[2].x()*v[1].y()) / (v[0].x()*(v[1].y() - v[2].y()) + (v[2].x() - v[1].x())*v[0].y() + v[1].x()*v[2].y() - v[2].x()*v[1].y()); + float c2 = (x*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*y + v[2].x()*v[0].y() - v[0].x()*v[2].y()) / (v[1].x()*(v[2].y() - v[0].y()) + (v[0].x() - v[2].x())*v[1].y() + v[2].x()*v[0].y() - v[0].x()*v[2].y()); + float c3 = (x*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*y + v[0].x()*v[1].y() - v[1].x()*v[0].y()) / (v[2].x()*(v[0].y() - v[1].y()) + (v[1].x() - v[0].x())*v[2].y() + v[0].x()*v[1].y() - v[1].x()*v[0].y()); + return {c1,c2,c3}; +} + +void rst::rasterizer::draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type) +{ + auto& buf = pos_buf[pos_buffer.pos_id]; + auto& ind = ind_buf[ind_buffer.ind_id]; + auto& col = col_buf[col_buffer.col_id]; + + float f1 = (50 - 0.1) / 2.0; + float f2 = (50 + 0.1) / 2.0; + + Eigen::Matrix4f mvp = projection * view * model; + for (auto& i : ind) + { + Triangle t; + Eigen::Vector4f v[] = { + mvp * to_vec4(buf[i[0]], 1.0f), + mvp * to_vec4(buf[i[1]], 1.0f), + mvp * to_vec4(buf[i[2]], 1.0f) + }; + //Homogeneous division + for (auto& vec : v) { + vec /= vec.w(); + } + //Viewport transformation + for (auto & vert : v) + { + vert.x() = 0.5*width*(vert.x()+1.0); + vert.y() = 0.5*height*(vert.y()+1.0); + vert.z() = vert.z() * f1 + f2; + } + + for (int i = 0; i < 3; ++i) + { + t.setVertex(i, v[i].head<3>()); + t.setVertex(i, v[i].head<3>()); + t.setVertex(i, v[i].head<3>()); + } + + auto col_x = col[i[0]]; + auto col_y = col[i[1]]; + auto col_z = col[i[2]]; + + t.setColor(0, col_x[0], col_x[1], col_x[2]); + t.setColor(1, col_y[0], col_y[1], col_y[2]); + t.setColor(2, col_z[0], col_z[1], col_z[2]); + + rasterize_triangle(t); + } +} + +//Screen space rasterization +void rst::rasterizer::rasterize_triangle(const Triangle& t) { + auto v = t.toVector4(); + + // TODO : Find out the bounding box of current triangle. + // iterate through the pixel and find if the current pixel is inside the triangle + + // If so, use the following code to get the interpolated z value. + //auto[alpha, beta, gamma] = computeBarycentric2D(x, y, t.v); + //float w_reciprocal = 1.0/(alpha / v[0].w() + beta / v[1].w() + gamma / v[2].w()); + //float z_interpolated = alpha * v[0].z() / v[0].w() + beta * v[1].z() / v[1].w() + gamma * v[2].z() / v[2].w(); + //z_interpolated *= w_reciprocal; + + // TODO : set the current pixel (use the set_pixel function) to the color of the triangle (use getColor function) if it should be painted. +} + +void rst::rasterizer::set_model(const Eigen::Matrix4f& m) +{ + model = m; +} + +void rst::rasterizer::set_view(const Eigen::Matrix4f& v) +{ + view = v; +} + +void rst::rasterizer::set_projection(const Eigen::Matrix4f& p) +{ + projection = p; +} + +void rst::rasterizer::clear(rst::Buffers buff) +{ + if ((buff & rst::Buffers::Color) == rst::Buffers::Color) + { + std::fill(frame_buf.begin(), frame_buf.end(), Eigen::Vector3f{0, 0, 0}); + } + if ((buff & rst::Buffers::Depth) == rst::Buffers::Depth) + { + std::fill(depth_buf.begin(), depth_buf.end(), std::numeric_limits::infinity()); + } +} + +rst::rasterizer::rasterizer(int w, int h) : width(w), height(h) +{ + frame_buf.resize(w * h); + depth_buf.resize(w * h); +} + +int rst::rasterizer::get_index(int x, int y) +{ + return (height-1-y)*width + x; +} + +void rst::rasterizer::set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color) +{ + //old index: auto ind = point.y() + point.x() * width; + auto ind = (height-1-point.y())*width + point.x(); + frame_buf[ind] = color; + +} + +// clang-format on \ No newline at end of file diff --git a/Frame/Source/Assignment/Assignment2/rasterizer.hpp b/Frame/Source/Assignment/Assignment2/rasterizer.hpp new file mode 100644 index 00000000..757f544a --- /dev/null +++ b/Frame/Source/Assignment/Assignment2/rasterizer.hpp @@ -0,0 +1,103 @@ +// +// Created by goksu on 4/6/19. +// + +#pragma once + +#include +#include +#include "global.hpp" +#include "Triangle.hpp" +using namespace Eigen; + +namespace rst +{ + enum class Buffers + { + Color = 1, + Depth = 2 + }; + + inline Buffers operator|(Buffers a, Buffers b) + { + return Buffers((int)a | (int)b); + } + + inline Buffers operator&(Buffers a, Buffers b) + { + return Buffers((int)a & (int)b); + } + + enum class Primitive + { + Line, + Triangle + }; + + /* + * For the curious : The draw function takes two buffer id's as its arguments. These two structs + * make sure that if you mix up with their orders, the compiler won't compile it. + * Aka : Type safety + * */ + struct pos_buf_id + { + int pos_id = 0; + }; + + struct ind_buf_id + { + int ind_id = 0; + }; + + struct col_buf_id + { + int col_id = 0; + }; + + class rasterizer + { + public: + rasterizer(int w, int h); + pos_buf_id load_positions(const std::vector& positions); + ind_buf_id load_indices(const std::vector& indices); + col_buf_id load_colors(const std::vector& colors); + + void set_model(const Eigen::Matrix4f& m); + void set_view(const Eigen::Matrix4f& v); + void set_projection(const Eigen::Matrix4f& p); + + void set_pixel(const Eigen::Vector3f& point, const Eigen::Vector3f& color); + + void clear(Buffers buff); + + void draw(pos_buf_id pos_buffer, ind_buf_id ind_buffer, col_buf_id col_buffer, Primitive type); + + std::vector& frame_buffer() { return frame_buf; } + + private: + void draw_line(Eigen::Vector3f begin, Eigen::Vector3f end); + + void rasterize_triangle(const Triangle& t); + + // VERTEX SHADER -> MVP -> Clipping -> /.W -> VIEWPORT -> DRAWLINE/DRAWTRI -> FRAGSHADER + + private: + Eigen::Matrix4f model; + Eigen::Matrix4f view; + Eigen::Matrix4f projection; + + std::map> pos_buf; + std::map> ind_buf; + std::map> col_buf; + + std::vector frame_buf; + + std::vector depth_buf; + int get_index(int x, int y); + + int width, height; + + int next_id = 0; + int get_next_id() { return next_id++; } + }; +} diff --git a/Script/MakeFrame.lua b/Script/MakeFrame.lua index 7719850e..da160fca 100644 --- a/Script/MakeFrame.lua +++ b/Script/MakeFrame.lua @@ -108,3 +108,4 @@ end MakeAssignment("Assignment0") MakeAssignment("Assignment1") +MakeAssignment("Assignment2")