Merge branch 'master' of https://gitea.sepcomet.xyz/basil/IMX6U-Game
This commit is contained in:
commit
24fe01af46
|
|
@ -100,7 +100,8 @@
|
|||
这意味着:
|
||||
|
||||
- 当前 demo 还没有实现完整的视锥裁剪
|
||||
- 如果一条线段只有一部分还在屏幕内,但端点已经越出 NDC,整条线仍可能被直接丢弃
|
||||
- **2D 屏幕空间线段裁剪已实现**:`Rasterizer::DrawLine` 使用 Cohen-Sutherland 算法对屏幕边界做裁剪,部分在屏幕内的线段可以正确绘制,不再整条丢弃
|
||||
- 3D 视锥裁剪(齐次裁剪空间)仍未实现
|
||||
|
||||
## 8. 屏幕与像素坐标
|
||||
|
||||
|
|
|
|||
|
|
@ -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` 提交路径
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
#include "Rasterizer.h"
|
||||
#include <Vector2.h>
|
||||
#include "Color.h"
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
|
||||
namespace Rasterizer
|
||||
|
|
@ -9,94 +8,192 @@ namespace Rasterizer
|
|||
using namespace Math;
|
||||
using namespace RenderData;
|
||||
|
||||
void Rasterizer::DrawLineHorizontal(const Vector2Int v0, const Vector2Int v1, const Color color)
|
||||
namespace
|
||||
{
|
||||
Vector2Int start = v0, end = v1;
|
||||
if (v0.x > v1.x)
|
||||
// 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)
|
||||
{
|
||||
start = v1;
|
||||
end = v0;
|
||||
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;
|
||||
}
|
||||
|
||||
int32_t dx = end.x - start.x;
|
||||
int32_t dy = end.y - start.y;
|
||||
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);
|
||||
|
||||
int32_t dir = dy < 0 ? -1 : 1;
|
||||
dy *= dir;
|
||||
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;
|
||||
|
||||
int32_t y = start.y;
|
||||
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 = start.x; x <= end.x; x++)
|
||||
|
||||
for (int32_t x = v0.x; x <= v1.x; ++x)
|
||||
{
|
||||
frameBuffer->set_pixel(x, y, color.to_rgba());
|
||||
// 当 p >= 0 时,取上方像素点
|
||||
frameBuffer->set_pixel_unsafe(x, y, rgba);
|
||||
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)
|
||||
void Rasterizer::DrawLineVertical(Vector2Int v0, Vector2Int v1, uint32_t rgba)
|
||||
{
|
||||
Vector2Int start = v0, end = v1;
|
||||
if (v0.y > v1.y)
|
||||
{
|
||||
start = v1;
|
||||
end = v0;
|
||||
Vector2Int tmp = v0;
|
||||
v0 = v1;
|
||||
v1 = tmp;
|
||||
}
|
||||
|
||||
int32_t dx = end.x - start.x;
|
||||
int32_t dy = end.y - start.y;
|
||||
|
||||
int32_t dir = dx < 0 ? -1 : 1;
|
||||
dx *= dir;
|
||||
|
||||
int32_t dx = v1.x - v0.x;
|
||||
const int32_t dy = v1.y - v0.y;
|
||||
if (dy == 0) return;
|
||||
|
||||
int32_t x = start.x;
|
||||
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 = start.y; y <= end.y; y++)
|
||||
|
||||
for (int32_t y = v0.y; y <= v1.y; ++y)
|
||||
{
|
||||
frameBuffer->set_pixel(x, y, color.to_rgba());
|
||||
// 当 p >= 0 时,取上方像素点
|
||||
frameBuffer->set_pixel_unsafe(x, y, rgba);
|
||||
if (p >= 0)
|
||||
{
|
||||
x += dir;
|
||||
p -= 2 * dy;
|
||||
}
|
||||
// 当 p < 0 时,取下方像素点
|
||||
p += 2 * dx;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在给定的起始点和结束点之间绘制一条线段,并使用指定的颜色填充
|
||||
/// </summary>
|
||||
/// <param name="v0">直线顶点 1</param>
|
||||
/// <param name="v1">直线顶点 2</param>
|
||||
/// <param name="color">绘制的颜色</param>
|
||||
/// <remarks>
|
||||
/// bresenham 画线算法
|
||||
/// </remarks>
|
||||
void Rasterizer::DrawLine(const Vector2Int v0, const Vector2Int v1, const Color color)
|
||||
void Rasterizer::DrawLineClipped(Vector2Int v0, Vector2Int v1, uint32_t rgba)
|
||||
{
|
||||
int32_t x0 = v0.x, y0 = v0.y;
|
||||
int32_t x1 = v1.x, y1 = v1.y;
|
||||
if (!cs_clip_line(v0.x, v0.y, v1.x, v1.y,
|
||||
frameBuffer->get_width(), frameBuffer->get_height()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (std::abs(x1 - x0) > std::abs(y1 - y0))
|
||||
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, color);
|
||||
DrawLineHorizontal(v0, v1, rgba);
|
||||
}
|
||||
else
|
||||
{
|
||||
DrawLineVertical(v0, v1, color);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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) {}
|
||||
|
|
|
|||
|
|
@ -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) / 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<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,13 +3,18 @@
|
|||
#include "BoundingBox.h"
|
||||
#include "Vertex.h"
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <Vector3.h>
|
||||
#include <cstdlib>
|
||||
|
||||
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<int32_t>(std::floor(std::min({ v0.position.x, v1.position.x, v2.position.x })));
|
||||
int32_t maxX = static_cast<int32_t>(std::ceil(std::max({ v0.position.x, v1.position.x, v2.position.x })));
|
||||
int32_t minY = static_cast<int32_t>(std::floor(std::min({ v0.position.y, v1.position.y, v2.position.y })));
|
||||
int32_t maxY = static_cast<int32_t>(std::ceil(std::max({ v0.position.y, v1.position.y, v2.position.y })));
|
||||
int32_t minX = static_cast<int32_t>(std::floor(detail::min3f(v0.position.x, v1.position.x, v2.position.x)));
|
||||
int32_t maxX = static_cast<int32_t>(std::ceil(detail::max3f(v0.position.x, v1.position.x, v2.position.x)));
|
||||
int32_t minY = static_cast<int32_t>(std::floor(detail::min3f(v0.position.y, v1.position.y, v2.position.y)));
|
||||
int32_t maxY = static_cast<int32_t>(std::ceil(detail::max3f(v0.position.y, v1.position.y, v2.position.y)));
|
||||
|
||||
Vector2Int min(minX, minY);
|
||||
Vector2Int max(maxX, maxY);
|
||||
|
|
|
|||
Loading…
Reference in New Issue