From 0d10b77f77459333c5549711334f417623ab1f3e Mon Sep 17 00:00:00 2001 From: vimene Date: Sat, 17 Jan 2026 00:06:31 +0100 Subject: improved software shaders - unified terminal and graphical software renderer shaders - added vertex shaders for software renderers - removed VertexData - moved shaders from frame buffers to their own class - added FrameBufferConcept and ShadersConcept --- src/Makefile.am | 11 ++-- src/engine.cpp | 112 +++++++++++++++-------------------------- src/fb/chfb.cpp | 13 ++--- src/fb/chfb.hpp | 4 +- src/fb/fb.hpp | 21 ++++++++ src/fb/pixfb.cpp | 37 +++----------- src/fb/pixfb.hpp | 4 +- src/o3d/vertex.hpp | 2 - src/o3d/vertex_data.hpp | 26 ---------- src/obj_parser.cpp | 1 - src/renderer.cpp | 57 ++++++++++++++------- src/renderer.hpp | 13 +++-- src/shaders/shaders.hpp | 24 +++++++++ src/shaders/simple_shaders.cpp | 18 +++++++ src/shaders/simple_shaders.hpp | 83 ++++++++++++++++++++++++++++++ 15 files changed, 254 insertions(+), 172 deletions(-) create mode 100644 src/fb/fb.hpp delete mode 100644 src/o3d/vertex_data.hpp create mode 100644 src/shaders/shaders.hpp create mode 100644 src/shaders/simple_shaders.cpp create mode 100644 src/shaders/simple_shaders.hpp diff --git a/src/Makefile.am b/src/Makefile.am index f7741a6..d014f5a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -10,7 +10,9 @@ engine_SOURCES = \ obj_parser.hpp obj_parser.cpp \ vulkan_utils.hpp \ stb_image.c \ - fb/chfb.hpp fb/chfb.cpp fb/pixfb.hpp fb/pixfb.cpp \ + fb/fb.hpp \ + fb/chfb.hpp fb/chfb.cpp \ + fb/pixfb.hpp fb/pixfb.cpp \ math/utils.hpp \ math/vector.hpp \ math/mat4.hpp \ @@ -18,7 +20,6 @@ engine_SOURCES = \ math/tform.hpp \ o3d/mesh.hpp o3d/mesh.cpp \ o3d/obj3d.hpp \ - o3d/vertex_data.hpp \ o3d/vertex.hpp \ o3d/deriv_vertex.hpp \ o3d/tri.hpp \ @@ -27,7 +28,9 @@ engine_SOURCES = \ o3d/camera.hpp \ o3d/scene.hpp \ ctrl/keyboard.hpp \ - ctrl/mouse.hpp + ctrl/mouse.hpp \ + shaders/shaders.hpp \ + shaders/simple_shaders.hpp shaders/simple_shaders.cpp # TODO: we should somehow pass assetsdir and texturesdir from ../Makefile.am to this Makefile.am. # We assume that they will always be relative to datadir, which might not be true if they are @@ -35,7 +38,7 @@ engine_SOURCES = \ engine_CPPFLAGS = -std=gnu++23 -Wall -Wextra \ -DDATADIR='"$(datadir)"' -DSHADERSDIR='"$(shadersdir)"' \ $(GLFW3_CFLAGS) $(STB_CFLAGS) $(VULKAN_CFLAGS) -engine_LDFLAGS = -std=gnu++23 -Wall -Wextra +engine_LDFLAGS = -std=gnu++23 -Wall -Wextra -fconcepts-diagnostics-depth=10 engine_LDADD = $(GLFW3_LIBS) $(STB_LIBS) $(VULKAN_LIBS) if HAVE_NCURSES diff --git a/src/engine.cpp b/src/engine.cpp index aed0970..305d254 100644 --- a/src/engine.cpp +++ b/src/engine.cpp @@ -21,6 +21,8 @@ #include #include #include +#include +#include #define GLFW_INCLUDE_VULKAN #include @@ -32,12 +34,12 @@ #include #endif +#include "fb/fb.hpp" #include "fb/chfb.hpp" #include "fb/pixfb.hpp" #include "o3d/scene.hpp" #include "o3d/mesh.hpp" #include "o3d/obj3d.hpp" -#include "o3d/vertex_data.hpp" #include "o3d/tri.hpp" #include "o3d/camera.hpp" #include "math/vector.hpp" @@ -46,12 +48,15 @@ #include "math/tform.hpp" #include "ctrl/keyboard.hpp" #include "ctrl/mouse.hpp" +#include "shaders/shaders.hpp" +#include "shaders/simple_shaders.hpp" #include "renderer.hpp" #include "obj_parser.hpp" #include "vulkan_utils.hpp" using engine::Renderer, + engine::fb::FrameBufferConcept, engine::fb::CharacterFrameBuffer, engine::fb::PixelFrameBuffer, engine::o3d::Scene, @@ -59,7 +64,6 @@ using engine::o3d::Mesh, engine::o3d::Triangle, engine::o3d::Camera, - engine::o3d::VertexData, engine::math::Vector2, engine::math::Vector3, engine::math::Vector4, @@ -67,7 +71,9 @@ using engine::math::Quaternion, engine::controllers::Keyboard, engine::controllers::KeyboardKey, - engine::controllers::Mouse; + engine::controllers::Mouse, + engine::shaders::ShadersConcept, + engine::shaders::SimpleShaders; #define FPS 60 @@ -102,9 +108,6 @@ static void usage_error_exit() { exit(EXIT_FAILURE); } -extern Camera* camera; -Camera* camera; - template static void scene_main(const Matrix4& final_transform_mat, Scene& scene, UpdateSurfaceSizeFn update_surface_size, PollEventsFn poll_events, RenderAndPresentFrameFn render_and_present_frame) { @@ -119,8 +122,6 @@ static void scene_main(const Matrix4& final_transform_mat, Scene& scene, if (rx > PI / 2.f) rx = PI / 2.f; } }; - camera = &scene.camera; - auto start_time = std::chrono::high_resolution_clock::now(); auto last_time = start_time; @@ -159,8 +160,8 @@ static void scene_main(const Matrix4& final_transform_mat, Scene& scene, } // TODO: find better name -template -static void render_software(Renderer& renderer, const Matrix4& final_transform_mat, Scene& scene, +template +static void render_software(Renderer& renderer, const Matrix4& final_transform_mat, Scene& scene, PollEventsFn poll_events, UpdateRendererSizeFn update_renderer_size, PresentFrameFn present_frame) { scene_main(final_transform_mat, scene, // update_surface_size @@ -172,33 +173,14 @@ static void render_software(Renderer& renderer, const Matrix4& fina poll_events, // render_and_present_frame - [&](const Matrix4& /* view_mat */, const Matrix4& /* proj_mat */, const Matrix4& /* inv_view_mat */, float /* time */, float /* ellapsed_time */) { + [&](const Matrix4& view_mat, const Matrix4& proj_mat, const Matrix4& /* inv_view_mat */, float /* time */, float /* ellapsed_time */) { // TODO: remove renderer.clear(); - // auto proj_view_mat = proj_mat * view_mat; - // renderer.clear(); - // for (const auto& obj : scene.objs) { - // auto model_mat = obj.transform.to_mat4(); - // auto final_mat = proj_view_mat * model_mat; - // const auto& mesh = obj.mesh; - // std::vector vertices; - // std::vector normals; - // std::vector vertices_data; - // for (const auto& vertex : mesh.vertices) { - // Vector4 vertex4 { vertex, 1.f }; - // vertices.push_back(final_mat * vertex4); - // vertices_data.push_back(VertexData((model_mat * vertex4).xyz())); - // } - // for (const auto& normal : mesh.normals) - // normals.push_back((model_mat * Vector4 { normal, 0.f }).xyz()); - // for (const auto& triangle_indices : mesh.indices) { - // [&](std::index_sequence) { - // renderer.draw_triangle( - // {{vertices[triangle_indices[j][0]], normals[triangle_indices[j][1]], vertices_data[triangle_indices[j][0]]}...} - // ); - // }(std::make_index_sequence<3>()); - // } - // } + renderer.shaders.set_view(view_mat, proj_mat); + for (const auto& obj : scene.objs) { + renderer.shaders.set_model(obj.transform.to_mat4()); + renderer.draw_mesh(obj.mesh); + } present_frame(); } ); @@ -218,10 +200,18 @@ static int main_term(Scene& scene) { set_escdelay(0); curs_set(0); + int texture_w, texture_h, texture_channels; + stbi_uc* texture_pixels = stbi_load(DATADIR "/assets/textures/viking_room.png", &texture_w, &texture_h, &texture_channels, STBI_rgb_alpha); + + if (!texture_pixels) { + std::println(stderr, "failed to load texture image, reason: {}", stbi_failure_reason()); + exit(EXIT_FAILURE); + } + auto renderer = [&] { int w, h; getmaxyx(stdscr, h, w); - return Renderer { CharacterFrameBuffer { static_cast(w), static_cast(h) } }; + return Renderer { CharacterFrameBuffer { static_cast(w), static_cast(h) }, SimpleShaders { texture_w, texture_h, texture_pixels } }; }(); render_software(renderer, Matrix4::scale(Vector3(2.f, 1.f, 1.f)), scene, @@ -311,6 +301,8 @@ static int main_term(Scene& scene) { } ); + stbi_image_free(texture_pixels); + // terminate endwin(); @@ -1672,7 +1664,7 @@ static int main_graphical(Scene& scene) { std::tie(depth_img, depth_img_device_mem, depth_img_view) = create_depth_resources(physical_device, device, swapchain_extent, depth_format); // create texture image - auto [texture_img, texture_img_device_mem] = [&] { + auto [texture_img, texture_img_device_mem, texture_w, texture_h, texture_pixels] = [&] { int w, h, channels; stbi_uc* pixels = stbi_load(DATADIR "/assets/textures/viking_room.png", &w, &h, &channels, STBI_rgb_alpha); @@ -1702,8 +1694,6 @@ static int main_graphical(Scene& scene) { vkUnmapMemory(device, staging_buf_device_mem); } - stbi_image_free(pixels); - auto [texture_img, texture_img_device_mem] = create_img(physical_device, device, static_cast(w), static_cast(h), VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); @@ -1724,7 +1714,7 @@ static int main_graphical(Scene& scene) { vkFreeMemory(device, staging_buf_device_mem, nullptr); vkDestroyBuffer(device, staging_buf, nullptr); - return std::tuple { texture_img, texture_img_device_mem }; + return std::tuple { texture_img, texture_img_device_mem, w, h, pixels }; }(); // create texture image view @@ -2099,11 +2089,10 @@ static int main_graphical(Scene& scene) { std::cout << " ]" << std::endl; // init software renderer - // TODO: resize buffer when surface extent change - Renderer software_renderer { PixelFrameBuffer { - static_cast(swapchain_extent.width), - static_cast(swapchain_extent.height), - } }; + Renderer software_renderer { + PixelFrameBuffer { static_cast(swapchain_extent.width), static_cast(swapchain_extent.height) }, + SimpleShaders { texture_w, texture_h, texture_pixels }, + }; bool first_software_renderer_buf_creation = true; std::array software_renderer_bufs; @@ -2286,6 +2275,9 @@ static int main_graphical(Scene& scene) { } break; case GraphicalRendererMode::software: + { + software_renderer.shaders.set_view(view_mat, proj_mat); + } break; } @@ -2294,35 +2286,10 @@ static int main_graphical(Scene& scene) { break; case GraphicalRendererMode::software: { - auto proj_view_mat = proj_mat * view_mat; software_renderer.clear(); for (const auto& obj : scene.objs) { - auto model_mat = obj.transform.to_mat4(); - auto final_mat = proj_view_mat * model_mat; - const auto& mesh = obj.mesh; - std::vector vertices; - std::vector normals; - std::vector vertices_data; - for (const auto& vertex : mesh.vertices) { - Vector4 vertex4 { vertex, 1.f }; - vertices.push_back(final_mat * vertex4); - vertices_data.push_back(VertexData((model_mat * vertex4).xyz())); - } - for (const auto& normal : mesh.normals) - normals.push_back((model_mat * Vector4 { normal, 0.f }).xyz()); - for (const auto& triangle_indices : mesh.indices) { - software_renderer.draw_triangle([&](std::index_sequence) constexpr -> engine::o3d::Triangle { - return { - { - vertices[triangle_indices[j][0]], - normals [triangle_indices[j][1]], - mesh.uvs[triangle_indices[j][2]], - vertices_data[triangle_indices[j][0]] - } - ... - }; - }(std::make_index_sequence<3>())); - } + software_renderer.shaders.set_model(obj.transform.to_mat4()); + software_renderer.draw_mesh(obj.mesh); } memcpy(software_renderer_buf_mems[frame_idx], software_renderer.fb.pixels(), swapchain_extent.width * swapchain_extent.height * 4); } @@ -2661,6 +2628,7 @@ static int main_graphical(Scene& scene) { vkDestroyBuffer(device, vertex_bufs[i - 1], nullptr); } vkDestroySampler(device, sampler, nullptr); + stbi_image_free(texture_pixels); vkDestroyImageView(device, texture_img_view, nullptr); vkFreeMemory(device, texture_img_device_mem, nullptr); vkDestroyImage(device, texture_img, nullptr); diff --git a/src/fb/chfb.cpp b/src/fb/chfb.cpp index 7341015..e7717eb 100644 --- a/src/fb/chfb.cpp +++ b/src/fb/chfb.cpp @@ -3,15 +3,10 @@ #include #include "math/vector.hpp" #include "math/quat.hpp" -#include "o3d/vertex_data.hpp" #include "o3d/camera.hpp" using namespace engine::fb; -using - engine::math::Vector2, - engine::math::Vector3, - engine::o3d::VertexData, - engine::o3d::Camera; +using engine::math::Vector4; CharacterFrameBuffer::CharacterFrameBuffer(unsigned int w, unsigned int h) { resize(w, h); @@ -30,11 +25,9 @@ void CharacterFrameBuffer::clear() { // taken from https://stackoverflow.com/a/74186686 char brightness_chars[] = " `.-':_,^=;><+!rc*/z?sLTv)J7(|Fi{C}fI31tlu[neoZ5Yxjya]2ESwqkP6h9d4VpOGbUAKXHm8RD#$Bg0MNWQ%&@"; -extern Camera* camera; -void CharacterFrameBuffer::draw_point(int x, int y, const Vector3& /* loc */, const Vector3& normal, const Vector2& /* uv */, const VertexData& /* vd */) { - auto v = camera->transform.rot.conjugate().rot(normal).normalize(); - float light = .1f + (v.z < 0.f ? 0.f : v.z) * .9f; +void CharacterFrameBuffer::draw_point(int x, int y, const Vector4& point) { + float light = .2126f * point.x + .7152f * point.y + .0722f * point.z; std::uint32_t c = (int) (light * static_cast(sizeof(brightness_chars) - 1)); chars_vector[x + y * w] = brightness_chars[c]; } diff --git a/src/fb/chfb.hpp b/src/fb/chfb.hpp index 1748abf..b076f7d 100644 --- a/src/fb/chfb.hpp +++ b/src/fb/chfb.hpp @@ -3,7 +3,6 @@ #include #include "math/vector.hpp" -#include "o3d/vertex_data.hpp" namespace engine::fb { @@ -12,8 +11,7 @@ class CharacterFrameBuffer { CharacterFrameBuffer(unsigned int w, unsigned int h); void resize(unsigned int w, unsigned int h); void clear(); - void draw_point(int x, int y, const engine::math::Vector3& loc, - const engine::math::Vector3& normal, const engine::math::Vector2& uv, const engine::o3d::VertexData& vd); + void draw_point(int x, int y, const engine::math::Vector4& point); constexpr unsigned int width() const & { return w; diff --git a/src/fb/fb.hpp b/src/fb/fb.hpp new file mode 100644 index 0000000..25b3a51 --- /dev/null +++ b/src/fb/fb.hpp @@ -0,0 +1,21 @@ +#ifndef FB_FB_HPP +#define FB_FB_HPP + +#include +#include "math/vector.hpp" + +namespace engine::fb { + +template +concept FrameBufferConcept = + requires (T fb, int x, int y, unsigned w, unsigned h, engine::math::Vector4 point) { + { fb.resize(w, h) } -> std::same_as; + { fb.clear() } -> std::same_as; + { fb.draw_point(x, y, point) } -> std::same_as; + { fb.width() } -> std::same_as; + { fb.height() } -> std::same_as; + }; + +} + +#endif // FB_FB_HPP diff --git a/src/fb/pixfb.cpp b/src/fb/pixfb.cpp index c629adb..c86859b 100644 --- a/src/fb/pixfb.cpp +++ b/src/fb/pixfb.cpp @@ -2,16 +2,9 @@ #include #include #include "math/vector.hpp" -#include "math/quat.hpp" -#include "o3d/vertex_data.hpp" -#include "o3d/camera.hpp" using namespace engine::fb; -using - engine::math::Vector2, - engine::math::Vector3, - engine::o3d::VertexData, - engine::o3d::Camera; +using engine::math::Vector4; PixelFrameBuffer::PixelFrameBuffer(unsigned int w, unsigned int h) { resize(w, h); @@ -28,26 +21,10 @@ void PixelFrameBuffer::clear() { std::fill(pixels_vector.begin(), pixels_vector.end(), 0xff000000); } -extern Camera* camera; - -void PixelFrameBuffer::draw_point(int x, int y, const Vector3& /* loc */, const Vector3& normal, const Vector2& /* uv */, const VertexData& vd) { - auto u = vd.world_loc - camera->transform.loc; - float u_len_sq = u.length_squared(); - u = u.normalize(); - float attenuation = camera->transform.rot.rot({ 0.f, 0.f, -1.f }).dot(u); - if (attenuation < .7f) attenuation = 0.f; - else if (attenuation > .8f) attenuation = 1.f; - else attenuation = (attenuation - .7f) / .1f; - float light = -normal.normalize().dot(u) / u_len_sq; - if (light < 0.f) light = 0.f; - float final_light = .003f + light * attenuation * .8f * .997f; - if (final_light > 1.f) final_light = 1.f; - // gamma correction - // TODO: improve, we shouldn't do it here - if (final_light <= .0031308f) - final_light = final_light * 12.92f; - else - final_light = 1.055f * pow(final_light, 1.f / 2.4f) - .055f; - std::uint32_t c = (int) (final_light * 255.f); - pixels_vector[x + y * w] = 0xff000000 | c << 16 | c << 8 | c; +void PixelFrameBuffer::draw_point(int x, int y, const Vector4& point) { + pixels_vector[x + y * w] = + std::clamp(static_cast(point.w * 256), static_cast(0), static_cast(255)) << (3 * 8) + | std::clamp(static_cast(point.x * 256), static_cast(0), static_cast(255)) << (2 * 8) + | std::clamp(static_cast(point.y * 256), static_cast(0), static_cast(255)) << (1 * 8) + | std::clamp(static_cast(point.z * 256), static_cast(0), static_cast(255)) << (0 * 8); } diff --git a/src/fb/pixfb.hpp b/src/fb/pixfb.hpp index 954f6e1..d341b10 100644 --- a/src/fb/pixfb.hpp +++ b/src/fb/pixfb.hpp @@ -4,7 +4,6 @@ #include #include #include "math/vector.hpp" -#include "o3d/vertex_data.hpp" namespace engine::fb { @@ -13,8 +12,7 @@ class PixelFrameBuffer { PixelFrameBuffer(unsigned int w, unsigned int h); void resize(unsigned int w, unsigned int h); void clear(); - void draw_point(int x, int y, const engine::math::Vector3& loc, - const engine::math::Vector3& normal, const engine::math::Vector2& uv, const engine::o3d::VertexData& vd); + void draw_point(int x, int y, const engine::math::Vector4& point); constexpr unsigned int width() const & { return w; diff --git a/src/o3d/vertex.hpp b/src/o3d/vertex.hpp index 8d02297..c09f26e 100644 --- a/src/o3d/vertex.hpp +++ b/src/o3d/vertex.hpp @@ -2,7 +2,6 @@ #define O3D_VERTEX_HPP #include "math/vector.hpp" -#include "o3d/vertex_data.hpp" namespace engine::o3d { @@ -10,7 +9,6 @@ struct Vertex { engine::math::Vector4 vertex; engine::math::Vector3 normal; engine::math::Vector2 uv; - engine::o3d::VertexData data; }; } diff --git a/src/o3d/vertex_data.hpp b/src/o3d/vertex_data.hpp deleted file mode 100644 index fa62271..0000000 --- a/src/o3d/vertex_data.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef O3D_VERTEX_DATA_HPP -#define O3D_VERTEX_DATA_HPP - -#include "math/vector.hpp" - -namespace engine::o3d { - -struct VertexData { - static constexpr VertexData lerp(const VertexData& vd1, const VertexData& vd2, float b0) { - return { - b0 * vd1.world_loc + (1.f - b0) * vd2.world_loc, - }; - } - - static constexpr VertexData bilerp(const VertexData& vd1, const VertexData& vd2, const VertexData& vd3, float b0, float b1) { - return { - b0 * vd1.world_loc + b1 * vd2.world_loc + (1.f - b0 - b1) * vd3.world_loc, - }; - } - - engine::math::Vector3 world_loc; -}; - -} - -#endif // O3D_VERTEX_DATA_HPP diff --git a/src/obj_parser.cpp b/src/obj_parser.cpp index 28df871..68abbff 100644 --- a/src/obj_parser.cpp +++ b/src/obj_parser.cpp @@ -11,7 +11,6 @@ #include "math/vector.hpp" #include "math/utils.hpp" #include "o3d/mesh.hpp" -#include "o3d/vertex_data.hpp" namespace engine { diff --git a/src/renderer.cpp b/src/renderer.cpp index 35e3ab3..9447119 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -3,12 +3,17 @@ #include #include #include +#include #include "math/vector.hpp" #include "o3d/tri.hpp" #include "o3d/tri_deriv.hpp" #include "o3d/polygon.hpp" +#include "o3d/mesh.hpp" +#include "fb/fb.hpp" #include "fb/chfb.hpp" #include "fb/pixfb.hpp" +#include "shaders/shaders.hpp" +#include "shaders/simple_shaders.hpp" using namespace engine; using @@ -17,32 +22,35 @@ using engine::math::Vector4, engine::o3d::Triangle, engine::o3d::TriangleDerived, - engine::o3d::VertexData, engine::o3d::DerivedVertex, + engine::o3d::Mesh, engine::fb::CharacterFrameBuffer, - engine::fb::PixelFrameBuffer; + engine::fb::FrameBufferConcept, + engine::fb::PixelFrameBuffer, + engine::shaders::ShadersConcept, + engine::shaders::SimpleShaders; -template -Renderer::Renderer(FrameBuffer&& fb) : fb { std::forward(fb) } { +template +Renderer::Renderer(FrameBuffer&& fb, Shaders&& shaders) : fb { std::forward(fb) }, shaders { std::forward(shaders) } { depth_buf.resize(this->fb.width() * this->fb.height()); } -template -void Renderer::resize(unsigned int w, unsigned int h) { +template +void Renderer::resize(unsigned int w, unsigned int h) { fb.resize(w, h); depth_buf.resize(fb.width() * fb.height()); } -template -void Renderer::clear() { +template +void Renderer::clear() { std::fill(depth_buf.begin(), depth_buf.end(), 1.f); fb.clear(); } enum class TriangleSide { top, bottom }; -template -void Renderer::draw_triangle(const Triangle& triangle) { +template +void Renderer::draw_triangle(const Triangle& triangle) { const auto& polygon = engine::o3d::polygon::div_by_w(engine::o3d::polygon::from_triangle_derived(triangle.to_derived()).clip_z(0.f, 1.f)); if (engine::o3d::polygon::signed_area_xy(polygon) >= 0) return; const auto& [final_triangles_count, final_triangles] @@ -53,11 +61,25 @@ void Renderer::draw_triangle(const Triangle& triangle) { _draw_cropped_triangle(triangle, final_triangles[i]); } +template +void Renderer::draw_mesh(const Mesh& mesh) { + for (const auto& triangle_indices : mesh.indices) { + draw_triangle([&](std::index_sequence) -> Triangle { + return { + shaders.vertex(mesh.vertices[triangle_indices[j][0]], + mesh.normals [triangle_indices[j][1]], + mesh.uvs [triangle_indices[j][2]]) + ... + }; + }(std::make_index_sequence<3>())); + } +} + // TODO: the renaming of w to z or the inverse is very confusing. The reason why it happens is // because we use Vector3 to store x, y and w, which therefore gets renamed to w. We should find // another way of doing this -template -void Renderer::_draw_cropped_triangle(const Triangle& root, const TriangleDerived& triangle) { +template +void Renderer::_draw_cropped_triangle(const Triangle& root, const TriangleDerived& triangle) { std::array*, 3> sorted_vs = { &triangle.derived_vertex1, &triangle.derived_vertex2, &triangle.derived_vertex3 }; { @@ -138,10 +160,9 @@ void Renderer::_draw_cropped_triangle(const Triangle& root, const T depth_buf[x + y * fb.width()] = loc.z; - fb.draw_point(x, y, loc, - Vector3 ::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1), - Vector2 ::bilerp(root.vertex1.uv, root.vertex2.uv, root.vertex3.uv, b0, b1), - VertexData::bilerp(root.vertex1.data, root.vertex2.data, root.vertex3.data, b0, b1)); + fb.draw_point(x, y, shaders.fragment(x, y, loc, + Vector3::bilerp(root.vertex1.normal, root.vertex2.normal, root.vertex3.normal, b0, b1), + Vector2::bilerp(root.vertex1.uv, root.vertex2.uv, root.vertex3.uv, b0, b1))); } } }; @@ -149,5 +170,5 @@ void Renderer::_draw_cropped_triangle(const Triangle& root, const T _draw_cropped_triangle_side.template operator()(); } -template class Renderer; -template class Renderer; +template class Renderer; +template class Renderer; diff --git a/src/renderer.hpp b/src/renderer.hpp index 6725f70..3880d04 100644 --- a/src/renderer.hpp +++ b/src/renderer.hpp @@ -2,22 +2,29 @@ #define RENDERER_HPP #include +#include #include "math/vector.hpp" #include "o3d/tri.hpp" #include "o3d/tri_deriv.hpp" +#include "o3d/mesh.hpp" +#include "fb/fb.hpp" +#include "shaders/shaders.hpp" namespace engine { -template -class Renderer { +template +class Renderer +{ public: FrameBuffer fb; + Shaders shaders; - Renderer(FrameBuffer&& fb); + Renderer(FrameBuffer&& fb, Shaders&& shaders); void resize(unsigned int w, unsigned int h); void clear(); void draw_triangle(const o3d::Triangle& triangle); + void draw_mesh(const o3d::Mesh& mesh); constexpr unsigned int width() const & { return fb.width(); diff --git a/src/shaders/shaders.hpp b/src/shaders/shaders.hpp new file mode 100644 index 0000000..d5e1c3a --- /dev/null +++ b/src/shaders/shaders.hpp @@ -0,0 +1,24 @@ +#ifndef SHADERS_SHADERS_HPP +#define SHADERS_SHADERS_HPP + +#include +#include "math/vector.hpp" +#include "o3d/vertex.hpp" +#include "fb/fb.hpp" + +namespace engine::shaders { + +template +concept ShadersConcept = + requires { + typename T::fb_type; + requires engine::fb::FrameBufferConcept; + } && + requires (T shaders, int x, int y, engine::math::Vector3 v3_1, engine::math::Vector3 v3_2, engine::math::Vector2 v2) { + { shaders.vertex(v3_1, v3_2, v2) } -> std::same_as; + { shaders.fragment(x, y, v3_1, v3_2, v2) } -> std::same_as; + }; + +} + +#endif // SHADERS_SHADERS_HPP diff --git a/src/shaders/simple_shaders.cpp b/src/shaders/simple_shaders.cpp new file mode 100644 index 0000000..ebbec31 --- /dev/null +++ b/src/shaders/simple_shaders.cpp @@ -0,0 +1,18 @@ +#include "shaders/simple_shaders.hpp" +#include "math/mat4.hpp" + +using namespace engine::shaders; +using engine::math::Matrix4; + +SimpleShaders::SimpleShaders(int w, int h, unsigned char* pixels) : w { w }, h { h }, pixels { pixels } { +} + +void SimpleShaders::set_view(const Matrix4& view_mat, const Matrix4& proj_mat) { + this->view_mat = view_mat; + this->proj_mat = proj_mat; +} + +void SimpleShaders::set_model(const Matrix4& model_mat) { + this->model_mat = model_mat; + final_mat = proj_mat * view_mat * model_mat; +} diff --git a/src/shaders/simple_shaders.hpp b/src/shaders/simple_shaders.hpp new file mode 100644 index 0000000..c308ab3 --- /dev/null +++ b/src/shaders/simple_shaders.hpp @@ -0,0 +1,83 @@ +#ifndef SHADERS_SIMPLE_SHADERS_HPP +#define SHADERS_SIMPLE_SHADERS_HPP + +#include +#include +#include "fb/pixfb.hpp" +#include "math/vector.hpp" +#include "math/mat4.hpp" +#include "o3d/vertex.hpp" + +namespace engine::shaders { + +class SimpleShaders { + public: + using fb_type = engine::fb::PixelFrameBuffer; + + engine::math::Matrix4 model_mat, view_mat, proj_mat, final_mat; + int w, h; + unsigned char* pixels; + + SimpleShaders(int w, int h, unsigned char* pixels); + void set_view(const engine::math::Matrix4& view_mat, const engine::math::Matrix4& proj_mat); + void set_model(const engine::math::Matrix4& model_mat); + + constexpr engine::o3d::Vertex vertex(const engine::math::Vector3& vertex, const engine::math::Vector3& normal, const engine::math::Vector2& uv) const & { + return { final_mat * engine::math::Vector4 { vertex, 1.f }, (model_mat * engine::math::Vector4 { normal, 0.f }).xyz(), uv }; + } + + constexpr engine::math::Vector4 fragment(int /* x */, int /* y */, const engine::math::Vector3& /* loc */, const engine::math::Vector3& /* normal */, + const engine::math::Vector2& uv) const & { + // auto u = vd.world_loc - camera->transform.loc; + // float u_len_sq = u.length_squared(); + // u = u.normalize(); + // float attenuation = camera->transform.rot.rot({ 0.f, 0.f, -1.f }).dot(u); + // if (attenuation < .7f) attenuation = 0.f; + // else if (attenuation > .8f) attenuation = 1.f; + // else attenuation = (attenuation - .7f) / .1f; + // float light = -normal.normalize().dot(u) / u_len_sq; + // if (light < 0.f) light = 0.f; + // float final_light = .003f + light * attenuation * .8f * .997f; + // if (final_light > 1.f) final_light = 1.f; + // // gamma correction + // // TODO: improve, we shouldn't do it here + // if (final_light <= .0031308f) + // final_light = final_light * 12.92f; + // else + // final_light = 1.055f * pow(final_light, 1.f / 2.4f) - .055f; + // std::uint32_t c = (int) (final_light * 255.f); + // pixels_vector[x + y * w] = 0xff000000 | c << 16 | c << 8 | c; + float txf, tyf; + float fac_x = std::modf(uv.x * static_cast(w) - .5f, &txf); + float fac_y = std::modf(uv.y * static_cast(h) - .5f, &tyf); + int tx0 = std::clamp(static_cast(txf) + 0, 0, w); + int tx1 = std::clamp(static_cast(txf) + 1, 0, w); + int ty0 = std::clamp(static_cast(tyf) + 0, 0, h); + int ty1 = std::clamp(static_cast(tyf) + 1, 0, h); + float r00 = static_cast(pixels[(tx0 + ty0 * w) * 4 + 0]) / 255.f; + float r10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 0]) / 255.f; + float r01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 0]) / 255.f; + float r11 = static_cast(pixels[(tx1 + ty1 * w) * 4 + 0]) / 255.f; + float r = (1.f - fac_x) * (1.f - fac_y) * r00 + fac_x * (1.f - fac_y) * r10 + (1.f - fac_x) * fac_y * r01 + fac_x * fac_y * r11; + float g00 = static_cast(pixels[(tx0 + ty0 * w) * 4 + 1]) / 255.f; + float g10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 1]) / 255.f; + float g01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 1]) / 255.f; + float g11 = static_cast(pixels[(tx1 + ty1 * w) * 4 + 1]) / 255.f; + float g = (1.f - fac_x) * (1.f - fac_y) * g00 + fac_x * (1.f - fac_y) * g10 + (1.f - fac_x) * fac_y * g01 + fac_x * fac_y * g11; + float b00 = static_cast(pixels[(tx0 + ty0 * w) * 4 + 2]) / 255.f; + float b10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 2]) / 255.f; + float b01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 2]) / 255.f; + float b11 = static_cast(pixels[(tx1 + ty1 * w) * 4 + 2]) / 255.f; + float b = (1.f - fac_x) * (1.f - fac_y) * b00 + fac_x * (1.f - fac_y) * b10 + (1.f - fac_x) * fac_y * b01 + fac_x * fac_y * b11; + float a00 = static_cast(pixels[(tx0 + ty0 * w) * 4 + 3]) / 255.f; + float a10 = static_cast(pixels[(tx1 + ty0 * w) * 4 + 3]) / 255.f; + float a01 = static_cast(pixels[(tx0 + ty1 * w) * 4 + 3]) / 255.f; + float a11 = static_cast(pixels[(tx1 + ty1 * w) * 4 + 3]) / 255.f; + float a = (1.f - fac_x) * (1.f - fac_y) * a00 + fac_x * (1.f - fac_y) * a10 + (1.f - fac_x) * fac_y * a01 + fac_x * fac_y * a11; + return engine::math::Vector4 { r, g, b, a }; + } +}; + +} + +#endif // SHADERS_SIMPLE_SHADERS_HPP -- cgit v1.2.3