From 16d961e9f3ef8fb9e2058ec2dac591c4721ec1d2 Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sun, 7 Jun 2026 19:25:00 +0800 Subject: [PATCH] =?UTF-8?q?DrawLine=20=E6=B6=88=E9=99=A4=20set=5Fpixel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/CONVENTIONS.md | 3 +- docs/DEVELOPMENT_GUIDELINES.md | 1 + src/Core/Rasterizer/Rasterizer.cpp | 301 +++++++++++++++++++---------- src/Core/Rasterizer/Rasterizer.h | 5 +- 4 files changed, 205 insertions(+), 105 deletions(-) diff --git a/docs/CONVENTIONS.md b/docs/CONVENTIONS.md index 819bcde..731fb76 100644 --- a/docs/CONVENTIONS.md +++ b/docs/CONVENTIONS.md @@ -100,7 +100,8 @@ 这意味着: - 当前 demo 还没有实现完整的视锥裁剪 -- 如果一条线段只有一部分还在屏幕内,但端点已经越出 NDC,整条线仍可能被直接丢弃 +- **2D 屏幕空间线段裁剪已实现**:`Rasterizer::DrawLine` 使用 Cohen-Sutherland 算法对屏幕边界做裁剪,部分在屏幕内的线段可以正确绘制,不再整条丢弃 +- 3D 视锥裁剪(齐次裁剪空间)仍未实现 ## 8. 屏幕与像素坐标 diff --git a/docs/DEVELOPMENT_GUIDELINES.md b/docs/DEVELOPMENT_GUIDELINES.md index 38d7856..5799a66 100644 --- a/docs/DEVELOPMENT_GUIDELINES.md +++ b/docs/DEVELOPMENT_GUIDELINES.md @@ -91,6 +91,7 @@ - 像素写入路径应尽量减少分支和函数调用层级。 - 像素级写入的 Release 快路径不要使用带额外边界检查的容器访问(例如 `std::vector::at()`);边界检查应在外层完成。 - 裁剪、剔除、包围盒收缩要尽早执行,避免把不可见数据送入像素级循环。 +- `Rasterizer::DrawLine` 已采用此策略:入口做快速全屏可见性检查,屏幕内直接走 `set_pixel_unsafe` 快路径;屏幕外走 Cohen-Sutherland 裁剪后再走快路径。 - 三角形属性插值、深度测试、纹理采样等未来功能必须先定义定点/整数方案,再接入热路径。 - PC 调试版可以保留更易读的检查与可视化代码,但 ARM release 路径必须能关闭这些额外成本。 diff --git a/src/Core/Rasterizer/Rasterizer.cpp b/src/Core/Rasterizer/Rasterizer.cpp index 21317c1..c443064 100644 --- a/src/Core/Rasterizer/Rasterizer.cpp +++ b/src/Core/Rasterizer/Rasterizer.cpp @@ -1,102 +1,199 @@ -#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 + +namespace Rasterizer +{ + using namespace Math; + using namespace RenderData; + + namespace + { + // Cohen-Sutherland 线段裁剪常量 + const int32_t CS_INSIDE = 0; + const int32_t CS_LEFT = 1; + const int32_t CS_RIGHT = 2; + const int32_t CS_BOTTOM = 4; + const int32_t CS_TOP = 8; + + inline int32_t cs_compute_outcode(int32_t x, int32_t y, int32_t w, int32_t h) + { + int32_t code = CS_INSIDE; + if (x < 0) code |= CS_LEFT; + else if (x >= w) code |= CS_RIGHT; + if (y < 0) code |= CS_TOP; + else if (y >= h) code |= CS_BOTTOM; + return code; + } + + bool cs_clip_line(int32_t& x0, int32_t& y0, int32_t& x1, int32_t& y1, + int32_t w, int32_t h) + { + int32_t outcode0 = cs_compute_outcode(x0, y0, w, h); + int32_t outcode1 = cs_compute_outcode(x1, y1, w, h); + + while (true) + { + if (!(outcode0 | outcode1)) + { + return true; + } + if (outcode0 & outcode1) + { + return false; + } + + const int32_t outcode_out = outcode0 ? outcode0 : outcode1; + int32_t x, y; + + if (outcode_out & CS_TOP) + { + x = x0 + (x1 - x0) * (0 - y0) / (y1 - y0); + y = 0; + } + else if (outcode_out & CS_BOTTOM) + { + x = x0 + (x1 - x0) * (h - 1 - y0) / (y1 - y0); + y = h - 1; + } + else if (outcode_out & CS_RIGHT) + { + y = y0 + (y1 - y0) * (w - 1 - x0) / (x1 - x0); + x = w - 1; + } + else + { + y = y0 + (y1 - y0) * (0 - x0) / (x1 - x0); + x = 0; + } + + if (outcode_out == outcode0) + { + x0 = x; y0 = y; + outcode0 = cs_compute_outcode(x0, y0, w, h); + } + else + { + x1 = x; y1 = y; + outcode1 = cs_compute_outcode(x1, y1, w, h); + } + } + } + } + + void Rasterizer::DrawLineHorizontal(Vector2Int v0, Vector2Int v1, uint32_t rgba) + { + if (v0.x > v1.x) + { + Vector2Int tmp = v0; + v0 = v1; + v1 = tmp; + } + + const int32_t dx = v1.x - v0.x; + int32_t dy = v1.y - v0.y; + if (dx == 0) return; + + const int32_t dir = dy < 0 ? -1 : 1; + dy *= dir; + + const int32_t fb_w = frameBuffer->get_width(); + int32_t y = v0.y; + int32_t p = 2 * dy - dx; + + for (int32_t x = v0.x; x <= v1.x; ++x) + { + frameBuffer->set_pixel_unsafe(x, y, rgba); + if (p >= 0) + { + y += dir; + p -= 2 * dx; + } + p += 2 * dy; + } + } + + void Rasterizer::DrawLineVertical(Vector2Int v0, Vector2Int v1, uint32_t rgba) + { + if (v0.y > v1.y) + { + Vector2Int tmp = v0; + v0 = v1; + v1 = tmp; + } + + int32_t dx = v1.x - v0.x; + const int32_t dy = v1.y - v0.y; + if (dy == 0) return; + + const int32_t dir = dx < 0 ? -1 : 1; + dx *= dir; + + int32_t x = v0.x; + int32_t p = 2 * dx - dy; + + for (int32_t y = v0.y; y <= v1.y; ++y) + { + frameBuffer->set_pixel_unsafe(x, y, rgba); + if (p >= 0) + { + x += dir; + p -= 2 * dy; + } + p += 2 * dx; + } + } + + void Rasterizer::DrawLineClipped(Vector2Int v0, Vector2Int v1, uint32_t rgba) + { + if (!cs_clip_line(v0.x, v0.y, v1.x, v1.y, + frameBuffer->get_width(), frameBuffer->get_height())) + { + return; + } + + const int32_t dx = v1.x - v0.x; + const int32_t dy = v1.y - v0.y; + const int32_t adx = dx < 0 ? -dx : dx; + const int32_t ady = dy < 0 ? -dy : dy; + + if (adx > ady) + { + DrawLineHorizontal(v0, v1, rgba); + } + else + { + DrawLineVertical(v0, v1, rgba); + } + } + + void Rasterizer::DrawLine(const Vector2Int v0, const Vector2Int v1, const Color color) + { + const int32_t w = frameBuffer->get_width(); + const int32_t h = frameBuffer->get_height(); + + // 快速全屏可见性检查:两点都在屏幕内则跳过裁剪 + if (v0.x >= 0 && v0.x < w && v0.y >= 0 && v0.y < h && + v1.x >= 0 && v1.x < w && v1.y >= 0 && v1.y < h) + { + const uint32_t rgba = color.to_rgba(); + const int32_t dx = v1.x - v0.x; + const int32_t dy = v1.y - v0.y; + const int32_t adx = dx < 0 ? -dx : dx; + const int32_t ady = dy < 0 ? -dy : dy; + + if (adx > ady) + { + DrawLineHorizontal(v0, v1, rgba); + } + else + { + DrawLineVertical(v0, v1, rgba); + } + return; + } + + DrawLineClipped(v0, v1, color.to_rgba()); + } +} diff --git a/src/Core/Rasterizer/Rasterizer.h b/src/Core/Rasterizer/Rasterizer.h index 0a0d9ab..7de3569 100644 --- a/src/Core/Rasterizer/Rasterizer.h +++ b/src/Core/Rasterizer/Rasterizer.h @@ -12,8 +12,9 @@ namespace Rasterizer 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); + void DrawLineHorizontal(Math::Vector2Int v0, Math::Vector2Int v1, uint32_t rgba); + void DrawLineVertical(Math::Vector2Int v0, Math::Vector2Int v1, uint32_t rgba); + void DrawLineClipped(Math::Vector2Int v0, Math::Vector2Int v1, uint32_t rgba); public: explicit Rasterizer(Core::FrameBuffer* frameBuffer) :frameBuffer(frameBuffer), depthBuffer(nullptr) {}