完善 SDL 主循环并收紧三角形光栅化边界处理

- 补齐 SDL 纹理的创建与释放流程,避免纹理资源悬空
  - 为主循环加入事件处理,支持通过关闭窗口或按空格正常退出程序
  - 为 FrameBuffer 增加宽高访问接口,供光栅化阶段做屏幕范围裁剪
  - 去掉越界写像素时的逐次错误输出,将其改为静默忽略
  - 将 BoundingBox2D 改为整数像素边界,统一屏幕空间扫描语义
  - 在 Triangle2D 中使用 floor/ceil 生成包围盒,避免浮点顶点直接截断带来的扫描误差
  - 在 TriangleRasterizer 中将包围盒裁剪到 framebuffer 范围内,减少无效遍历
  - 将工程中的 SDL include/lib 路径从绝对路径改为基于 $(ProjectDir) 的相对路径
  - 将仓库根目录的 README.md 纳入 Visual Studio 工程项,便于在 IDE 中查看
This commit is contained in:
SepComet 2026-03-15 17:01:48 +08:00
parent 7bd5d7c13d
commit e1dd23131c
7 changed files with 87 additions and 30 deletions

View File

@ -77,13 +77,13 @@
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<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)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -95,13 +95,13 @@
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<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)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)libs\SDL2\lib\x86;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
@ -111,12 +111,12 @@
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<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)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalLibraryDirectories>D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
@ -129,13 +129,13 @@
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<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)</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>$(ProjectDir)Asset;$(ProjectDir)Core;$(ProjectDir)Math;$(ProjectDir)Rasterizer;$(ProjectDir)RenderData;$(ProjectDir)Scene;$(ProjectDir)Shading;$(ProjectDir)libs\SDL2\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>SDL2.lib;SDL2main.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>D:\source\CPU-Software-Renderer\CPU-Software-Renderer\libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<AdditionalLibraryDirectories>$(ProjectDir)libs\SDL2\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
@ -149,6 +149,7 @@
<ClCompile Include="Shading\BlinnPhongShader.cpp" />
</ItemGroup>
<ItemGroup>
<None Include="..\README.md" />
<None Include="TODO.md" />
</ItemGroup>
<ItemGroup>

View File

@ -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<size_t>(y) * width + x;

View File

@ -14,6 +14,10 @@ namespace Core
std::vector<uint32_t> 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(); }

View File

@ -3,21 +3,27 @@
#include <Color.h>
#include "BoundingBox.h"
#include <Vector2.h>
using namespace RenderData;
#include <algorithm>
#include <cstdint>
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());
}
}
}

View File

@ -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) {}
};
}

View File

@ -2,29 +2,38 @@
#include "Vector2.h"
#include "BoundingBox.h"
#include <algorithm>
#include <cmath>
#include <cstdint>
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<int32_t>(std::floor(std::min({ v0.x, v1.x, v2.x })));
int32_t maxX = static_cast<int32_t>(std::ceil(std::max({ v0.x, v1.x, v2.x })));
int32_t minY = static_cast<int32_t>(std::floor(std::min({ v0.y, v1.y, v2.y })));
int32_t maxY = static_cast<int32_t>(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;

View File

@ -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);