From 5b8a65b15a37d9c1c97fb39db93f1b40db628d70 Mon Sep 17 00:00:00 2001 From: vimene Date: Sun, 19 Nov 2023 07:11:23 +0100 Subject: improved pixel window rendering Pixel window rendering is now at the same state as the character rendering. --- Makefile | 7 ++- main.cpp | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---- pixfb.cpp | 153 ++++++++++++++++++++++++++++++++++++++++++++++ pixfb.h | 29 +++++++++ 4 files changed, 378 insertions(+), 14 deletions(-) create mode 100644 pixfb.cpp create mode 100644 pixfb.h diff --git a/Makefile b/Makefile index 4ba6469..0de5723 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ EXE=main -OBJECT_FILES=main.o math_vector.o chfb.o obj3d.o vertex_data.o vertex.o tri_vertex.o +OBJECT_FILES=main.o math_vector.o chfb.o pixfb.o obj3d.o vertex_data.o vertex.o tri_vertex.o CC=g++ LD=g++ COMMON_ARGS=-Wall -Wextra @@ -11,7 +11,7 @@ LD_ARGS=$(COMMON_ARGS) -lncurses -lSDL2 $(EXE): $(OBJECT_FILES) $(LD) -o $@ $^ $(LD_ARGS) -main.o: main.cpp chfb.h obj3d.h math_vector.h vertex_data.h vertex.h tri_vertex.h +main.o: main.cpp chfb.h pixfb.h obj3d.h math_vector.h vertex_data.h vertex.h tri_vertex.h $(CC) -o $@ -c $< $(CC_ARGS) math_vector.o: math_vector.cpp math_vector.h @@ -20,6 +20,9 @@ math_vector.o: math_vector.cpp math_vector.h chfb.o: chfb.cpp chfb.h math_vector.h vertex_data.h vertex.h tri_vertex.h $(CC) -o $@ -c $< $(CC_ARGS) +pixfb.o: pixfb.cpp pixfb.h math_vector.h vertex_data.h vertex.h tri_vertex.h + $(CC) -o $@ -c $< $(CC_ARGS) + obj3d.o: obj3d.cpp obj3d.h math_vector.h vertex_data.h vertex.h tri_vertex.h $(CC) -o $@ -c $< $(CC_ARGS) diff --git a/main.cpp b/main.cpp index eb8f117..732588a 100644 --- a/main.cpp +++ b/main.cpp @@ -5,6 +5,7 @@ #include #include #include "chfb.h" +#include "pixfb.h" #include "obj3d.h" #include "math_vector.h" #include "vertex.h" @@ -277,21 +278,199 @@ void main_SDL() { } renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, SCREEN_WIDTH, SCREEN_HEIGHT); - std::vector pixels(SCREEN_WIDTH * SCREEN_HEIGHT); - for (int y = 0; y < SCREEN_HEIGHT; y++) { - for (int x = 0; x < SCREEN_WIDTH; x++) { - pixels[x + y * SCREEN_WIDTH] = ((uint32_t) (x + y * SCREEN_WIDTH) << 8) | 0xFF; - } - } - SDL_UpdateTexture(texture, NULL, pixels.data(), SCREEN_WIDTH * 4); - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, texture, NULL, NULL); - SDL_RenderPresent(renderer); - SDL_UpdateWindowSurface(window); + PixelFrameBuffer pfb{SCREEN_WIDTH, SCREEN_HEIGHT}; + + MathVector3 a{0.f, 0.f, 0.f}; + float dist = 4.f; SDL_Event e; bool quit = false; while (!quit) { - while (SDL_PollEvent(&e)) { + a.x += .0050f; + a.y += .0065f; + a.z += .0080f; + + float rad = 5.f; + MathVector3 ca{std::cos(a.x), std::cos(a.y), std::cos(a.z)}; + MathVector3 sa{std::sin(a.x), std::sin(a.y), std::sin(a.z)}; + + std::array rot_x = {{ + { 1.f, 0.f, 0.f }, + { 0.f, ca.x, sa.x }, + { 0.f, -sa.x, ca.x }, + }}; + std::array rot_y = {{ + { ca.y, 0.f, -sa.y }, + { 0.f, 1.f, 0.f }, + { sa.y, 0.f, ca.y }, + }}; + std::array rot_z = {{ + { ca.z, sa.z, 0.f }, + { -sa.z, ca.z, 0.f }, + { 0.f, 0.f, 1.f }, + }}; + + auto [e_x, e_y, e_z] = rot_x; + e_x = e_x.x * rot_y[0] + e_x.y * rot_y[1] + e_x.z * rot_y[2]; + e_y = e_y.x * rot_y[0] + e_y.y * rot_y[1] + e_y.z * rot_y[2]; + e_z = e_z.x * rot_y[0] + e_z.y * rot_y[1] + e_z.z * rot_y[2]; + e_x = e_x.x * rot_z[0] + e_x.y * rot_z[1] + e_x.z * rot_z[2]; + e_y = e_y.x * rot_z[0] + e_y.y * rot_z[1] + e_y.z * rot_z[2]; + e_z = e_z.x * rot_z[0] + e_z.y * rot_z[1] + e_z.z * rot_z[2]; + + std::array objs{{ + { + { + { + rad * (-e_x + -e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + -e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + +e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + +e_y + -e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + -e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + -e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + +e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + +e_y + +e_z - .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + } + }, + { + // face 1 + { 0, 2, 3 }, + { 0, 3, 1 }, + + // face 2 + { 0, 4, 6 }, + { 0, 6, 2 }, + + // face 3 + { 0, 1, 5 }, + { 0, 5, 4 }, + + // face 4 + { 7, 6, 4 }, + { 7, 4, 5 }, + + // face 5 + { 7, 3, 2 }, + { 7, 2, 6 }, + + // face 6 + { 7, 5, 1 }, + { 7, 1, 3 }, + } + }, + { + { + { + rad * (-e_x + -e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + -e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + +e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + +e_y + -e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + -e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + -e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (-e_x + +e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + }, + { + rad * (+e_x + +e_y + +e_z + .5f * (e_x + e_y + e_z)) - dist * rad * MathVector3{0.f, 0.f, 1.f}, + {} + } + }, + { + // face 1 + { 0, 2, 3 }, + { 0, 3, 1 }, + + // face 2 + { 0, 4, 6 }, + { 0, 6, 2 }, + + // face 3 + { 0, 1, 5 }, + { 0, 5, 4 }, + + // face 4 + { 7, 6, 4 }, + { 7, 4, 5 }, + + // face 5 + { 7, 3, 2 }, + { 7, 2, 6 }, + + // face 6 + { 7, 5, 1 }, + { 7, 1, 3 }, + } + } + }}; + pfb.clear(); + float min_z = 2.f, max_z = 50.f; + float fac_for_aspect_ratio = 2.f * static_cast(pfb.height()) / static_cast(pfb.width()); + for (auto obj : objs) { + for (auto triangle : obj) { + TriangleVertex4 t{triangle}; + + // should be multiplied by a matrix, temporary replacement + t.vertex1.point.x *= fac_for_aspect_ratio; + t.vertex1.point.y = -t.vertex1.point.y; + t.vertex1.point.w = -t.vertex1.point.z; + t.vertex1.point.z = 2.f * (-t.vertex1.point.z - min_z) / (max_z - min_z) - 1.f; + t.vertex2.point.x *= fac_for_aspect_ratio; + t.vertex2.point.y = -t.vertex2.point.y; + t.vertex2.point.w = -t.vertex2.point.z; + t.vertex2.point.z = 2.f * (-t.vertex2.point.z - min_z) / (max_z - min_z) - 1.f; + t.vertex3.point.x *= fac_for_aspect_ratio; + t.vertex3.point.y = -t.vertex3.point.y; + t.vertex3.point.w = -t.vertex3.point.z; + t.vertex3.point.z = 2.f * (-t.vertex3.point.z - min_z) / (max_z - min_z) - 1.f; + + pfb.draw_triangle(t); + } + } + SDL_UpdateTexture(texture, NULL, pfb.pixels(), SCREEN_WIDTH * 4); + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); + SDL_UpdateWindowSurface(window); + while (SDL_WaitEventTimeout(&e, 10)) { if (e.type == SDL_QUIT) { quit = true; } diff --git a/pixfb.cpp b/pixfb.cpp new file mode 100644 index 0000000..f84bf2f --- /dev/null +++ b/pixfb.cpp @@ -0,0 +1,153 @@ +#include "pixfb.h" +#include +#include +#include +#include +#include "math_vector.h" +#include "vertex.h" +#include "tri_vertex.h" +#include "vertex_data.h" + +PixelFrameBuffer::PixelFrameBuffer(unsigned int w, unsigned int h) { + resize(w, h); +} + +void PixelFrameBuffer::resize(unsigned int w, unsigned int h) { + this->w = w; + this->h = h; + pixels_vector.resize(w * h); + depth_buf.resize(w * h); + clear(); +} + +unsigned int PixelFrameBuffer::width() const { + return w; +} + +unsigned int PixelFrameBuffer::height() const { + return h; +} + +const uint32_t* PixelFrameBuffer::pixels() const { + return pixels_vector.data(); +} + +void PixelFrameBuffer::clear() { + std::fill(pixels_vector.begin(), pixels_vector.end(), 0x000000FF); + std::fill(depth_buf.begin(), depth_buf.end(), 1.f); + face_ind = -1; +} + +void PixelFrameBuffer::_draw_cropped_triangle(TriangleVertex3 triangle) { + std::array sorted_vs = { &triangle.vertex1, &triangle.vertex2, &triangle.vertex3 }; + +#define SWAP_IF_LT(X,Y) ({\ + if (sorted_vs[X]->point.y < sorted_vs[Y]->point.y) {\ + Vertex3* temp = sorted_vs[X];\ + sorted_vs[X] = sorted_vs[Y];\ + sorted_vs[Y] = temp;\ + }\ + }) + SWAP_IF_LT(1, 0); + SWAP_IF_LT(2, 1); + SWAP_IF_LT(1, 0); +#undef SWAP_IF_LT + + Vertex3 middle_vl = *sorted_vs[1]; + float fac = (sorted_vs[1]->point.y - sorted_vs[0]->point.y) / (sorted_vs[2]->point.y - sorted_vs[0]->point.y); + Vertex3 middle_vr{ + { + sorted_vs[0]->point.x + fac * (sorted_vs[2]->point.x - sorted_vs[0]->point.x), + sorted_vs[1]->point.y, + sorted_vs[0]->point.z + fac * (sorted_vs[2]->point.z - sorted_vs[0]->point.z) + }, + VertexData::lerp(sorted_vs[0]->data, sorted_vs[2]->data, fac) + }; + if (middle_vr.point.x < middle_vl.point.x) { + Vertex3 temp = middle_vr; + middle_vr = middle_vl; + middle_vl = temp; + } + + // top triangle + { + if (sorted_vs[0]->point.y != sorted_vs[1]->point.y) { + int top_y = static_cast(std::floor(sorted_vs[0]->point.y + 1.f)); + int bottom_y = static_cast(std::ceil(sorted_vs[1]->point.y - 1.f)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast(y); + float s = (iy - sorted_vs[0]->point.y) / (sorted_vs[1]->point.y - sorted_vs[0]->point.y); + float xl = sorted_vs[0]->point.x + s * (middle_vl.point.x - sorted_vs[0]->point.x); + float xr = sorted_vs[0]->point.x + s * (middle_vr.point.x - sorted_vs[0]->point.x); + int left_x = static_cast(std::ceil(xl)); + int right_x = static_cast(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float depth = sorted_vs[0]->point.z + s * (middle_vl.point.z - sorted_vs[0]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); + // VertexData vd = VertexData::bilerp(sorted_vs[0]->data, middle_vl.data, middle_vr.data, s, t); + if (depth < depth_buf[x + y * w]) { + depth_buf[x + y * w] = depth; + pixels_vector[x + y * w] = face_color(); + } + } + } + } + } + + // bottom triangle + { + if (sorted_vs[1]->point.y != sorted_vs[2]->point.y) { + int bottom_y = static_cast(std::floor(sorted_vs[2]->point.y)); + int top_y = static_cast(std::ceil(sorted_vs[1]->point.y)); + for (int y = top_y; y <= bottom_y; y++) { + float iy = static_cast(y); + float s = (iy - sorted_vs[2]->point.y) / (sorted_vs[1]->point.y - sorted_vs[2]->point.y); + float xl = sorted_vs[2]->point.x + s * (middle_vl.point.x - sorted_vs[2]->point.x); + float xr = sorted_vs[2]->point.x + s * (middle_vr.point.x - sorted_vs[2]->point.x); + int left_x = static_cast(std::ceil(xl)); + int right_x = static_cast(std::ceil(xr - 1.f)); + for (int x = left_x; x <= right_x; x++) { + float ix = static_cast(x); + float t = (ix - xl) / (xr - xl); + // depth and vd don't take into account perspective + float depth = sorted_vs[2]->point.z + s * (middle_vl.point.z - sorted_vs[2]->point.z + t * (middle_vr.point.z - middle_vl.point.z)); + // VertexData vd = VertexData::bilerp(sorted_vs[2]->data, middle_vl.data, middle_vr.data, s, t); + if (depth < depth_buf[x + y * w]) { + depth_buf[x + y * w] = depth; + pixels_vector[x + y * w] = face_color(); + } + } + } + } + } +} + +void PixelFrameBuffer::draw_triangle(TriangleVertex4 triangle) { + face_ind++; + for (auto t1 : triangle.crop_z_out(-1.f, 1.f)) { + for (auto t2 : t1.div_by_w().crop_xy_out(-1.f, 1.f, -1.f, 1.f)) { + MathVector2 pp1 = t2.vertex1.point.xy(), + pp2 = t2.vertex2.point.xy(), + pp3 = t2.vertex3.point.xy(); + if ((pp2 - pp1).det(pp3 - pp1) >= 0.f) continue; + t2.vertex1.point = (t2.vertex1.point + MathVector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex2.point = (t2.vertex2.point + MathVector3{1.f, 1.f, 0.f}) / 2.f; + t2.vertex3.point = (t2.vertex3.point + MathVector3{1.f, 1.f, 0.f}) / 2.f; + float fw = static_cast(w), fh = static_cast(h); + t2.vertex1.point.x = t2.vertex1.point.x * fw - .5f; + t2.vertex1.point.y = t2.vertex1.point.y * fh - .5f; + t2.vertex2.point.x = t2.vertex2.point.x * fw - .5f; + t2.vertex2.point.y = t2.vertex2.point.y * fh - .5f; + t2.vertex3.point.x = t2.vertex3.point.x * fw - .5f; + t2.vertex3.point.y = t2.vertex3.point.y * fh - .5f; + _draw_cropped_triangle(t2); + } + } +} + +uint32_t PixelFrameBuffer::face_color() const { + int n = face_ind / 2; + return ((n % 2 ? 0xFF000000 : 0x55000000) >> ((n % 6) / 2 * 8)) | 0xFF; +} diff --git a/pixfb.h b/pixfb.h new file mode 100644 index 0000000..c54b5fb --- /dev/null +++ b/pixfb.h @@ -0,0 +1,29 @@ +#ifndef PIXFB_H +#define PIXFB_H + +#include +#include +#include "math_vector.h" +#include "tri_vertex.h" + +class PixelFrameBuffer { + public: + PixelFrameBuffer(unsigned int w, unsigned int h); + void resize(unsigned int w, unsigned int h); + unsigned int width() const; + unsigned int height() const; + const uint32_t* pixels() const; + void clear(); + void draw_triangle(TriangleVertex4 triangle); + + private: + unsigned int w, h; + std::vector pixels_vector; + std::vector depth_buf; + int face_ind; + + void _draw_cropped_triangle(TriangleVertex3 triangle); + uint32_t face_color() const; +}; + +#endif // PIXFB_H -- cgit v1.2.3