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..1c10d70 100644 --- a/docs/DEVELOPMENT_GUIDELINES.md +++ b/docs/DEVELOPMENT_GUIDELINES.md @@ -91,7 +91,9 @@ - 像素写入路径应尽量减少分支和函数调用层级。 - 像素级写入的 Release 快路径不要使用带额外边界检查的容器访问(例如 `std::vector::at()`);边界检查应在外层完成。 - 裁剪、剔除、包围盒收缩要尽早执行,避免把不可见数据送入像素级循环。 +- `Rasterizer::DrawLine` 已采用此策略:入口做快速全屏可见性检查,屏幕内直接走 `set_pixel_unsafe` 快路径;屏幕外走 Cohen-Sutherland 裁剪后再走快路径。 - 三角形属性插值、深度测试、纹理采样等未来功能必须先定义定点/整数方案,再接入热路径。 +- `TriangleRasterizer::DrawTriangle2D` 已采用增量式整数边缘函数(替代每像素 float 重心+除法)和定点深度插值(`depth_fp += depth_fp_per_pixel`,提取时 `>> 16`),内层循环无 float 运算。 - PC 调试版可以保留更易读的检查与可视化代码,但 ARM release 路径必须能关闭这些额外成本。 ### 5.1 `/dev/fb0` 提交路径 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) {} diff --git a/src/Core/Rasterizer/TriangleRasterizer.cpp b/src/Core/Rasterizer/TriangleRasterizer.cpp index a478790..fb1a52a 100644 --- a/src/Core/Rasterizer/TriangleRasterizer.cpp +++ b/src/Core/Rasterizer/TriangleRasterizer.cpp @@ -5,19 +5,7 @@ #include #include #include - -namespace -{ - // 临时边界:将 float 深度映射到 uint16_t 定点深度。 - // 约定:0.0 ~ 1.0 映射到 0 ~ 65535,越小越近。 - // TODO: 等 TriangleRasterizer 重构为定点后删除此转换。 - inline uint16_t temp_float_depth_to_uint16(float z) - { - if (z <= 0.0f) return 0u; - if (z >= 1.0f) return 0xFFFFu; - return static_cast(z * 65535.0f + 0.5f); - } -} +#include namespace Rasterizer { @@ -27,49 +15,109 @@ namespace Rasterizer 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); + const int32_t minX = std::max(0, boundingBox.min.x); + const int32_t maxX = std::min(frameBuffer->get_width() - 1, boundingBox.max.x); + const int32_t minY = std::max(0, boundingBox.min.y); + const int32_t maxY = std::min(frameBuffer->get_height() - 1, boundingBox.max.y); - for (int x = minX; x <= maxX; x++) + if (minX > maxX || minY > maxY) return; + + // 顶点屏幕坐标(整数) + const int32_t x0 = static_cast(triangle.v0.position.x); + const int32_t y0 = static_cast(triangle.v0.position.y); + const int32_t x1 = static_cast(triangle.v1.position.x); + const int32_t y1 = static_cast(triangle.v1.position.y); + const int32_t x2 = static_cast(triangle.v2.position.x); + const int32_t y2 = static_cast(triangle.v2.position.y); + + // 有符号面积 * 2(整数),CW 在屏幕空间(Y-down)下为负 + const int32_t cross = (x1 - x0) * (y2 - y0) - (x2 - x0) * (y1 - y0); + if (cross == 0) return; + + // 边缘函数增量 + const int32_t e0_dx = y1 - y0; + const int32_t e0_dy = x0 - x1; + const int32_t e1_dx = y2 - y1; + const int32_t e1_dy = x1 - x2; + const int32_t e2_dx = y0 - y2; + const int32_t e2_dy = x2 - x0; + + // 定点深度插值 + // depth(x,y) = (e1*z0 + e2*z1 + e0*z2) / cross(float 语义) + // depth_fp = depth * 65535(定点,精度 ≈ 1/65535) + // 对 CW(cross < 0)内部 e ≤ 0,取反得正值: + // depth_fp = (−e1*z0_s − e2*z1_s − e0*z2_s) / (−cross) + // 右移 16 位提取 depth_fp >> 16 = depth * 65535 + // 需要 depth_fp = depth * 65535 * 65536 以支持 >> 16 提取 + const int32_t z0_s = static_cast(triangle.v0.position.z * 65535.0f + 0.5f); + const int32_t z1_s = static_cast(triangle.v1.position.z * 65535.0f + 0.5f); + const int32_t z2_s = static_cast(triangle.v2.position.z * 65535.0f + 0.5f); + const int32_t neg_cross = -cross; // > 0 for CW + + // per-pixel depth_fp 增量(setup 阶段 int64 除法,内层 int32 加法) + const int32_t depth_step_num = -(z0_s * e0_dx + z1_s * e2_dx + z2_s * e1_dx); + const int32_t depth_fp_per_pixel = static_cast( + static_cast(depth_step_num) * 65536 / neg_cross); + const int32_t depth_step_row_num = -(z0_s * e0_dy + z1_s * e2_dy + z2_s * e1_dy); + const int32_t depth_fp_per_row = static_cast( + static_cast(depth_step_row_num) * 65536 / neg_cross); + + // 边缘函数在起始像素 (minX, minY) + int32_t e0_row = (x1 - x0) * (minY - y0) - (y1 - y0) * (minX - x0); + int32_t e1_row = (x2 - x1) * (minY - y1) - (y2 - y1) * (minX - x1); + int32_t e2_row = (x0 - x2) * (minY - y2) - (y0 - y2) * (minX - x2); + + // depth_fp 在起始像素的值(int64 除法,setup 阶段) + int64_t depth_fp_init = (-(static_cast(e1_row) * z0_s + + static_cast(e2_row) * z1_s + + static_cast(e0_row) * z2_s)) + * 65536 / neg_cross; + int32_t depth_fp_row = static_cast(depth_fp_init); + + const uint32_t rgba = color.to_rgba(); + + for (int32_t y = minY; y <= maxY; ++y) { - for (int y = minY; y <= maxY; y++) + int32_t e0 = e0_row; + int32_t e1 = e1_row; + int32_t e2 = e2_row; + int32_t depth_fp = depth_fp_row; + + for (int32_t x = minX; x <= maxX; ++x) { - Vector2 samplePoint(x + 0.5f, y + 0.5f); - float w0 = 0, w1 = 0, w2 = 0; - if (!triangle.get_barycentric(samplePoint, w0, w1, w2)) + // CW(屏幕空间 Y-down):内部点三个边缘函数均 ≤ 0 + if (e0 <= 0 && e1 <= 0 && e2 <= 0) { - 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) - { - const uint16_t depth_u16 = temp_float_depth_to_uint16(depth); - // 深度越小离相机越近 - if (depthBuffer->get_depth(x, y) < depth_u16) + if (depthBuffer) { - continue; - } + // depth_fp >> 16 = depth * 65535 = uint16 深度值 + const int32_t shifted = depth_fp >> 16; + const uint16_t depth_u16 = shifted <= 0 ? 0u : + shifted >= 0xFFFF ? 0xFFFFu : + static_cast(shifted); - depthBuffer->set_depth(x, y, depth_u16); - frameBuffer->set_pixel(x, y, color.to_rgba()); - } - else - { - frameBuffer->set_pixel(x, y, color.to_rgba()); + if (depth_u16 <= depthBuffer->get_depth_unsafe(x, y)) + { + depthBuffer->set_depth_unsafe(x, y, depth_u16); + frameBuffer->set_pixel_unsafe(x, y, rgba); + } + } + else + { + frameBuffer->set_pixel_unsafe(x, y, rgba); + } } + + e0 += e0_dx; + e1 += e1_dx; + e2 += e2_dx; + depth_fp += depth_fp_per_pixel; } + + e0_row += e0_dy; + e1_row += e1_dy; + e2_row += e2_dy; + depth_fp_row += depth_fp_per_row; } } } diff --git a/src/Core/RenderData/Triangle.h b/src/Core/RenderData/Triangle.h index dae9c99..555a1f2 100644 --- a/src/Core/RenderData/Triangle.h +++ b/src/Core/RenderData/Triangle.h @@ -3,13 +3,18 @@ #include "BoundingBox.h" #include "Vertex.h" #include -#include #include #include #include namespace RenderData { + namespace detail + { + inline float min3f(float a, float b, float c) { return a < b ? (a < c ? a : c) : (b < c ? b : c); } + inline float max3f(float a, float b, float c) { return a > b ? (a > c ? a : c) : (b > c ? b : c); } + } + struct Triangle { Scene::Vertex v0; @@ -22,10 +27,10 @@ namespace RenderData { using namespace Math; - int32_t minX = static_cast(std::floor(std::min({ v0.position.x, v1.position.x, v2.position.x }))); - int32_t maxX = static_cast(std::ceil(std::max({ v0.position.x, v1.position.x, v2.position.x }))); - int32_t minY = static_cast(std::floor(std::min({ v0.position.y, v1.position.y, v2.position.y }))); - int32_t maxY = static_cast(std::ceil(std::max({ v0.position.y, v1.position.y, v2.position.y }))); + int32_t minX = static_cast(std::floor(detail::min3f(v0.position.x, v1.position.x, v2.position.x))); + int32_t maxX = static_cast(std::ceil(detail::max3f(v0.position.x, v1.position.x, v2.position.x))); + int32_t minY = static_cast(std::floor(detail::min3f(v0.position.y, v1.position.y, v2.position.y))); + int32_t maxY = static_cast(std::ceil(detail::max3f(v0.position.y, v1.position.y, v2.position.y))); Vector2Int min(minX, minY); Vector2Int max(maxX, maxY);