apply depthBuffer

This commit is contained in:
SepComet 2026-03-20 19:53:08 +08:00
parent cc203c40f3
commit e089619dcf
10 changed files with 260 additions and 201 deletions

View File

@ -1,16 +1,27 @@
#include "DepthBuffer.h" #include "DepthBuffer.h"
#include <cstdint> #include <cstdint>
#include <algorithm> #include <algorithm>
#include <cmath>
namespace Core namespace Core
{ {
void DepthBuffer::clear(const uint8_t depth) void DepthBuffer::clear(const float depth)
{ {
std::fill(buffer.begin(), buffer.end(), 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<size_t>(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) if (x < 0 || x >= width || y < 0 || y >= height)
{ {

View File

@ -1,8 +1,8 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <vector> #include <vector>
#include "Color.h"
#include "Vector2.h" #include "Vector2.h"
#include <cmath>
namespace Core namespace Core
{ {
@ -11,7 +11,7 @@ namespace Core
private: private:
int32_t width; int32_t width;
int32_t height; int32_t height;
std::vector<uint8_t> buffer; std::vector<float> buffer;
public: public:
int32_t get_width() const { return width; } int32_t get_width() const { return width; }
@ -22,15 +22,16 @@ namespace Core
void* get_buffer() const { return (void*)buffer.data(); } void* get_buffer() const { return (void*)buffer.data(); }
DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector<uint8_t>(width* height, 0)) {} DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector<float>(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) float get_depth(const Math::Vector2Int position) const { return get_depth(position.x, position.y); }
{
set_pixel(position.x, position.y, depth);
}
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);
}; };
}; };

View File

@ -2,6 +2,7 @@
#include "Matrix4x4.h" #include "Matrix4x4.h"
#include "Vector3.h" #include "Vector3.h"
#include <cmath> #include <cmath>
#include "Vector2.h"
namespace Math namespace Math
{ {
@ -99,5 +100,10 @@ namespace Math
{ {
return vec1.cross(vec2); return vec1.cross(vec2);
} }
static Vector3 cross(const Vector2& vec1, const Vector2& vec2)
{
return vec1.cross(vec2);
}
}; };
} }

View File

@ -2,9 +2,13 @@
#include <cstdint> #include <cstdint>
#include <utility> #include <utility>
#include <cmath> #include <cmath>
#include "Vector3.h"
namespace Math namespace Math
{ {
struct Vector2;
struct Vector2Int;
struct Vector2Int struct Vector2Int
{ {
int32_t x = 0; int32_t x = 0;
@ -31,6 +35,7 @@ namespace Math
Vector2() : x(0), y(0) {} Vector2() : x(0), y(0) {}
Vector2(float x, float y) : x(x), y(y) {} Vector2(float x, float y) : x(x), y(y) {}
Vector2(const Vector2Int& other) :x(other.x), y(other.y) {}
/// <summary> /// <summary>
/// 交换当前 Vector2 对象与另一个 Vector2 对象的 x 和 y 的值 /// 交换当前 Vector2 对象与另一个 Vector2 对象的 x 和 y 的值
@ -46,5 +51,10 @@ namespace Math
{ {
return Vector2Int(static_cast<int32_t>(std::lround(x)), static_cast<int32_t>(std::lround(y))); return Vector2Int(static_cast<int32_t>(std::lround(x)), static_cast<int32_t>(std::lround(y)));
} }
Vector3 cross(const Vector2& other) const
{
return Vector3(0, 0, this->x * other.y - this->y * other.x);
}
}; };
} }

View File

@ -30,7 +30,7 @@ namespace Rasterizer
int32_t p = 2 * dy - dx; int32_t p = 2 * dy - dx;
for (int32_t x = start.x; x <= end.x; x++) for (int32_t x = start.x; x <= end.x; x++)
{ {
frameBuffer.set_pixel(x, y, color.to_rgba()); frameBuffer->set_pixel(x, y, color.to_rgba());
// 当 p >= 0 时,取上方像素点 // 当 p >= 0 时,取上方像素点
if (p >= 0) if (p >= 0)
{ {
@ -63,7 +63,7 @@ namespace Rasterizer
int32_t p = 2 * dx - dy; int32_t p = 2 * dx - dy;
for (int32_t y = start.y; y <= end.y; y++) for (int32_t y = start.y; y <= end.y; y++)
{ {
frameBuffer.set_pixel(x, y, color.to_rgba()); frameBuffer->set_pixel(x, y, color.to_rgba());
// 当 p >= 0 时,取上方像素点 // 当 p >= 0 时,取上方像素点
if (p >= 0) if (p >= 0)
{ {

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "Color.h" #include "Color.h"
#include "FrameBuffer.h" #include "FrameBuffer.h"
#include "DepthBuffer.h"
#include <Vector2.h> #include <Vector2.h>
namespace Rasterizer namespace Rasterizer
@ -8,13 +9,15 @@ namespace Rasterizer
class Rasterizer class Rasterizer
{ {
private: private:
Core::FrameBuffer& frameBuffer; Core::FrameBuffer* frameBuffer;
Core::DepthBuffer* depthBuffer;
void DrawLineHorizontal(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color); 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); void DrawLineVertical(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color);
public: public:
explicit Rasterizer(Core::FrameBuffer& frameBuffer) :frameBuffer(frameBuffer) {}; 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); void DrawLine(const Math::Vector2Int v0, const Math::Vector2Int v1, const RenderData::Color color);
}; };

View File

@ -10,20 +10,50 @@ namespace Rasterizer
{ {
void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color) void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color)
{ {
using namespace Math;
auto boundingBox = triangle.get_boundingBox(); auto boundingBox = triangle.get_boundingBox();
int32_t minX = std::max(0, boundingBox.min.x); int32_t minX = std::max(0, boundingBox.min.x);
int32_t maxX = std::min(frameBuffer.get_width() - 1, boundingBox.max.x); int32_t maxX = std::min(frameBuffer->get_width() - 1, boundingBox.max.x);
int32_t minY = std::max(0, boundingBox.min.y); int32_t minY = std::max(0, boundingBox.min.y);
int32_t maxY = std::min(frameBuffer.get_height() - 1, boundingBox.max.y); int32_t maxY = std::min(frameBuffer->get_height() - 1, boundingBox.max.y);
for (int x = minX; x <= maxX; x++) for (int x = minX; x <= maxX; x++)
{ {
for (int y = minY; y <= maxY; y++) 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());
} }
} }
} }

View File

@ -2,17 +2,20 @@
#include "Triangle.h" #include "Triangle.h"
#include "Color.h" #include "Color.h"
#include "FrameBuffer.h" #include "FrameBuffer.h"
#include <DepthBuffer.h>
namespace Rasterizer namespace Rasterizer
{ {
class TriangleRasterizer class TriangleRasterizer
{ {
private: private:
Core::FrameBuffer& frameBuffer; Core::FrameBuffer* frameBuffer;
Core::DepthBuffer* depthBuffer;
public: 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); void DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color);
}; };

View File

@ -6,6 +6,7 @@
#include <algorithm> #include <algorithm>
#include <cmath> #include <cmath>
#include <Vector3.h> #include <Vector3.h>
#include <cstdlib>
namespace RenderData namespace RenderData
{ {
@ -32,28 +33,35 @@ namespace RenderData
return BoundingBox2D(min, max); return BoundingBox2D(min, max);
} }
bool ContainsPixel(const Math::Vector2Int point) const /// <summary>
/// 给定屏幕像素坐标,输出该点的面积坐标
/// </summary>
/// <param name="pos">要计算的屏幕像素点</param>
/// <param name="w0">面积坐标的 x 分量(引用)</param>
/// <param name="w1">面积坐标的 y 分量(引用)</param>
/// <param name="w2">面积坐标的 z 分量(引用)</param>
/// <returns>是否计算成功</returns>
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 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 square2D = (y1 - y2) * (x0 - x2) + (x2 - x1) * (y0 - y2);
if (std::abs(square2D) < 1e-6f)
{ {
Scene::Vertex v3(Math::Vector3(point.x, point.y, 0)); return false;
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 c0 = cross(v0, v1, point); w0 = ((y1 - y2) * (p.x - x2) + (x2 - x1) * (p.y - y2)) / square2D;
const float c1 = cross(v1, v2, point); w1 = ((y2 - y0) * (p.x - x2) + (x0 - x2) * (p.y - y2)) / square2D;
const float c2 = cross(v2, v0, point); w2 = 1.0f - w0 - w1;
return true;
const bool hasNeg = (c0 < 0) || (c1 < 0) || (c2 < 0);
const bool hasPos = (c0 > 0) || (c1 > 0) || (c2 > 0);
return !(hasNeg && hasPos);
} }
}; };
} }

