aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorvimene <vincent.menegaux@gmail.com>2026-01-17 00:06:31 +0100
committervimene <vincent.menegaux@gmail.com>2026-01-17 00:06:31 +0100
commit0d10b77f77459333c5549711334f417623ab1f3e (patch)
tree6584ab5d09fa72c93a70ac916bfdf401c7617157 /src
parent175c71637b6bea6dcdd0faf3d614339983809bb1 (diff)
downloadengine-0d10b77f77459333c5549711334f417623ab1f3e.tar.gz
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
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am11
-rw-r--r--src/engine.cpp112
-rw-r--r--src/fb/chfb.cpp13
-rw-r--r--src/fb/chfb.hpp4
-rw-r--r--src/fb/fb.hpp21
-rw-r--r--src/fb/pixfb.cpp37
-rw-r--r--src/fb/pixfb.hpp4
-rw-r--r--src/o3d/vertex.hpp2
-rw-r--r--src/o3d/vertex_data.hpp26
-rw-r--r--src/obj_parser.cpp1
-rw-r--r--src/renderer.cpp57
-rw-r--r--src/renderer.hpp13
-rw-r--r--src/shaders/shaders.hpp24
-rw-r--r--src/shaders/simple_shaders.cpp18
-rw-r--r--src/shaders/simple_shaders.hpp83
15 files changed, 254 insertions, 172 deletions
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 <fstream>
#include <map>
#include <chrono>
+#include <print>
+#include <cstdio>
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>
@@ -32,12 +34,12 @@
#include <ncurses.h>
#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<typename UpdateSurfaceSizeFn, typename PollEventsFn, typename RenderAndPresentFrameFn>
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<typename FrameBuffer, typename PollEventsFn, typename UpdateRendererSizeFn, typename PresentFrameFn>
-static void render_software(Renderer<FrameBuffer>& renderer, const Matrix4& final_transform_mat, Scene& scene,
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders, typename PollEventsFn, typename UpdateRendererSizeFn, typename PresentFrameFn>
+static void render_software(Renderer<FrameBuffer, Shaders>& 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<FrameBuffer>& 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<Vector4> vertices;
- // std::vector<Vector3> normals;
- // std::vector<VertexData> 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::size_t... j>(std::index_sequence<j...>) {
- // 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<unsigned int>(w), static_cast<unsigned int>(h) } };
+ return Renderer { CharacterFrameBuffer { static_cast<unsigned int>(w), static_cast<unsigned int>(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<uint32_t>(w), static_cast<uint32_t>(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<PixelFrameBuffer> software_renderer { PixelFrameBuffer {
- static_cast<unsigned int>(swapchain_extent.width),
- static_cast<unsigned int>(swapchain_extent.height),
- } };
+ Renderer software_renderer {
+ PixelFrameBuffer { static_cast<unsigned int>(swapchain_extent.width), static_cast<unsigned int>(swapchain_extent.height) },
+ SimpleShaders { texture_w, texture_h, texture_pixels },
+ };
bool first_software_renderer_buf_creation = true;
std::array<VkBuffer, max_frames_in_flight> 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<Vector4> vertices;
- std::vector<Vector3> normals;
- std::vector<VertexData> 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([&]<size_t... j>(std::index_sequence<j...>) 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 <cstdint>
#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<float>(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 <vector>
#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 <concepts>
+#include "math/vector.hpp"
+
+namespace engine::fb {
+
+template<typename T>
+concept FrameBufferConcept =
+ requires (T fb, int x, int y, unsigned w, unsigned h, engine::math::Vector4 point) {
+ { fb.resize(w, h) } -> std::same_as<void >;
+ { fb.clear() } -> std::same_as<void >;
+ { fb.draw_point(x, y, point) } -> std::same_as<void >;
+ { fb.width() } -> std::same_as<unsigned>;
+ { fb.height() } -> std::same_as<unsigned>;
+ };
+
+}
+
+#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 <cstdint>
#include <algorithm>
#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<std::uint32_t>(point.w * 256), static_cast<std::uint32_t>(0), static_cast<std::uint32_t>(255)) << (3 * 8)
+ | std::clamp(static_cast<std::uint32_t>(point.x * 256), static_cast<std::uint32_t>(0), static_cast<std::uint32_t>(255)) << (2 * 8)
+ | std::clamp(static_cast<std::uint32_t>(point.y * 256), static_cast<std::uint32_t>(0), static_cast<std::uint32_t>(255)) << (1 * 8)
+ | std::clamp(static_cast<std::uint32_t>(point.z * 256), static_cast<std::uint32_t>(0), static_cast<std::uint32_t>(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 <vector>
#include <cstdint>
#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 <memory>
#include <array>
#include <tuple>
+#include <utility>
#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<typename FrameBuffer>
-Renderer<FrameBuffer>::Renderer(FrameBuffer&& fb) : fb { std::forward<FrameBuffer>(fb) } {
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+Renderer<FrameBuffer, Shaders>::Renderer(FrameBuffer&& fb, Shaders&& shaders) : fb { std::forward<FrameBuffer>(fb) }, shaders { std::forward<Shaders>(shaders) } {
depth_buf.resize(this->fb.width() * this->fb.height());
}
-template<typename FrameBuffer>
-void Renderer<FrameBuffer>::resize(unsigned int w, unsigned int h) {
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+void Renderer<FrameBuffer, Shaders>::resize(unsigned int w, unsigned int h) {
fb.resize(w, h);
depth_buf.resize(fb.width() * fb.height());
}
-template<typename FrameBuffer>
-void Renderer<FrameBuffer>::clear() {
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+void Renderer<FrameBuffer, Shaders>::clear() {
std::fill(depth_buf.begin(), depth_buf.end(), 1.f);
fb.clear();
}
enum class TriangleSide { top, bottom };
-template<typename FrameBuffer>
-void Renderer<FrameBuffer>::draw_triangle(const Triangle& triangle) {
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+void Renderer<FrameBuffer, Shaders>::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<FrameBuffer>::draw_triangle(const Triangle& triangle) {
_draw_cropped_triangle(triangle, final_triangles[i]);
}
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+void Renderer<FrameBuffer, Shaders>::draw_mesh(const Mesh& mesh) {
+ for (const auto& triangle_indices : mesh.indices) {
+ draw_triangle([&]<std::size_t... j>(std::index_sequence<j...>) -> 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<typename FrameBuffer>
-void Renderer<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const TriangleDerived<Vector3>& triangle) {
+template<FrameBufferConcept FrameBuffer, ShadersConcept Shaders>
+void Renderer<FrameBuffer, Shaders>::_draw_cropped_triangle(const Triangle& root, const TriangleDerived<Vector3>& triangle) {
std::array<const DerivedVertex<Vector3>*, 3> sorted_vs = { &triangle.derived_vertex1, &triangle.derived_vertex2, &triangle.derived_vertex3 };
{
@@ -138,10 +160,9 @@ void Renderer<FrameBuffer>::_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<FrameBuffer>::_draw_cropped_triangle(const Triangle& root, const T
_draw_cropped_triangle_side.template operator()<TriangleSide::bottom>();
}
-template class Renderer<CharacterFrameBuffer>;
-template class Renderer<PixelFrameBuffer>;
+template class Renderer<CharacterFrameBuffer, SimpleShaders>;
+template class Renderer<PixelFrameBuffer, SimpleShaders>;
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 <memory>
+#include <type_traits>
#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<typename FrameBuffer>
-class Renderer {
+template<fb::FrameBufferConcept FrameBuffer, shaders::ShadersConcept Shaders>
+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 <concepts>
+#include "math/vector.hpp"
+#include "o3d/vertex.hpp"
+#include "fb/fb.hpp"
+
+namespace engine::shaders {
+
+template<typename T>
+concept ShadersConcept =
+ requires {
+ typename T::fb_type;
+ requires engine::fb::FrameBufferConcept<typename T::fb_type>;
+ } &&
+ 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<engine::o3d::Vertex>;
+ { shaders.fragment(x, y, v3_1, v3_2, v2) } -> std::same_as<engine::math::Vector4>;
+ };
+
+}
+
+#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 <algorithm>
+#include <cmath>
+#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<float>(w) - .5f, &txf);
+ float fac_y = std::modf(uv.y * static_cast<float>(h) - .5f, &tyf);
+ int tx0 = std::clamp(static_cast<int>(txf) + 0, 0, w);
+ int tx1 = std::clamp(static_cast<int>(txf) + 1, 0, w);
+ int ty0 = std::clamp(static_cast<int>(tyf) + 0, 0, h);
+ int ty1 = std::clamp(static_cast<int>(tyf) + 1, 0, h);
+ float r00 = static_cast<float>(pixels[(tx0 + ty0 * w) * 4 + 0]) / 255.f;
+ float r10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 0]) / 255.f;
+ float r01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 0]) / 255.f;
+ float r11 = static_cast<float>(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<float>(pixels[(tx0 + ty0 * w) * 4 + 1]) / 255.f;
+ float g10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 1]) / 255.f;
+ float g01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 1]) / 255.f;
+ float g11 = static_cast<float>(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<float>(pixels[(tx0 + ty0 * w) * 4 + 2]) / 255.f;
+ float b10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 2]) / 255.f;
+ float b01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 2]) / 255.f;
+ float b11 = static_cast<float>(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<float>(pixels[(tx0 + ty0 * w) * 4 + 3]) / 255.f;
+ float a10 = static_cast<float>(pixels[(tx1 + ty0 * w) * 4 + 3]) / 255.f;
+ float a01 = static_cast<float>(pixels[(tx0 + ty1 * w) * 4 + 3]) / 255.f;
+ float a11 = static_cast<float>(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