From e1dd23131c7021de12c5a5244a38b4f773a6358f Mon Sep 17 00:00:00 2001
From: SepComet <202308010230@stu.csust.edu.cn>
Date: Sun, 15 Mar 2026 17:01:48 +0800
Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=20SDL=20=E4=B8=BB=E5=BE=AA?=
=?UTF-8?q?=E7=8E=AF=E5=B9=B6=E6=94=B6=E7=B4=A7=E4=B8=89=E8=A7=92=E5=BD=A2?=
=?UTF-8?q?=E5=85=89=E6=A0=85=E5=8C=96=E8=BE=B9=E7=95=8C=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 补齐 SDL 纹理的创建与释放流程,避免纹理资源悬空
- 为主循环加入事件处理,支持通过关闭窗口或按空格正常退出程序
- 为 FrameBuffer 增加宽高访问接口,供光栅化阶段做屏幕范围裁剪
- 去掉越界写像素时的逐次错误输出,将其改为静默忽略
- 将 BoundingBox2D 改为整数像素边界,统一屏幕空间扫描语义
- 在 Triangle2D 中使用 floor/ceil 生成包围盒,避免浮点顶点直接截断带来的扫描误差
- 在 TriangleRasterizer 中将包围盒裁剪到 framebuffer 范围内,减少无效遍历
- 将工程中的 SDL include/lib 路径从绝对路径改为基于 $(ProjectDir) 的相对路径
- 将仓库根目录的 README.md 纳入 Visual Studio 工程项,便于在 IDE 中查看
---
.../CPU-Software-Renderer.vcxproj | 17 +++----
CPU-Software-Renderer/Core/FrameBuffer.cpp | 1 -
CPU-Software-Renderer/Core/FrameBuffer.h | 4 ++
.../Rasterizer/TriangleRasterizer.cpp | 18 +++++---
.../RenderData/BoundingBox.h | 6 +--
CPU-Software-Renderer/RenderData/Triangle.h | 27 ++++++++----
CPU-Software-Renderer/main.cpp | 44 +++++++++++++++++--
7 files changed, 87 insertions(+), 30 deletions(-)
diff --git a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj
index 33b6481..5c6484c 100644
--- a/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj
+++ b/CPU-Software-Renderer/CPU-Software-Renderer.vcxproj
@@ -77,13 +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)
+ $(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)
Console
true
SDL2.lib;SDL2main.lib;%(AdditionalDependencies)
- D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)
+ $(ProjectDir)libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)
@@ -95,13 +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)
+ $(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)
Console
true
SDL2.lib;SDL2main.lib;%(AdditionalDependencies)
- D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)
+ $(ProjectDir)libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)
@@ -111,12 +111,12 @@
_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)
+ $(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)
Console
true
- D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)
+ $(ProjectDir)libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)
SDL2.lib;SDL2main.lib;%(AdditionalDependencies)
@@ -129,13 +129,13 @@
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)
+ $(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)
Console
true
SDL2.lib;SDL2main.lib;%(AdditionalDependencies)
- D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)
+ $(ProjectDir)libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)
@@ -149,6 +149,7 @@
+
diff --git a/CPU-Software-Renderer/Core/FrameBuffer.cpp b/CPU-Software-Renderer/Core/FrameBuffer.cpp
index 30072cf..d565b23 100644
--- a/CPU-Software-Renderer/Core/FrameBuffer.cpp
+++ b/CPU-Software-Renderer/Core/FrameBuffer.cpp
@@ -16,7 +16,6 @@ namespace Core
{
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;
diff --git a/CPU-Software-Renderer/Core/FrameBuffer.h b/CPU-Software-Renderer/Core/FrameBuffer.h
index 41e42b1..5ef6ebb 100644
--- a/CPU-Software-Renderer/Core/FrameBuffer.h
+++ b/CPU-Software-Renderer/Core/FrameBuffer.h
@@ -14,6 +14,10 @@ namespace Core
std::vector buffer;
public:
+ int32_t get_width() const { return width; }
+
+ int32_t get_height() const { return height; }
+
size_t total_pixels() const { return buffer.size(); }
void* get_buffer() const { return (void*)buffer.data(); }
diff --git a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp
index 07f2f07..294704f 100644
--- a/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp
+++ b/CPU-Software-Renderer/Rasterizer/TriangleRasterizer.cpp
@@ -3,21 +3,27 @@
#include
#include "BoundingBox.h"
#include
-using namespace RenderData;
+#include
+#include
namespace Rasterizer
{
- void TriangleRasterizer::DrawTriangle2D(const Triangle2D triangle, const Color color)
+ void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle2D triangle, const RenderData::Color color)
{
auto boundingBox = triangle.get_boundingBox();
- for (int x = boundingBox.min.x; x <= boundingBox.max.x; x++)
+ 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 = boundingBox.min.y; y <= boundingBox.max.y; y++)
+ for (int y = minY; y <= maxY; y++)
{
- if (triangle.ContainsPixel(Vector2(x, y)))
+ if (triangle.ContainsPixel(Math::Vector2(x, y)))
{
- frameBuffer.set_pixel(Vector2Int(x, y), color.to_rgba());
+ frameBuffer.set_pixel(Math::Vector2Int(x, y), color.to_rgba());
}
}
}
diff --git a/CPU-Software-Renderer/RenderData/BoundingBox.h b/CPU-Software-Renderer/RenderData/BoundingBox.h
index deb4bb5..86bc0c2 100644
--- a/CPU-Software-Renderer/RenderData/BoundingBox.h
+++ b/CPU-Software-Renderer/RenderData/BoundingBox.h
@@ -5,10 +5,10 @@ namespace RenderData
{
struct BoundingBox2D
{
- Math::Vector2 min;
- Math::Vector2 max;
+ Math::Vector2Int min;
+ Math::Vector2Int max;
BoundingBox2D() : min(), max() {}
- BoundingBox2D(const Math::Vector2 min, const Math::Vector2 max) : min(min), max(max) {}
+ BoundingBox2D(const Math::Vector2Int min, const Math::Vector2Int max) : min(min), max(max) {}
};
}
\ No newline at end of file
diff --git a/CPU-Software-Renderer/RenderData/Triangle.h b/CPU-Software-Renderer/RenderData/Triangle.h
index a470230..dcf359f 100644
--- a/CPU-Software-Renderer/RenderData/Triangle.h
+++ b/CPU-Software-Renderer/RenderData/Triangle.h
@@ -2,29 +2,38 @@
#include "Vector2.h"
#include "BoundingBox.h"
#include
+#include
+#include
namespace RenderData
{
- using namespace Math;
struct Triangle2D
{
- Vector2 v0;
- Vector2 v1;
- Vector2 v2;
+ Math::Vector2 v0;
+ Math::Vector2 v1;
+ Math::Vector2 v2;
Triangle2D() : v0(), v1(), v2() {}
- Triangle2D(const Vector2 a, const Vector2 b, const Vector2 c) : v0(a), v1(b), v2(c) {}
+ Triangle2D(const Math::Vector2 a, const Math::Vector2 b, const Math::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 }));
+ using namespace Math;
+
+ int32_t minX = static_cast(std::floor(std::min({ v0.x, v1.x, v2.x })));
+ int32_t maxX = static_cast(std::ceil(std::max({ v0.x, v1.x, v2.x })));
+ int32_t minY = static_cast(std::floor(std::min({ v0.y, v1.y, v2.y })));
+ int32_t maxY = static_cast(std::ceil(std::max({ v0.y, v1.y, v2.y })));
+
+ Vector2Int min(minX, minY);
+ Vector2Int max(maxX, maxY);
return BoundingBox2D(min, max);
}
- bool ContainsPixel(const Vector2 point) const
+ bool ContainsPixel(const Math::Vector2 point) const
{
+ using namespace Math;
+
auto cross = [](const Vector2& p1, const Vector2& p2, const Vector2& p3) -> float
{
const float x1 = p2.x - p1.x;
diff --git a/CPU-Software-Renderer/main.cpp b/CPU-Software-Renderer/main.cpp
index a20b2d2..92635fb 100644
--- a/CPU-Software-Renderer/main.cpp
+++ b/CPU-Software-Renderer/main.cpp
@@ -8,15 +8,24 @@
#include "Color.h"
#include "FrameBuffer.h"
#include "Rasterizer.h"
+#include "SDL_events.h"
+#include "SDL_keycode.h"
const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO;
const int32_t width = 800;
const int32_t height = 600;
SDL_Window* window = nullptr;
SDL_Renderer* renderer = nullptr;
+SDL_Texture* texture = nullptr;
static void ClearSDLResources()
{
+ if (texture != nullptr)
+ {
+ SDL_DestroyTexture(texture);
+ texture = nullptr;
+ }
+
if (renderer != nullptr)
{
SDL_DestroyRenderer(renderer);
@@ -82,6 +91,21 @@ static bool EnsureRenderer()
return true;
}
+static bool EnsureTexture()
+{
+ if (texture != nullptr) return true;
+
+ texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height);
+
+ if (texture == nullptr)
+ {
+ std::cerr << "Texture could not be created! SDL_Error: " << SDL_GetError() << std::endl;
+ ClearSDLResources();
+ return false;
+ }
+ return true;
+}
+
int main(int argc, char* argv[])
{
@@ -91,7 +115,7 @@ int main(int argc, char* argv[])
if (!EnsureRenderer()) return -1;
- SDL_Texture* texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_STREAMING, width, height);
+ if (!EnsureTexture()) return -1;
Core::FrameBuffer frameBuffer(width, height);
Rasterizer::Rasterizer rasterizer(frameBuffer);
@@ -106,10 +130,24 @@ int main(int argc, char* argv[])
rasterizer.DrawLine(v1, v2, color1);
rasterizer.DrawLine(v2, v0, color2);
- while (true)
+ bool isRuning = true;
+ while (isRuning)
{
- SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t));
+ SDL_Event event;
+ while (SDL_PollEvent(&event))
+ {
+ if (event.type == SDL_QUIT)
+ {
+ isRuning = false;
+ }
+ if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_SPACE)
+ {
+ isRuning = false;
+ }
+ }
+
+ SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t));
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, nullptr, nullptr);
SDL_RenderPresent(renderer);