View File

@ -23,6 +23,7 @@
#include <cstdlib> #include <cstdlib>
#include <algorithm> #include <algorithm>
#include "Vertex.h" #include "Vertex.h"
#include "DepthBuffer.h"
const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO; const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO;
const int32_t width = 800; const int32_t width = 800;
@ -135,12 +136,6 @@ struct CubeTriangle
std::array<int, 3> vertices; std::array<int, 3> vertices;
}; };
struct TriangleDrawCommand
{
RenderData::Triangle triangle;
float averageViewSpaceZ = 0.0f;
};
static ProjectedVertex ProjectToScreen( static ProjectedVertex ProjectToScreen(
const Math::Vector3& vertex, const Math::Vector3& vertex,
const Math::Matrix4x4& mvp, const Math::Matrix4x4& mvp,
@ -208,9 +203,10 @@ int main(int argc, char* argv[])
if (!EnsureTexture()) return -1; if (!EnsureTexture()) return -1;
Core::FrameBuffer frameBuffer(width, height); Core::FrameBuffer* frameBuffer = new Core::FrameBuffer(width, height);
Rasterizer::Rasterizer rasterizer(frameBuffer); Core::DepthBuffer* depthBuffer = new Core::DepthBuffer(width, height);
Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer); Rasterizer::Rasterizer rasterizer(frameBuffer, depthBuffer);
Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer, depthBuffer);
Scene::Camera camera; Scene::Camera camera;
camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f); 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<float>(SDL_GetTicks()) * 0.001f; const float timeSeconds = static_cast<float>(SDL_GetTicks()) * 0.001f;
const Math::Matrix4x4 model = const Math::Matrix4x4 model =
@ -291,7 +288,7 @@ int main(int argc, char* argv[])
visibleFaces[faceIndex] = IsFaceVisible(cubeFaces[faceIndex], viewSpaceVertices); visibleFaces[faceIndex] = IsFaceVisible(cubeFaces[faceIndex], viewSpaceVertices);
} }
std::array<TriangleDrawCommand, 12> drawCommands; std::array<RenderData::Triangle, 12> drawTriangles;
size_t drawCommandCount = 0; size_t drawCommandCount = 0;
for (const CubeTriangle& cubeTriangle : cubeTriangles) 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& viewV1 = viewSpaceVertices[cubeTriangle.vertices[1]];
const Math::Vector3& viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]]; const Math::Vector3& viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]];
drawCommands[drawCommandCount++] = TriangleDrawCommand{ drawTriangles[drawCommandCount++] =
RenderData::Triangle( RenderData::Triangle(
Scene::Vertex(v0.screen), Scene::Vertex(v0.screen),
Scene::Vertex(v1.screen), Scene::Vertex(v1.screen),
Scene::Vertex(v2.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) 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) 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_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr); SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer); SDL_RenderPresent(renderer);