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 <cstdint>
#include <algorithm>
#include <cmath>
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<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)
{

View File

@ -1,8 +1,8 @@
#pragma once
#include <cstdint>
#include <vector>
#include "Color.h"
#include "Vector2.h"
#include <cmath>
namespace Core
{
@ -11,7 +11,7 @@ namespace Core
private:
int32_t width;
int32_t height;
std::vector<uint8_t> buffer;
std::vector<float> 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<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)
{
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);
};
};

View File

@ -2,6 +2,7 @@
#include "Matrix4x4.h"
#include "Vector3.h"
#include <cmath>
#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);
}
};
}

View File

@ -2,9 +2,13 @@
#include <cstdint>
#include <utility>
#include <cmath>
#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) {}
/// <summary>
/// 交换当前 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)));
}
Vector3 cross(const Vector2& other) const
{
return Vector3(0, 0, this->x * other.y - this->y * other.x);
}
};
}
}

View File

@ -1,102 +1,102 @@
#include "Rasterizer.h"
#include <Vector2.h>
#include "Color.h"
#include <cmath>
#include <cstdint>
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;
}
}
/// <summary>
/// 在给定的起始点和结束点之间绘制一条线段,并使用指定的颜色填充
/// </summary>
/// <param name="v0">直线顶点 1</param>
/// <param name="v1">直线顶点 2</param>
/// <param name="color">绘制的颜色</param>
/// <remarks>
/// bresenham 画线算法
/// </remarks>
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 <Vector2.h>
#include "Color.h"
#include <cmath>
#include <cstdint>
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;
}
}
/// <summary>
/// 在给定的起始点和结束点之间绘制一条线段,并使用指定的颜色填充
/// </summary>
/// <param name="v0">直线顶点 1</param>
/// <param name="v1">直线顶点 2</param>
/// <param name="color">绘制的颜色</param>
/// <remarks>
/// bresenham 画线算法
/// </remarks>
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);
}
}
}

View File

@ -1,21 +1,24 @@
#pragma once
#include "Color.h"
#include "FrameBuffer.h"
#include <Vector2.h>
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 <Vector2.h>
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);
};
}

View File

@ -1,31 +1,61 @@
#include "TriangleRasterizer.h"
#include <Triangle.h>
#include <Color.h>
#include "BoundingBox.h"
#include <Vector2.h>
#include <algorithm>
#include <cstdint>
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 <Triangle.h>
#include <Color.h>
#include "BoundingBox.h"
#include <Vector2.h>
#include <algorithm>
#include <cstdint>
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());
}
}
}
}
}
}

View File

@ -2,17 +2,20 @@
#include "Triangle.h"
#include "Color.h"
#include "FrameBuffer.h"
#include <DepthBuffer.h>
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);
};

View File

@ -6,6 +6,7 @@
#include <algorithm>
#include <cmath>
#include <Vector3.h>
#include <cstdlib>
namespace RenderData
{
@ -32,28 +33,35 @@ namespace RenderData
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
{
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;
}
};
}

View File

@ -23,6 +23,7 @@
#include <cstdlib>
#include <algorithm>
#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<int, 3> 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<float>(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<TriangleDrawCommand, 12> drawCommands;
std::array<RenderData::Triangle, 12> 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);