TriangleRasterizer 定点化

This commit is contained in:
SepComet 2026-06-07 19:54:15 +08:00
parent 16d961e9f3
commit a8630c720e
2 changed files with 97 additions and 48 deletions

View File

@ -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` 提交路径

View File

@ -5,19 +5,7 @@
#include <Vector2.h>
#include <algorithm>
#include <cstdint>
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<uint16_t>(z * 65535.0f + 0.5f);
}
}
#include <cstdlib>
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++)
{
for (int y = minY; y <= maxY; y++)
{
Vector2 samplePoint(x + 0.5f, y + 0.5f);
float w0 = 0, w1 = 0, w2 = 0;
if (!triangle.get_barycentric(samplePoint, w0, w1, w2))
{
continue;
}
if (minX > maxX || minY > maxY) return;
if ((w0 < 0) || (w1 < 0) || (w2 < 0))
// 顶点屏幕坐标(整数)
const int32_t x0 = static_cast<int32_t>(triangle.v0.position.x);
const int32_t y0 = static_cast<int32_t>(triangle.v0.position.y);
const int32_t x1 = static_cast<int32_t>(triangle.v1.position.x);
const int32_t y1 = static_cast<int32_t>(triangle.v1.position.y);
const int32_t x2 = static_cast<int32_t>(triangle.v2.position.x);
const int32_t y2 = static_cast<int32_t>(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) / crossfloat 语义)
// depth_fp = depth * 65535定点精度 ≈ 1/65535
// 对 CWcross < 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<int32_t>(triangle.v0.position.z * 65535.0f + 0.5f);
const int32_t z1_s = static_cast<int32_t>(triangle.v1.position.z * 65535.0f + 0.5f);
const int32_t z2_s = static_cast<int32_t>(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<int32_t>(
static_cast<int64_t>(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<int32_t>(
static_cast<int64_t>(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<int64_t>(e1_row) * z0_s
+ static_cast<int64_t>(e2_row) * z1_s
+ static_cast<int64_t>(e0_row) * z2_s))
* 65536 / neg_cross;
int32_t depth_fp_row = static_cast<int32_t>(depth_fp_init);
const uint32_t rgba = color.to_rgba();
for (int32_t y = minY; y <= maxY; ++y)
{
continue;
}
const float depth =
w0 * triangle.v0.position.z +
w1 * triangle.v1.position.z +
w2 * triangle.v2.position.z;
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)
{
// CW屏幕空间 Y-down内部点三个边缘函数均 ≤ 0
if (e0 <= 0 && e1 <= 0 && e2 <= 0)
{
if (depthBuffer)
{
const uint16_t depth_u16 = temp_float_depth_to_uint16(depth);
// 深度越小离相机越近
if (depthBuffer->get_depth(x, y) < depth_u16)
{
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<uint16_t>(shifted);
depthBuffer->set_depth(x, y, depth_u16);
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(x, y, color.to_rgba());
}
}
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;
}
}
}