From a8630c720e481c5db3dbc2e1f3e42568c2d22abf Mon Sep 17 00:00:00 2001 From: SepComet <202308010230@stu.csust.edu.cn> Date: Sun, 7 Jun 2026 19:54:15 +0800 Subject: [PATCH] =?UTF-8?q?TriangleRasterizer=20=E5=AE=9A=E7=82=B9?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/DEVELOPMENT_GUIDELINES.md | 1 + src/Core/Rasterizer/TriangleRasterizer.cpp | 144 ++++++++++++++------- 2 files changed, 97 insertions(+), 48 deletions(-) diff --git a/docs/DEVELOPMENT_GUIDELINES.md b/docs/DEVELOPMENT_GUIDELINES.md index 5799a66..1c10d70 100644 --- a/docs/DEVELOPMENT_GUIDELINES.md +++ b/docs/DEVELOPMENT_GUIDELINES.md @@ -93,6 +93,7 @@ - 裁剪、剔除、包围盒收缩要尽早执行,避免把不可见数据送入像素级循环。 - `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/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; } } }