diff --git a/CPU-Software-Renderer.slnx b/CPU-Software-Renderer.slnx index 5180c2c..3cdcc9b 100644 --- a/CPU-Software-Renderer.slnx +++ b/CPU-Software-Renderer.slnx @@ -3,5 +3,7 @@ - + + + diff --git a/CPU-Software-Renderer/Asset/ObjLoader.cpp b/CPU-Software-Renderer/Asset/ObjLoader.cpp new file mode 100644 index 0000000..e69de29 diff --git a/CPU-Software-Renderer/Asset/ObjLoader.h b/CPU-Software-Renderer/Asset/ObjLoader.h new file mode 100644 index 0000000..d94dcec --- /dev/null +++ b/CPU-Software-Renderer/Asset/ObjLoader.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Asset +{ + class ObjLoader + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj index 4d2f6bc..33b6481 100644 --- a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj +++ b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj @@ -77,10 +77,13 @@ WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Asset;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Core;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Math;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Rasterizer;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\RenderData;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Scene;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Shading;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\include;%(AdditionalIncludeDirectories) Console true + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories) @@ -92,10 +95,13 @@ WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Asset;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Core;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Math;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Rasterizer;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\RenderData;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Scene;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Shading;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\include;%(AdditionalIncludeDirectories) Console true + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories) @@ -105,7 +111,7 @@ _DEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 - D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\include;%(AdditionalIncludeDirectories) + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Asset;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Core;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Math;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Rasterizer;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\RenderData;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Scene;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Shading;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\include;%(AdditionalIncludeDirectories) Console @@ -123,26 +129,52 @@ NDEBUG;_CONSOLE;%(PreprocessorDefinitions) true stdcpp20 + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Asset;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Core;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Math;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Rasterizer;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\RenderData;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Scene;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\Shading;D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\include;%(AdditionalIncludeDirectories) Console true + SDL2.lib;SDL2main.lib;%(AdditionalDependencies) + D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x64;%(AdditionalLibraryDirectories) - + + + + - + + + - - - + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file + diff --git a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj.filters b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj.filters index 42fc765..ad8a31f 100644 --- a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj.filters +++ b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj.filters @@ -13,16 +13,64 @@ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + {e546c002-17ba-4911-9a70-a1cf7ce337fd} + + + {b92096d9-3d39-4064-b9a2-efa279ad0775} + + + {7b6f5c6b-60bb-46f2-925b-65346e4d8c4b} + + + {1aa3c6ac-a580-480c-a2fe-2d57c98693ff} + + + {a7d67eb7-b4bc-4b5a-86f2-e80bcd1518d5} + + + {55224e49-7917-4fee-9a61-f1c14ebf8d5e} + + + {6eda961b-ec6a-4670-b093-be46b27fdeb7} + + + {4345cc0e-3d61-4dd9-b37f-4b42c30aa8d3} + + + {4b52509e-dde8-45e0-aeb3-d18131b94265} + + + {1644176d-c2fe-404b-9f42-45eb0bc85222} + + + {c77ae566-281a-4d76-80bd-41496deb0aeb} + 源文件 - - 源文件 + + 源文件\Core - - 源文件 + + 源文件\Core + + + 源文件\Core + + + 源文件\Asset + + + 源文件\Shading + + + 源文件\Rasterizer + + + 源文件\Rasterizer @@ -31,14 +79,68 @@ - - 头文件 + + 头文件\Core - - 头文件 + + 头文件\Core - - 头文件 + + 头文件\Core + + + 头文件\Math + + + 头文件\Math + + + 头文件\Math + + + 头文件\Math + + + 头文件\Math + + + 头文件\Scene + + + 头文件\Scene + + + 头文件\Scene + + + 头文件\Scene + + + 头文件\Scene + + + 头文件\Shading + + + 头文件\Shading + + + 头文件\Rasterizer + + + 头文件\Rasterizer + + + 头文件\Asset + + + 头文件\RenderData + + + 头文件\RenderData + + + 头文件\RenderData \ No newline at end of file diff --git a/CPU-Software-Renderer/Color.cpp b/CPU-Software-Renderer/Color.cpp deleted file mode 100644 index 873f23a..0000000 --- a/CPU-Software-Renderer/Color.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include "Color.h" - -Color::Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) -{ - this->r = r; - this->g = g; - this->b = b; - this->a = a; -} - -Color::Color(uint8_t r, uint8_t g, uint8_t b) -{ - Color(r, g, b, 255); -} - -Color::Color(Color& other) -{ - this->r = other.r; - this->g = other.g; - this->b = other.b; - this->a = other.a; -} \ No newline at end of file diff --git a/CPU-Software-Renderer/Color.h b/CPU-Software-Renderer/Color.h deleted file mode 100644 index 2634d4e..0000000 --- a/CPU-Software-Renderer/Color.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include - -class Color -{ - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - -public: - Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a); - Color(uint8_t r, uint8_t g, uint8_t b); - Color(Color& other); - - uint8_t GetR() const { return r; } - void SetR(const uint8_t r) { this->r = r; } - - uint8_t GetG() const { return g; } - void SetG(const uint8_t g) { this->g = g; } - - uint8_t GetB() const { return b; } - void SetB(const uint8_t b) { this->b = b; } - - uint8_t GetA() const { return a; } - void SetA(const uint8_t a) { this->a = a; } - - operator uint32_t() const - { - return (r << 24) | (g << 16) | (b << 8) | a; - } - -}; \ No newline at end of file diff --git a/CPU-Software-Renderer/Core/DepthBuffer.cpp b/CPU-Software-Renderer/Core/DepthBuffer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/CPU-Software-Renderer/Core/DepthBuffer.h b/CPU-Software-Renderer/Core/DepthBuffer.h new file mode 100644 index 0000000..4e113e6 --- /dev/null +++ b/CPU-Software-Renderer/Core/DepthBuffer.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Core +{ + class DepthBuffer + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Core/FrameBuffer.cpp b/CPU-Software-Renderer/Core/FrameBuffer.cpp new file mode 100644 index 0000000..30072cf --- /dev/null +++ b/CPU-Software-Renderer/Core/FrameBuffer.cpp @@ -0,0 +1,25 @@ +#include "FrameBuffer.h" +#include +#include +#include +using namespace Math; + +namespace Core +{ + void FrameBuffer::clear(const uint32_t color) + { + std::fill(buffer.begin(), buffer.end(), color); + } + + + void FrameBuffer::set_pixel(const int32_t x, const int32_t y, const uint32_t color) + { + if (x < 0 || x >= width || y < 0 || y >= height) + { + std::cerr << "Error: Attempting to set pixel at (" << x << ", " << y << ") which is out of bounds." << std::endl; + return; + } + size_t index = static_cast(y) * width + x; + buffer.at(index) = color; + } +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Core/FrameBuffer.h b/CPU-Software-Renderer/Core/FrameBuffer.h new file mode 100644 index 0000000..41e42b1 --- /dev/null +++ b/CPU-Software-Renderer/Core/FrameBuffer.h @@ -0,0 +1,42 @@ +#pragma once +#include "Color.h" +#include "Vector2.h" +#include +#include + +namespace Core +{ + class FrameBuffer + { + private: + int32_t width; + int32_t height; + std::vector buffer; + + public: + size_t total_pixels() const { return buffer.size(); } + + void* get_buffer() const { return (void*)buffer.data(); } + + FrameBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector(width * height, 0)) {} + + void clear(const RenderData::Color& color) + { + clear(color.to_rgba()); + } + + void clear(const uint32_t color); + + void set_pixel(const int32_t x, const int32_t y, const RenderData::Color& color) + { + set_pixel(Math::Vector2Int(x, y), color.to_rgba()); + } + + void set_pixel(const Math::Vector2Int position, const uint32_t color) + { + set_pixel(position.x, position.y, color); + } + + void set_pixel(const int32_t x, const int32_t y, const uint32_t color); + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Core/Renderer.cpp b/CPU-Software-Renderer/Core/Renderer.cpp new file mode 100644 index 0000000..e69de29 diff --git a/CPU-Software-Renderer/Core/Renderer.h b/CPU-Software-Renderer/Core/Renderer.h new file mode 100644 index 0000000..b3b04c9 --- /dev/null +++ b/CPU-Software-Renderer/Core/Renderer.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Core +{ + class Renderer + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Math/MathUtil.h b/CPU-Software-Renderer/Math/MathUtil.h new file mode 100644 index 0000000..fefaf5a --- /dev/null +++ b/CPU-Software-Renderer/Math/MathUtil.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Math +{ + class MathUtil + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Math/Matrix4x4.h b/CPU-Software-Renderer/Math/Matrix4x4.h new file mode 100644 index 0000000..555a649 --- /dev/null +++ b/CPU-Software-Renderer/Math/Matrix4x4.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Math +{ + class Matrix4x4 + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Math/Vector2.h b/CPU-Software-Renderer/Math/Vector2.h new file mode 100644 index 0000000..d3176ac --- /dev/null +++ b/CPU-Software-Renderer/Math/Vector2.h @@ -0,0 +1,50 @@ +#pragma once +#include +#include +#include + +namespace Math +{ + struct Vector2Int + { + int32_t x = 0; + int32_t y = 0; + + Vector2Int() : x(0), y(0) {} + Vector2Int(int32_t x, int32_t y) : x(x), y(y) {} + + /// + /// 交换当前 Vector2Int 对象与另一个 Vector2Int 对象的 x 和 y 的值 + /// + /// 要交换的对象 + static void swap(Vector2Int& vec1, Vector2Int& vec2) + { + std::swap(vec1.x, vec2.x); + std::swap(vec1.y, vec2.y); + } + }; + + struct Vector2 + { + float x = 0.0f; + float y = 0.0f; + + Vector2() : x(0), y(0) {} + Vector2(float x, float y) : x(x), y(y) {} + + /// + /// 交换当前 Vector2 对象与另一个 Vector2 对象的 x 和 y 的值 + /// + /// 要交换的对象 + static void swap(Vector2& vec1, Vector2& vec2) + { + std::swap(vec1.x, vec2.x); + std::swap(vec1.y, vec2.y); + } + + Vector2Int to_vector2Int() const + { + return Vector2Int(static_cast(std::lround(x)), static_cast(std::lround(y))); + } + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Math/Vector3.h b/CPU-Software-Renderer/Math/Vector3.h new file mode 100644 index 0000000..acff28b --- /dev/null +++ b/CPU-Software-Renderer/Math/Vector3.h @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace Math +{ + struct Vector3 + { + float x = 0.0f; + float y = 0.0f; + float z = 0.0f; + + Vector3() : x(0), y(0), z(0) {} + Vector3(float x, float y, float z) : x(x), y(y), z(z) {} + + float magnitude() const + { + return std::sqrt(x * x + y * y + z * z); + } + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Math/Vector4.h b/CPU-Software-Renderer/Math/Vector4.h new file mode 100644 index 0000000..1d8d84e --- /dev/null +++ b/CPU-Software-Renderer/Math/Vector4.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Math +{ + class Vector4 + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Rasterization.cpp b/CPU-Software-Renderer/Rasterization.cpp deleted file mode 100644 index 3952a54..0000000 --- a/CPU-Software-Renderer/Rasterization.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "Rasterization.h" -#include "StructType.h" -#include -#include -#include -#include -using namespace CustomStructs; - -void Rasterization::DrawLineHorizontal(Vector2Int v0, Vector2Int v1, Color color) -{ - if (v0.x > v1.x) Vector2Int::swap(v0, v1); - - int32_t dx = v1.x - v0.x; - int32_t dy = v1.y - v0.y; - - int32_t dir = dy < 0 ? -1 : 1; - dy *= dir; - - if (dx == 0) return; - - int32_t y = v0.y; - int32_t p = 2 * dy - dx; - for (int32_t x = v0.x; x <= v1.x; x++) - { - SetPixel(Vector2Int(x, y), color); - // 当 p >= 0 时,取上方像素点 - if (p >= 0) - { - y += dir; - p -= 2 * dx; - } - // 当 p < 0 时,取下方像素点 - p += 2 * dy; - } -} - -void Rasterization::DrawLineVertical(Vector2Int v0, Vector2Int v1, Color color) -{ - if (v0.y > v1.y) Vector2Int::swap(v0, v1); - - int32_t dx = v1.x - v0.x; - int32_t dy = v1.y - v0.y; - - int32_t dir = dx < 0 ? -1 : 1; - dx *= dir; - - if (dy == 0) return; - - int32_t x = v0.x; - int32_t p = 2 * dx - dy; - for (int32_t y = v0.y; y <= v1.y; y++) - { - SetPixel(Vector2Int(x, y), color); - // 当 p >= 0 时,取上方像素点 - if (p >= 0) - { - x += dir; - p -= 2 * dy; - } - // 当 p < 0 时,取下方像素点 - p += 2 * dx; - } -} - -/// -/// 在给定的起始点和结束点之间绘制一条线段,并使用指定的颜色填充 -/// -/// 直线顶点 1 -/// 直线顶点 2 -/// 绘制的颜色 -/// -/// bresenham 画线算法 -/// -void Rasterization::DrawLine(Vector2Int v0, Vector2Int v1, Color color) -{ - int32_t x0 = v0.x, y0 = v0.y; - int32_t x1 = v1.x, y1 = v1.y; - - if (std::abs(x1 - x0) > std::abs(y1 - y0)) - { - DrawLineHorizontal(v0, v1, color); - } - else - { - DrawLineVertical(v0, v1, color); - } -} - -void Rasterization::Clear(const Color color) -{ - uint32_t colorValue = static_cast(color); - std::fill(frameBuffer.begin(), frameBuffer.end(), colorValue); -} - -void Rasterization::SetPixel(const Vector2Int pos, const Color color) -{ - uint32_t colorValue = static_cast(color); - if (pos.x < 0 || pos.y < 0 || pos.x >= width || pos.y >= height) return; - const size_t pixelIndex = static_cast(pos.y) * static_cast(width) + static_cast(pos.x); - frameBuffer[pixelIndex] = colorValue; -} - -void Rasterization::DrawWireTriangle(const Vector2Int v0, const Vector2Int v1, const Vector2Int v2, Color color) -{ - DrawLine(v0, v1, color); - DrawLine(v0, v2, color); - DrawLine(v1, v2, color); -} - -void Rasterization::DrawFillTriangle(const Vector2Int v0, const Vector2Int v1, const Vector2Int v2, Color color) -{ - Vector2Int aa, bb; - aa.x = std::min({ v0.x, v1.x, v2.x }); - aa.y = std::min({ v0.y, v1.y, v2.y }); - bb.x = std::max({ v0.x, v1.x, v2.x }); - bb.y = std::max({ v0.y, v1.y, v2.y }); - Triangle triangle(v0, v1, v2); - - for (int x = aa.x; x <= bb.x; x++) - { - for (int y = aa.y; y <= bb.y; y++) - { - if (triangle.IsInTriangle(Vector2Int(x, y))) - { - SetPixel(Vector2Int(x, y), color); - } - } - } -} diff --git a/CPU-Software-Renderer/Rasterization.h b/CPU-Software-Renderer/Rasterization.h deleted file mode 100644 index 147e0bf..0000000 --- a/CPU-Software-Renderer/Rasterization.h +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once -#include "StructType.h" -#include -#include -using namespace CustomStructs; - -class Rasterization -{ -private: - int32_t width; - int32_t height; - -public: - std::vector frameBuffer; - -private: - void DrawLineHorizontal(Vector2Int v0, Vector2Int v1, Color color); - - void DrawLineVertical(Vector2Int v0, Vector2Int v1, Color color); - -public: - Rasterization(const int32_t width, const int32_t height) - : width(width), height(height), frameBuffer(std::vector(static_cast(width)* static_cast(height))) {} - - void Clear(const Color color); - - void SetPixel(const Vector2Int pos, const Color color); - - void DrawLine(const Vector2Int v0, const Vector2Int v1, Color color); - - void DrawWireTriangle(const Vector2Int v0, const Vector2Int v1, const Vector2Int v2, Color color); - - void DrawFillTriangle(const Vector2Int v0, const Vector2Int v1, const Vector2Int v2, Color color); -}; diff --git a/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp b/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp new file mode 100644 index 0000000..9e7a943 --- /dev/null +++ b/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp @@ -0,0 +1,102 @@ +#include "Rasterizer.h" +#include +#include "Color.h" +#include +#include + +namespace Rasterizer +{ + using namespace Math; + using namespace RenderData; + + void Rasterizer::DrawLineHorizontal(const Vector2Int v0, const Vector2Int v1, const Color color) + { + Vector2Int start = v0, end = v1; + if (v0.x > v1.x) + { + start = v1; + end = v0; + } + + int32_t dx = end.x - start.x; + int32_t dy = end.y - start.y; + + int32_t dir = dy < 0 ? -1 : 1; + dy *= dir; + + if (dx == 0) return; + + int32_t y = start.y; + int32_t p = 2 * dy - dx; + for (int32_t x = start.x; x <= end.x; x++) + { + frameBuffer.set_pixel(x, y, color.to_rgba()); + // 当 p >= 0 时,取上方像素点 + if (p >= 0) + { + y += dir; + p -= 2 * dx; + } + // 当 p < 0 时,取下方像素点 + p += 2 * dy; + } + } + + void Rasterizer::DrawLineVertical(const Vector2Int v0, const Vector2Int v1, const Color color) + { + Vector2Int start = v0, end = v1; + if (v0.y > v1.y) + { + start = v1; + end = v0; + } + + int32_t dx = end.x - start.x; + int32_t dy = end.y - start.y; + + int32_t dir = dx < 0 ? -1 : 1; + dx *= dir; + + if (dy == 0) return; + + int32_t x = start.x; + int32_t p = 2 * dx - dy; + for (int32_t y = start.y; y <= end.y; y++) + { + frameBuffer.set_pixel(x, y, color.to_rgba()); + // 当 p >= 0 时,取上方像素点 + if (p >= 0) + { + x += dir; + p -= 2 * dy; + } + // 当 p < 0 时,取下方像素点 + p += 2 * dx; + } + } + + /// + /// 在给定的起始点和结束点之间绘制一条线段,并使用指定的颜色填充 + /// + /// 直线顶点 1 + /// 直线顶点 2 + /// 绘制的颜色 + /// + /// bresenham 画线算法 + /// + void Rasterizer::DrawLine(const Vector2Int v0, const Vector2Int v1, const Color color) + { + int32_t x0 = v0.x, y0 = v0.y; + int32_t x1 = v1.x, y1 = v1.y; + + if (std::abs(x1 - x0) > std::abs(y1 - y0)) + { + DrawLineHorizontal(v0, v1, color); + } + else + { + DrawLineVertical(v0, v1, color); + } + } +} + diff --git a/CPU-Software-Renderer/Rasterizer/Rasterizer.h b/CPU-Software-Renderer/Rasterizer/Rasterizer.h new file mode 100644 index 0000000..58c291d --- /dev/null +++ b/CPU-Software-Renderer/Rasterizer/Rasterizer.h @@ -0,0 +1,21 @@ +#pragma once +#include "Color.h" +#include "FrameBuffer.h" +#include + +namespace Rasterizer +{ + class Rasterizer + { + private: + Core::FrameBuffer& frameBuffer; + + void DrawLineHorizontal(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color); + void DrawLineVertical(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color); + + public: + explicit Rasterizer(Core::FrameBuffer& frameBuffer) :frameBuffer(frameBuffer) {}; + + void DrawLine(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color); + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp new file mode 100644 index 0000000..07f2f07 --- /dev/null +++ b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp @@ -0,0 +1,25 @@ +#include "TriangleRasterizer.h" +#include +#include +#include "BoundingBox.h" +#include +using namespace RenderData; + +namespace Rasterizer +{ + void TriangleRasterizer::DrawTriangle2D(const Triangle2D triangle, const Color color) + { + auto boundingBox = triangle.get_boundingBox(); + + for (int x = boundingBox.min.x; x <= boundingBox.max.x; x++) + { + for (int y = boundingBox.min.y; y <= boundingBox.max.y; y++) + { + if (triangle.ContainsPixel(Vector2(x, y))) + { + frameBuffer.set_pixel(Vector2Int(x, y), color.to_rgba()); + } + } + } + } +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h new file mode 100644 index 0000000..bd51c20 --- /dev/null +++ b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h @@ -0,0 +1,19 @@ +#pragma once +#include "Triangle.h" +#include "Color.h" +#include "FrameBuffer.h" + +namespace Rasterizer +{ + class TriangleRasterizer + { + private: + Core::FrameBuffer& frameBuffer; + + + public: + explicit TriangleRasterizer(Core::FrameBuffer& frameBuffer) :frameBuffer(frameBuffer) {}; + + void DrawTriangle2D(const RenderData::Triangle2D triangle, const RenderData::Color color); + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/RenderData/BoundingBox.h b/CPU-Software-Renderer/RenderData/BoundingBox.h new file mode 100644 index 0000000..deb4bb5 --- /dev/null +++ b/CPU-Software-Renderer/RenderData/BoundingBox.h @@ -0,0 +1,14 @@ +#pragma once +#include "Vector2.h" + +namespace RenderData +{ + struct BoundingBox2D + { + Math::Vector2 min; + Math::Vector2 max; + + BoundingBox2D() : min(), max() {} + BoundingBox2D(const Math::Vector2 min, const Math::Vector2 max) : min(min), max(max) {} + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/RenderData/Color.h b/CPU-Software-Renderer/RenderData/Color.h new file mode 100644 index 0000000..1aa5ba6 --- /dev/null +++ b/CPU-Software-Renderer/RenderData/Color.h @@ -0,0 +1,34 @@ +#pragma once +#include + +namespace RenderData +{ + struct Color + { + uint8_t r; + uint8_t g; + uint8_t b; + uint8_t a; + Color() : r(0), g(0), b(0), a(0) {} + Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) + { + this->r = ClampToByte(r); + this->g = ClampToByte(g); + this->b = ClampToByte(b); + this->a = ClampToByte(a); + } + + uint32_t to_rgba() const + { + uint32_t value = (static_cast(r) << 24) | (static_cast(g) << 16) | (static_cast(b) << 8) | (static_cast(a)); + return value; + } + + static uint8_t ClampToByte(const int32_t value) + { + if (value < 0) return 0; + if (value > 255) return 255; + return static_cast(value); + } + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/RenderData/Triangle.h b/CPU-Software-Renderer/RenderData/Triangle.h new file mode 100644 index 0000000..a470230 --- /dev/null +++ b/CPU-Software-Renderer/RenderData/Triangle.h @@ -0,0 +1,47 @@ +#pragma once +#include "Vector2.h" +#include "BoundingBox.h" +#include + +namespace RenderData +{ + using namespace Math; + struct Triangle2D + { + Vector2 v0; + Vector2 v1; + Vector2 v2; + Triangle2D() : v0(), v1(), v2() {} + Triangle2D(const Vector2 a, const Vector2 b, const Vector2 c) : v0(a), v1(b), v2(c) {} + + BoundingBox2D get_boundingBox() const + { + Vector2 min, max; + min = Vector2(std::min({ v0.x, v1.x, v2.x }), min.y = std::min({ v0.y, v1.y, v2.y })); + max = Vector2(std::max({ v0.x, v1.x, v2.x }), max.y = std::max({ v0.y, v1.y, v2.y })); + + return BoundingBox2D(min, max); + } + + bool ContainsPixel(const Vector2 point) const + { + auto cross = [](const Vector2& p1, const Vector2& p2, const Vector2& p3) -> float + { + const float x1 = p2.x - p1.x; + const float y1 = p2.y - p1.y; + const float x2 = p3.x - p1.x; + const float y2 = p3.y - p1.y; + return x1 * y2 - y1 * x2; + }; + + const float c0 = cross(v0, v1, point); + const float c1 = cross(v1, v2, point); + const float c2 = cross(v2, v0, point); + + const bool hasNeg = (c0 < 0) || (c1 < 0) || (c2 < 0); + const bool hasPos = (c0 > 0) || (c1 > 0) || (c2 > 0); + + return !(hasNeg && hasPos); + } + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Camera.h b/CPU-Software-Renderer/Scene/Camera.h new file mode 100644 index 0000000..2e3c3c6 --- /dev/null +++ b/CPU-Software-Renderer/Scene/Camera.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Scene +{ + class Camera + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Mesh.h b/CPU-Software-Renderer/Scene/Mesh.h new file mode 100644 index 0000000..7ee047c --- /dev/null +++ b/CPU-Software-Renderer/Scene/Mesh.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Scene +{ + class Mesh + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Model.h b/CPU-Software-Renderer/Scene/Model.h new file mode 100644 index 0000000..838472a --- /dev/null +++ b/CPU-Software-Renderer/Scene/Model.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Scene +{ + class Model + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Transform.h b/CPU-Software-Renderer/Scene/Transform.h new file mode 100644 index 0000000..d6d4abe --- /dev/null +++ b/CPU-Software-Renderer/Scene/Transform.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Scene +{ + class Transform + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Vertex.h b/CPU-Software-Renderer/Scene/Vertex.h new file mode 100644 index 0000000..40c5f0b --- /dev/null +++ b/CPU-Software-Renderer/Scene/Vertex.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Scene +{ + class Vertex + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Shading/BlinnPhongShader.cpp b/CPU-Software-Renderer/Shading/BlinnPhongShader.cpp new file mode 100644 index 0000000..e69de29 diff --git a/CPU-Software-Renderer/Shading/BlinnPhongShader.h b/CPU-Software-Renderer/Shading/BlinnPhongShader.h new file mode 100644 index 0000000..399eaff --- /dev/null +++ b/CPU-Software-Renderer/Shading/BlinnPhongShader.h @@ -0,0 +1,7 @@ +#pragma once + +namespace Shading +{ + class BlinnPhongShader + {}; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/Shading/ShaderTypes.h b/CPU-Software-Renderer/Shading/ShaderTypes.h new file mode 100644 index 0000000..d5b03d2 --- /dev/null +++ b/CPU-Software-Renderer/Shading/ShaderTypes.h @@ -0,0 +1,9 @@ +#pragma once + +namespace Shading +{ + enum class ShaderType + { + BlinnPhong, + }; +} \ No newline at end of file diff --git a/CPU-Software-Renderer/StructType.h b/CPU-Software-Renderer/StructType.h deleted file mode 100644 index 71d4d27..0000000 --- a/CPU-Software-Renderer/StructType.h +++ /dev/null @@ -1,142 +0,0 @@ -#pragma once -#include -#include - -namespace CustomStructs -{ - struct Vector2Int - { - int32_t x; - int32_t y; - Vector2Int() : x(0), y(0) {} - Vector2Int(int32_t x, int32_t y) : x(x), y(y) {} - - /// - /// 交换 x 和 y 的值 - /// - void swap() - { - std::swap(x, y); - } - - /// - /// 交换当前 Vector2Int 对象与另一个 Vector2Int 对象的 x 和 y 的值 - /// - /// 要交换的对象 - static void swap(Vector2Int& vec1, Vector2Int& vec2) - { - std::swap(vec1.x, vec2.x); - std::swap(vec1.y, vec2.y); - } - }; - - struct Vector2 - { - float x; - float y; - Vector2() : x(0), y(0) {} - Vector2(float x, float y) : x(x), y(y) {} - Vector2(const Vector2Int& v) : x(static_cast(v.x)), y(static_cast(v.y)) {} - - /// - /// 交换 x 和 y 的值 - /// - void swap() - { - std::swap(x, y); - } - - /// - /// 交换当前 Vector2 对象与另一个 Vector2 对象的 x 和 y 的值 - /// - /// 要交换的对象 - static void swap(Vector2& vec1, Vector2& vec2) - { - std::swap(vec1.x, vec2.x); - std::swap(vec1.y, vec2.y); - } - }; - - struct Vector3Int - { - int32_t x; - int32_t y; - int32_t z; - Vector3Int() : x(0), y(0), z(0) {} - Vector3Int(int32_t x, int32_t y, int32_t z) : x(x), y(y), z(z) {} - }; - - struct Vector3 - { - float x; - float y; - float z; - Vector3() : x(0), y(0), z(0) {} - Vector3(float x, float y, float z) : x(x), y(y), z(z) {} - Vector3(const Vector3Int& v) : x(static_cast(v.x)), y(static_cast(v.y)), z(static_cast(v.z)) {} - }; - - struct Triangle - { - Vector2Int a; - Vector2Int b; - Vector2Int c; - Triangle() : a(), b(), c() {} - Triangle(const Vector2Int a, const Vector2Int b, const Vector2Int c) : a(a), b(b), c(c) {} - - bool IsInTriangle(const Vector2Int point) const - { - auto cross = [](const Vector2Int& p1, const Vector2Int& p2, const Vector2Int& p3) -> int64_t - { - const int64_t x1 = static_cast(p2.x) - p1.x; - const int64_t y1 = static_cast(p2.y) - p1.y; - const int64_t x2 = static_cast(p3.x) - p1.x; - const int64_t y2 = static_cast(p3.y) - p1.y; - return x1 * y2 - y1 * x2; - }; - - const int64_t c0 = cross(a, b, point); - const int64_t c1 = cross(b, c, point); - const int64_t c2 = cross(c, a, point); - - const bool hasNeg = (c0 < 0) || (c1 < 0) || (c2 < 0); - const bool hasPos = (c0 > 0) || (c1 > 0) || (c2 > 0); - - return !(hasNeg && hasPos); - } - }; - - struct Color - { - uint8_t r; - uint8_t g; - uint8_t b; - uint8_t a; - Color() : r(0), g(0), b(0), a(0) {} - Color(uint8_t r, uint8_t g, uint8_t b, uint8_t a) - { - this->r = ClampToByte(static_cast(r)); - this->g = ClampToByte(static_cast(g)); - this->b = ClampToByte(static_cast(b)); - this->a = ClampToByte(static_cast(a)); - } - - operator uint32_t() const - { - return - ( - (static_cast(r) << 24) | - (static_cast(g) << 16) | - (static_cast(b) << 8) | - (static_cast(a)) - ); - } - - static uint8_t ClampToByte(const int32_t value) - { - if (value < 0) return 0; - if (value > 255) return 255; - return static_cast(value); - } - }; -} diff --git a/CPU-Software-Renderer/main.cpp b/CPU-Software-Renderer/main.cpp index e5ad45d..a20b2d2 100644 --- a/CPU-Software-Renderer/main.cpp +++ b/CPU-Software-Renderer/main.cpp @@ -1,17 +1,17 @@ -#include -#include +#include #include -#include "Rasterization.h" -#include "StructType.h" #include "SDL_video.h" #include "SDL_render.h" #include #include "SDL_error.h" -#include +#include "Vector2.h" +#include "Color.h" +#include "FrameBuffer.h" +#include "Rasterizer.h" const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO; -const int width = 800; -const int height = 600; +const int32_t width = 800; +const int32_t height = 600; SDL_Window* window = nullptr; SDL_Renderer* renderer = nullptr; @@ -90,20 +90,25 @@ int main(int argc, char* argv[]) if (!EnsureSDLWindow()) return -1; if (!EnsureRenderer()) return -1; - + SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height); - Rasterization* rasterization = new Rasterization(width, height); + Core::FrameBuffer frameBuffer(width, height); + Rasterizer::Rasterizer rasterizer(frameBuffer); - CustomStructs::Vector2Int v0(100, 100); - CustomStructs::Vector2Int v1(300, 100); - CustomStructs::Vector2Int v2(300, 400); - CustomStructs::Color color1(255, 0, 0, 255); - rasterization->DrawFillTriangle(v0, v1, v2, color1); + Math::Vector2Int v0(100, 100); + Math::Vector2Int v1(300, 100); + Math::Vector2Int v2(300, 400); + RenderData::Color color0(255, 0, 0, 255); + RenderData::Color color1(0, 255, 0, 255); + RenderData::Color color2(0, 0, 255, 255); + rasterizer.DrawLine(v0, v1, color0); + rasterizer.DrawLine(v1, v2, color1); + rasterizer.DrawLine(v2, v0, color2); while (true) { - SDL_UpdateTexture(texture, nullptr, rasterization->frameBuffer.data(), width * sizeof(uint32_t)); + SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t)); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, nullptr, nullptr); diff --git a/README.md b/README.md index 332c609..0ca0ee4 100644 --- a/README.md +++ b/README.md @@ -1,158 +1,161 @@ # CPU Software Renderer -一个基于 C++ 和 SDL2 的小型 CPU 渲染器练习项目。当前版本聚焦在最基础的 2D 光栅化能力上,通过 CPU 维护帧缓冲区,再将像素数据上传到 SDL 纹理并显示到窗口中。 +一个基于 C++20 和 SDL2 的 CPU 软光栅化练习项目。 -项目目标不是调用现成图形 API 去画三角形,而是从像素写入、直线绘制、三角形光栅化开始,逐步补齐一个软件渲染器所需的核心 pipeline。 - -## 项目定位 - -- 使用 CPU 完成像素级绘制和光栅化 -- 使用 SDL2 负责窗口创建、纹理更新和屏幕展示 -- 以最小可运行工程为基础,逐步扩展到 3D 渲染流程 +这个仓库当前还处在早期阶段,重点不是调用现成图形 API 画三角形,而是自己维护帧缓冲、自己做基础光栅化,并逐步搭起一个小型 software renderer 的骨架。 ## 当前状态 -当前代码已经具备这些基础能力: +目前已经完成或具备雏形的部分: -- 基于 `frameBuffer` 的软件像素输出 -- `SetPixel` 像素写入 -- Bresenham 直线绘制 -- 三角形线框绘制 -- 三角形包围盒扫描填充 -- SDL2 窗口创建、纹理更新与显示 +- 使用 SDL2 创建窗口、纹理和显示循环 +- 在 CPU 侧维护 `FrameBuffer` +- 像素写入接口 `set_pixel` +- 基础直线光栅化模块 `Rasterizer` +- 2D 三角形、包围盒、颜色等基础数据结构 +- 独立的 `TriangleRasterizer` 模块,用于后续填充三角形 -当前 `main.cpp` 中已经可以直接绘制一个填充三角形,用来验证基础光栅化逻辑。 +当前 `main.cpp` 主要用于验证基础链路,程序启动后会在窗口中绘制三条彩色线段组成的三角形轮廓。 -## 技术选型 +还没有完成的核心能力: -- 语言标准:C++20 -- 工程类型:Visual Studio C++ 项目(`.vcxproj`) -- 渲染方式:CPU Software Rasterization -- 窗口与展示层:SDL2 -- 开发环境:Windows + MSVC +- 完整接入实心三角形渲染流程 +- MVP 变换 +- 深度测试 +- 重心坐标插值 +- 光照 +- OBJ 加载 +- 可交互相机 -选择 SDL2 的原因很直接:它只负责跨平台窗口和像素展示,足够轻量,不会替代掉软件渲染器本身的核心逻辑。 - -## 依赖 - -项目当前依赖很少: - -- SDL2 - -仓库内已经包含 SDL2 相关文件: - -- 头文件目录:`libs/SDL2/include` -- x64 库目录:`libs/SDL2/lib/x64` -- 运行时动态库:`libs/SDL2/SDL2.dll` - -## 构建环境 - -当前工程配置以 Windows 本地开发为主,推荐环境: - -- Visual Studio 2022 -- MSVC 工具链 -- Windows 10/11 SDK - -项目文件中启用了: - -- `stdcpp20` -- `PlatformToolset=v145` - -## 构建方式 - -1. 使用 Visual Studio 打开 `CPU-Software-Renderer.vcxproj` -2. 选择 `Debug | x64` -3. 直接构建并运行 - -说明: - -- 当前工程的 SDL2 include/lib 路径写在 `.vcxproj` 里 -- 现有配置里 `Debug | x64` 已配置 SDL2 头文件和链接目录 -- 如果你移动了仓库位置,可能需要同步调整工程中的绝对路径 - -## 当前工程结构 +## 当前结构 ```text CPU-Software-Renderer/ -├─ main.cpp # 程序入口,SDL 初始化与显示循环 -├─ Rasterization.h/.cpp # 帧缓冲、画线、三角形绘制 -├─ StructType.h # 向量、三角形、颜色等基础结构 -├─ Color.h/.cpp # 颜色相关封装 -├─ libs/SDL2/ # SDL2 头文件、库和运行时文件 -└─ TODO.md # 后续功能路线图 +├─ CPU-Software-Renderer/ +│ ├─ main.cpp +│ ├─ Core/ +│ │ ├─ FrameBuffer.h/.cpp +│ │ ├─ DepthBuffer.h/.cpp +│ │ └─ Renderer.h/.cpp +│ ├─ Math/ +│ │ ├─ Vector2.h +│ │ ├─ Vector3.h +│ │ ├─ Vector4.h +│ │ ├─ Matrix4x4.h +│ │ └─ MathUtil.h +│ ├─ RenderData/ +│ │ ├─ Color.h +│ │ ├─ BoundingBox.h +│ │ └─ Triangle.h +│ ├─ Rasterizer/ +│ │ ├─ Rasterizer.h/.cpp +│ │ └─ TriangleRasterizer.h/.cpp +│ ├─ Scene/ +│ │ ├─ Vertex.h +│ │ ├─ Mesh.h +│ │ ├─ Transform.h +│ │ ├─ Model.h +│ │ └─ Camera.h +│ ├─ Asset/ +│ │ └─ ObjLoader.h/.cpp +│ ├─ Shading/ +│ │ ├─ ShaderTypes.h +│ │ └─ BlinnPhongShader.h/.cpp +│ ├─ libs/SDL2/ +│ └─ TODO.md +└─ README.md ``` -## 已实现模块 +## 模块说明 -### 1. Frame Buffer +### Core -渲染结果先写入 CPU 侧的 `std::vector` 帧缓冲区,再通过 `SDL_UpdateTexture` 更新到纹理。 +- `FrameBuffer`:管理 CPU 侧颜色缓冲 +- `DepthBuffer`:为后续深度测试预留 +- `Renderer`:为后续统一调度渲染流程预留 -### 2. Rasterizer +### Math -当前已经实现: +- 放通用数学类型 +- 当前以 `Vector2`、`Vector2Int`、`Vector3` 为主 +- `Vector4` 和 `Matrix4x4` 目前还是占位 -- `DrawLine` -- `DrawWireTriangle` -- `DrawFillTriangle` +### RenderData -其中直线绘制使用 Bresenham 思路,填充三角形使用包围盒扫描配合点在三角形内测试。 +- 放渲染领域数据,而不是底层数学类型 +- 当前包含 `Color`、`BoundingBox2D`、`Triangle2D` -### 3. 基础数学类型 +### Rasterizer -当前工程已经定义了: +- `Rasterizer`:负责基础线段光栅化 +- `TriangleRasterizer`:负责三角形填充相关逻辑 -- `Vector2Int` -- `Vector2` -- `Vector3Int` -- `Vector3` -- `Triangle` -- `Color` +### Scene / Asset / Shading -这些结构会作为后续扩展 MVP 变换、深度测试、法线和属性插值的基础。 +这些目录已经预留出来了,方便后面扩展到 3D 渲染流程,但当前多数还是占位类。 + +## 构建环境 + +- Windows +- Visual Studio 2022 +- MSVC +- C++20 +- SDL2 + +工程文件是 Visual Studio 的 `.vcxproj`。 + +## 依赖 + +仓库内已经包含 SDL2: + +- 头文件:`CPU-Software-Renderer/libs/SDL2/include` +- Win32 库:`CPU-Software-Renderer/libs/SDL2/lib/x86` +- x64 库:`CPU-Software-Renderer/libs/SDL2/lib/x64` + +## 构建与运行 + +1. 用 Visual Studio 打开 `CPU-Software-Renderer/CPU-Software-Renderer.vcxproj` +2. 选择 `Debug` 或 `Release` +3. 选择 `Win32` 或 `x64` +4. 构建并运行 + +说明: + +- 工程里已经分别配置了 Win32 和 x64 的 SDL2 include/lib 路径 +- 运行时需要让程序找到与平台匹配的 `SDL2.dll` +- 如果你移动了仓库位置,可能需要同步检查 `.vcxproj` 里的绝对路径 + +## 设计取向 + +当前代码结构遵循一个比较简单的原则: + +- 纯数据类型放在 `Math` 和 `RenderData` +- 做事的模块放在 `Core`、`Rasterizer`、`Asset`、`Shading` +- 先把 2D 基础链路理顺,再逐步加 3D pipeline + +这意味着现阶段更强调“结构和边界先理清”,而不是一次性把所有功能堆进去。 ## 后续计划 -1. 完成稳定的三角形光栅化基础 -2. 加入 MVP 变换,实现 3D 顶点到屏幕空间的投影 -3. 加入 Z-buffer,解决遮挡关系 -4. 实现重心坐标属性插值 -5. 实现 Blinn-Phong 光照 -6. 支持 OBJ 模型加载 -7. 实现相机控制 +按 `TODO.md` 的路线,后续会逐步补齐: -如果后续继续扩展,比较值得加入的方向还有: +1. 稳定的三角形填充 +2. MVP 变换 +3. Z-buffer +4. 属性插值 +5. Blinn-Phong 光照 +6. OBJ 模型加载 +7. 相机控制 + +如果后续继续扩展,还可以考虑: - Texture Mapping -- MSAA 抗锯齿 +- MSAA - 更完整的数学库 -- Shader 结构拆分 - -## 这个项目最终会变成什么 - -按当前路线推进后,这个项目会逐步具备一个小型软件渲染器的基本结构: - -```text -Renderer -├─ Rasterizer -├─ Shader -├─ Camera -├─ Math -├─ OBJLoader -└─ ZBuffer -``` - -最终目标是实现一个不依赖 GPU 光栅化管线、但具备基本 3D 渲染能力的 CPU Renderer。 - -## 适合用来做什么 - -这个项目比较适合: - -- 学习软件渲染器的基本实现方式 -- 理解三角形光栅化、像素写入和屏幕空间坐标 -- 作为后续学习图形学、渲染 pipeline、光照和模型加载的实验场 -- 用于整理个人图形学项目经历 +- 更清晰的 shader 输入输出结构 ## 说明 -当前项目仍在早期阶段,很多功能还处于逐步实现中。README 中的“后续计划”部分表示目标路线,不代表这些能力已经全部完成。 +这个项目现在更接近“正在搭骨架的 renderer”,不是一个已经完成的 3D 渲染器。 + +README 中提到的很多模块和目录,目的是反映当前的架构方向;其中一部分已经有初步实现,另一部分还只是为后续阶段预留。