diff --git a/CPU-Software-Renderer/Core/DepthBuffer.cpp b/CPU-Software-Renderer/Core/DepthBuffer.cpp index 63ea6bd..4fea6ad 100644 --- a/CPU-Software-Renderer/Core/DepthBuffer.cpp +++ b/CPU-Software-Renderer/Core/DepthBuffer.cpp @@ -1,16 +1,27 @@ #include "DepthBuffer.h" #include #include +#include namespace Core { - void DepthBuffer::clear(const uint8_t depth) + void DepthBuffer::clear(const float depth) { std::fill(buffer.begin(), buffer.end(), depth); } + float DepthBuffer::get_depth(const int32_t x, const int32_t y) const + { + if (x < 0 || x >= width || y < 0 || y >= height) + { + return INFINITY; + } + // Row-major layout with y = 0 on the first row, matching a top-left screen origin. + size_t index = static_cast(y) * width + x; + return buffer.at(index); + } - void DepthBuffer::set_pixel(const int32_t x, const int32_t y, const uint8_t depth) + void DepthBuffer::set_depth(const int32_t x, const int32_t y, const float depth) { if (x < 0 || x >= width || y < 0 || y >= height) { diff --git a/CPU-Software-Renderer/Core/DepthBuffer.h b/CPU-Software-Renderer/Core/DepthBuffer.h index ca508d5..a569cc4 100644 --- a/CPU-Software-Renderer/Core/DepthBuffer.h +++ b/CPU-Software-Renderer/Core/DepthBuffer.h @@ -1,8 +1,8 @@ #pragma once #include #include -#include "Color.h" #include "Vector2.h" +#include namespace Core { @@ -11,7 +11,7 @@ namespace Core private: int32_t width; int32_t height; - std::vector buffer; + std::vector buffer; public: int32_t get_width() const { return width; } @@ -22,15 +22,16 @@ namespace Core void* get_buffer() const { return (void*)buffer.data(); } - DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector(width* height, 0)) {} + DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector(width* height, INFINITY)) {} - void clear(const uint8_t depth); + void clear(const float depth = INFINITY); - void set_pixel(const Math::Vector2Int position, const uint8_t depth) - { - set_pixel(position.x, position.y, depth); - } + float get_depth(const Math::Vector2Int position) const { return get_depth(position.x, position.y); } - void set_pixel(const int32_t x, const int32_t y, const uint8_t depth); + float get_depth(const int32_t x, const int32_t y) const; + + void set_depth(const Math::Vector2Int position, const float depth) { set_depth(position.x, position.y, depth); } + + void set_depth(const int32_t x, const int32_t y, const float depth); }; }; diff --git a/CPU-Software-Renderer/Math/MathUtil.h b/CPU-Software-Renderer/Math/MathUtil.h index f81fc9d..b1c879c 100644 --- a/CPU-Software-Renderer/Math/MathUtil.h +++ b/CPU-Software-Renderer/Math/MathUtil.h @@ -2,6 +2,7 @@ #include "Matrix4x4.h" #include "Vector3.h" #include +#include "Vector2.h" namespace Math { @@ -99,5 +100,10 @@ namespace Math { return vec1.cross(vec2); } + + static Vector3 cross(const Vector2& vec1, const Vector2& vec2) + { + return vec1.cross(vec2); + } }; } diff --git a/CPU-Software-Renderer/Math/Vector2.h b/CPU-Software-Renderer/Math/Vector2.h index d3176ac..69c4b56 100644 --- a/CPU-Software-Renderer/Math/Vector2.h +++ b/CPU-Software-Renderer/Math/Vector2.h @@ -2,9 +2,13 @@ #include #include #include +#include "Vector3.h" namespace Math { + struct Vector2; + struct Vector2Int; + struct Vector2Int { int32_t x = 0; @@ -31,6 +35,7 @@ namespace Math Vector2() : x(0), y(0) {} Vector2(float x, float y) : x(x), y(y) {} + Vector2(const Vector2Int& other) :x(other.x), y(other.y) {} /// /// 交换当前 Vector2 对象与另一个 Vector2 对象的 x 和 y 的值 @@ -46,5 +51,10 @@ namespace Math { return Vector2Int(static_cast(std::lround(x)), static_cast(std::lround(y))); } + + Vector3 cross(const Vector2& other) const + { + return Vector3(0, 0, this->x * other.y - this->y * other.x); + } }; -} \ No newline at end of file +} diff --git a/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp b/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp index 9e7a943..21317c1 100644 --- a/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp +++ b/CPU-Software-Renderer/Rasterizer/Rasterizer.cpp @@ -1,102 +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); - } - } -} - +#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 index 58c291d..0a0d9ab 100644 --- a/CPU-Software-Renderer/Rasterizer/Rasterizer.h +++ b/CPU-Software-Renderer/Rasterizer/Rasterizer.h @@ -1,21 +1,24 @@ -#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); - }; +#pragma once +#include "Color.h" +#include "FrameBuffer.h" +#include "DepthBuffer.h" +#include + +namespace Rasterizer +{ + class Rasterizer + { + private: + Core::FrameBuffer* frameBuffer; + Core::DepthBuffer* depthBuffer; + + 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), depthBuffer(nullptr) {} + explicit Rasterizer(Core::FrameBuffer* frameBuffer, Core::DepthBuffer* depthBuffer) :frameBuffer(frameBuffer), depthBuffer(depthBuffer) {} + + 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 index 65bea00..7efc132 100644 --- a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp +++ b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp @@ -1,31 +1,61 @@ -#include "TriangleRasterizer.h" -#include -#include -#include "BoundingBox.h" -#include -#include -#include - -namespace Rasterizer -{ - void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color) - { - auto boundingBox = triangle.get_boundingBox(); - - int32_t minX = std::max(0, boundingBox.min.x); - int32_t maxX = std::min(frameBuffer.get_width() - 1, boundingBox.max.x); - int32_t minY = std::max(0, boundingBox.min.y); - int32_t maxY = std::min(frameBuffer.get_height() - 1, boundingBox.max.y); - +#include "TriangleRasterizer.h" +#include +#include +#include "BoundingBox.h" +#include +#include +#include + +namespace Rasterizer +{ + void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color) + { + using namespace Math; + + auto boundingBox = triangle.get_boundingBox(); + + int32_t minX = std::max(0, boundingBox.min.x); + int32_t maxX = std::min(frameBuffer->get_width() - 1, boundingBox.max.x); + int32_t minY = std::max(0, boundingBox.min.y); + int32_t maxY = std::min(frameBuffer->get_height() - 1, boundingBox.max.y); + for (int x = minX; x <= maxX; x++) { for (int y = minY; y <= maxY; y++) { - if (triangle.ContainsPixel(Math::Vector2Int(x, y))) + Vector2 samplePoint(x + 0.5f, y + 0.5f); + float w0 = 0, w1 = 0, w2 = 0; + if (!triangle.get_barycentric(samplePoint, w0, w1, w2)) { - frameBuffer.set_pixel(Math::Vector2Int(x, y), color.to_rgba()); + continue; + } + + if ((w0 < 0) || (w1 < 0) || (w2 < 0)) + { + continue; + } + + const float depth = + w0 * triangle.v0.position.z + + w1 * triangle.v1.position.z + + w2 * triangle.v2.position.z; + + if (depthBuffer) + { + // 深度越小离相机越近 + if (depthBuffer->get_depth(x, y) < depth) + { + continue; + } + + depthBuffer->set_depth(x, y, depth); + frameBuffer->set_pixel(x, y, color.to_rgba()); + } + else + { + frameBuffer->set_pixel(x, y, color.to_rgba()); } } } - } + } } diff --git a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h index 9863337..38bff83 100644 --- a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h +++ b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.h @@ -2,17 +2,20 @@ #include "Triangle.h" #include "Color.h" #include "FrameBuffer.h" +#include namespace Rasterizer { class TriangleRasterizer { private: - Core::FrameBuffer& frameBuffer; + Core::FrameBuffer* frameBuffer; + Core::DepthBuffer* depthBuffer; public: - explicit TriangleRasterizer(Core::FrameBuffer& frameBuffer) :frameBuffer(frameBuffer) {}; + explicit TriangleRasterizer(Core::FrameBuffer* frameBuffer) :frameBuffer(frameBuffer), depthBuffer(nullptr) {}; + explicit TriangleRasterizer(Core::FrameBuffer* frameBuffer, Core::DepthBuffer* depthBuffer) :frameBuffer(frameBuffer), depthBuffer(depthBuffer) {}; void DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color); }; diff --git a/CPU-Software-Renderer/RenderData/Triangle.h b/CPU-Software-Renderer/RenderData/Triangle.h index 0edab97..dae9c99 100644 --- a/CPU-Software-Renderer/RenderData/Triangle.h +++ b/CPU-Software-Renderer/RenderData/Triangle.h @@ -6,6 +6,7 @@ #include #include #include +#include namespace RenderData { @@ -32,28 +33,35 @@ namespace RenderData return BoundingBox2D(min, max); } - bool ContainsPixel(const Math::Vector2Int point) const + /// + /// 给定屏幕像素坐标,输出该点的面积坐标 + /// + /// 要计算的屏幕像素点 + /// 面积坐标的 x 分量(引用) + /// 面积坐标的 y 分量(引用) + /// 面积坐标的 z 分量(引用) + /// 是否计算成功 + bool get_barycentric(const Math::Vector2& p, float& w0, float& w1, float& w2) const { - using namespace Scene; + using namespace Math; - auto cross = [](const Vertex& v1, const Vertex& v2, const Math::Vector2Int& point) -> float - { - Scene::Vertex v3(Math::Vector3(point.x, point.y, 0)); - const float x1 = v2.position.x - v1.position.x; - const float y1 = v2.position.y - v1.position.y; - const float x2 = v3.position.x - v1.position.x; - const float y2 = v3.position.y - v1.position.y; - return x1 * y2 - y1 * x2; - }; + const float x0 = v0.position.x; + const float y0 = v0.position.y; + const float x1 = v1.position.x; + const float y1 = v1.position.y; + const float x2 = v2.position.x; + const float y2 = v2.position.y; - const float c0 = cross(v0, v1, point); - const float c1 = cross(v1, v2, point); - const float c2 = cross(v2, v0, point); + const float square2D = (y1 - y2) * (x0 - x2) + (x2 - x1) * (y0 - y2); + if (std::abs(square2D) < 1e-6f) + { + return false; + } - const bool hasNeg = (c0 < 0) || (c1 < 0) || (c2 < 0); - const bool hasPos = (c0 > 0) || (c1 > 0) || (c2 > 0); - - return !(hasNeg && hasPos); + w0 = ((y1 - y2) * (p.x - x2) + (x2 - x1) * (p.y - y2)) / square2D; + w1 = ((y2 - y0) * (p.x - x2) + (x0 - x2) * (p.y - y2)) / square2D; + w2 = 1.0f - w0 - w1; + return true; } }; } \ No newline at end of file diff --git a/CPU-Software-Renderer/main.cpp b/CPU-Software-Renderer/main.cpp index 5c3dbd3..e7d5f25 100644 --- a/CPU-Software-Renderer/main.cpp +++ b/CPU-Software-Renderer/main.cpp @@ -23,6 +23,7 @@ #include #include #include "Vertex.h" +#include "DepthBuffer.h" const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO; const int32_t width = 800; @@ -135,12 +136,6 @@ struct CubeTriangle std::array vertices; }; -struct TriangleDrawCommand -{ - RenderData::Triangle triangle; - float averageViewSpaceZ = 0.0f; -}; - static ProjectedVertex ProjectToScreen( const Math::Vector3& vertex, const Math::Matrix4x4& mvp, @@ -208,9 +203,10 @@ int main(int argc, char* argv[]) if (!EnsureTexture()) return -1; - Core::FrameBuffer frameBuffer(width, height); - Rasterizer::Rasterizer rasterizer(frameBuffer); - Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer); + Core::FrameBuffer* frameBuffer = new Core::FrameBuffer(width, height); + Core::DepthBuffer* depthBuffer = new Core::DepthBuffer(width, height); + Rasterizer::Rasterizer rasterizer(frameBuffer, depthBuffer); + Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer, depthBuffer); Scene::Camera camera; camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f); @@ -265,7 +261,8 @@ int main(int argc, char* argv[]) } } - frameBuffer.clear(clearColor); + frameBuffer->clear(clearColor); + depthBuffer->clear(); const float timeSeconds = static_cast(SDL_GetTicks()) * 0.001f; const Math::Matrix4x4 model = @@ -291,7 +288,7 @@ int main(int argc, char* argv[]) visibleFaces[faceIndex] = IsFaceVisible(cubeFaces[faceIndex], viewSpaceVertices); } - std::array drawCommands; + std::array drawTriangles; size_t drawCommandCount = 0; for (const CubeTriangle& cubeTriangle : cubeTriangles) { @@ -312,27 +309,17 @@ int main(int argc, char* argv[]) const Math::Vector3& viewV1 = viewSpaceVertices[cubeTriangle.vertices[1]]; const Math::Vector3& viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]]; - drawCommands[drawCommandCount++] = TriangleDrawCommand{ + drawTriangles[drawCommandCount++] = RenderData::Triangle( Scene::Vertex(v0.screen), Scene::Vertex(v1.screen), Scene::Vertex(v2.screen) - ), - (viewV0.z + viewV1.z + viewV2.z) / 3.0f - }; + ); } - std::sort( - drawCommands.begin(), - drawCommands.begin() + drawCommandCount, - [](const TriangleDrawCommand& a, const TriangleDrawCommand& b) - { - return a.averageViewSpaceZ < b.averageViewSpaceZ; - }); - for (size_t i = 0; i < drawCommandCount; ++i) { - triangleRasterizer.DrawTriangle2D(drawCommands[i].triangle, cubeColor); + triangleRasterizer.DrawTriangle2D(drawTriangles[i], cubeColor); } for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex) @@ -361,7 +348,7 @@ int main(int argc, char* argv[]) } } - SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t)); + SDL_UpdateTexture(texture, nullptr, frameBuffer->get_buffer(), width * sizeof(uint32_t)); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderPresent(renderer);