Skip to content

Commit d6e3b40

Browse files
committed
TBR: Replace barycentric coordinate computation with half-space testing to enable SIMD-friendly rasterization; use relative-coordinate cross products to ensure numerical stability.
Signed-off-by: ZhouFANG <indevn@outlook.com>
1 parent 957c9b0 commit d6e3b40

1 file changed

Lines changed: 64 additions & 23 deletions

File tree

src/rasterizer.cpp

Lines changed: 64 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -145,41 +145,82 @@ void Rasterizer::RasterizeTo(const VertexSoA& soa, size_t i0, size_t i1, size_t
145145
const Vector4f& p1 = soa.pos_screen[i1];
146146
const Vector4f& p2 = soa.pos_screen[i2];
147147

148-
Vector2f a = Vector2f(p0.x, p0.y);
149-
Vector2f b = Vector2f(p1.x, p1.y);
150-
Vector2f c = Vector2f(p2.x, p2.y);
148+
// 为BarycentricCoord预构造Vec3f,避免循环内重复构造
149+
const Vector3f sp0(p0.x, p0.y, p0.z);
150+
const Vector3f sp1(p1.x, p1.y, p1.z);
151+
const Vector3f sp2(p2.x, p2.y, p2.z);
151152

152-
Vector2f bboxMin = Vector2f{std::min({a.x, b.x, c.x}), std::min({a.y, b.y, c.y})};
153-
Vector2f bboxMax = Vector2f{std::max({a.x, b.x, c.x}), std::max({a.y, b.y, c.y})};
154-
155-
// Clamp 到屏幕尺寸
156-
float minx = std::max(0.0f, bboxMin.x);
157-
float miny = std::max(0.0f, bboxMin.y);
158-
float maxx = std::min(float(width_ - 1), bboxMax.x);
159-
float maxy = std::min(float(height_ - 1), bboxMax.y);
153+
// 计算屏幕空间AABB包围盒
154+
const float minx_f = std::max(0.0f, std::min({p0.x, p1.x, p2.x}));
155+
const float miny_f = std::max(0.0f, std::min({p0.y, p1.y, p2.y}));
156+
const float maxx_f = std::min(float(width_ - 1), std::max({p0.x, p1.x, p2.x}));
157+
const float maxy_f = std::min(float(height_ - 1), std::max({p0.y, p1.y, p2.y}));
160158

161159
// 与外部提供的裁剪区域相交(半开区间) -> 闭区间扫描
162-
int sx = std::max(x0, static_cast<int>(std::floor(minx)));
163-
int sy = std::max(y0, static_cast<int>(std::floor(miny)));
164-
int ex = std::min(x1 - 1, static_cast<int>(std::floor(maxx)));
165-
int ey = std::min(y1 - 1, static_cast<int>(std::floor(maxy)));
160+
int sx = std::max(x0, static_cast<int>(std::floor(minx_f)));
161+
int sy = std::max(y0, static_cast<int>(std::floor(miny_f)));
162+
int ex = std::min(x1 - 1, static_cast<int>(std::floor(maxx_f)));
163+
int ey = std::min(y1 - 1, static_cast<int>(std::floor(maxy_f)));
166164
if (sx > ex || sy > ey) return;
167165

168-
for (int x = sx; x <= ex; ++x) {
169-
for (int y = sy; y <= ey; ++y) {
170-
auto [is_inside, bary] = GetBarycentricCoord(
171-
Vector3f(p0.x, p0.y, p0.z), Vector3f(p1.x, p1.y, p1.z), Vector3f(p2.x, p2.y, p2.z),
172-
Vector3f(static_cast<float>(x), static_cast<float>(y), 0));
173-
if (!is_inside) continue;
166+
// 预计算边函数系数:E(x,y) = A*x + B*y + C
167+
// 使用相对坐标的边函数定义,避免大常数项导致的数值不稳定
168+
// 如使用绝对形式Ax+By+C会由于常数C的量级过大,造成浮点抵消,有效位丢失不稳定
169+
auto cross2 = [](float ax, float ay, float bx, float by) {
170+
return ax * by - ay * bx;
171+
};
172+
// 边向量
173+
const float e01x = p1.x - p0.x, e01y = p1.y - p0.y; // (p0->p1)
174+
const float e12x = p2.x - p1.x, e12y = p2.y - p1.y; // (p1->p2)
175+
const float e20x = p0.x - p2.x, e20y = p0.y - p2.y; // (p2->p0)
176+
177+
// 有向面积(两倍),用相对面积定义:area2 = cross(p1 - p0, p2 - p0)
178+
float area2 = cross2(e01x, e01y, p2.x - p0.x, p2.y - p0.y);
179+
if (std::abs(area2) < 1e-6f) return; // 退化三角形
180+
const float inv_area2 = 1.0f / area2;
181+
const bool positive = (area2 > 0.0f);
182+
183+
// 行优先遍历:有利于 cache 与向量化
184+
#pragma omp simd
185+
for (int y = sy; y <= ey; ++y) {
186+
const float yf = static_cast<float>(y);
187+
188+
// 注意:此处存在对 out.push_back 的写入,属于有副作用操作,不适合使用
189+
// omp simd 进行强制向量化,否则可能导致不符合预期的行为(如周期性伪影)。
190+
// 先保持标量内层,后续如切换为“直写像素回调”再考虑安全的 SIMD 化。
191+
for (int x = sx; x <= ex; ++x) {
192+
const float xf = static_cast<float>(x);
193+
194+
// 相对坐标边函数:
195+
// E01(p) = cross(p1 - p0, p - p0)
196+
// E12(p) = cross(p2 - p1, p - p1)
197+
// E20(p) = cross(p0 - p2, p - p2)
198+
const float E01 = cross2(e01x, e01y, xf - p0.x, yf - p0.y);
199+
const float E12 = cross2(e12x, e12y, xf - p1.x, yf - p1.y);
200+
const float E20 = cross2(e20x, e20y, xf - p2.x, yf - p2.y);
201+
202+
// 半空间测试(根据朝向选择符号)
203+
const bool inside = positive ? (E01 >= 0.0f && E12 >= 0.0f && E20 >= 0.0f)
204+
: (E01 <= 0.0f && E12 <= 0.0f && E20 <= 0.0f);
205+
if (!inside) continue;
206+
207+
// 重心权重映射:
208+
// b0 对应 v0,取与对边 (v1,v2) 的子面积 → E12
209+
// b1 对应 v1 → E20
210+
// b2 对应 v2 → E01
211+
const float b0 = E12 * inv_area2;
212+
const float b1 = E20 * inv_area2;
213+
const float b2 = E01 * inv_area2;
214+
const Vector3f bary(b0, b1, b2);
174215

175216
// 透视矫正插值
176217
auto perspective_result = PerformPerspectiveCorrection(
177218
p0.w, p1.w, p2.w,
178219
p0.z, p1.z, p2.z,
179220
bary);
180-
221+
181222
const Vector3f& corrected_bary = perspective_result.corrected_barycentric;
182-
float z = perspective_result.interpolated_z;
223+
const float z = perspective_result.interpolated_z;
183224

184225
Fragment frag; // Note: material 指针由调用方填写
185226
frag.screen_coord = {x, y};

0 commit comments

Comments
 (0)