基于重心坐标算法的三角形边缘光栅化异常问题求助
重心坐标算法渲染三角形边缘偏差问题排查
问题描述
正在给自定义3D渲染引擎添加Z-buffer,采用重心坐标算法对三角形内部点的深度进行插值,但该算法在三角形边缘计算上存在偏差。左侧绿色为旧算法渲染结果,右侧红色为重心坐标算法结果,叠加后边缘偏差明显。
测试三角形坐标(渲染分辨率320x240,右侧三角形仅偏移50像素):
{50, 20} {70, 60} {30, 50}

旋转三角形示例(正确边缘为红色):
算法实现
重心坐标计算与渲染代码
void barycentric(vec3f a, vec3f b, vec3f c, vec3f p, float *u, float *v, float *w) { vec3f v0 = vec3f_sub(b,a), v1 = vec3f_sub(c,a), v2 = vec3f_sub(p,a); float d00 = vec3f_dot_product(v0, v0); float d01 = vec3f_dot_product(v0, v1); float d11 = vec3f_dot_product(v1, v1); float d20 = vec3f_dot_product(v2, v0); float d21 = vec3f_dot_product(v2, v1); float denom = d00 * d11 - d01 * d01; *v = (d11 * d20 - d01 * d21) / denom; *w = (d00 * d21 - d01 * d20) / denom; *u = 1.0f - *v - *w; } void drawBarycentricTriangle(uint32_t pixels[], vec3f a, vec3f b, vec3f c, uint32_t color) { int min_x = (int)round(fminf(a.x, fminf(b.x, c.x))); int max_x = (int)round(fmaxf(a.x, fmaxf(b.x, c.x))); int min_y = (int)round(fminf(a.y, fminf(b.y, c.y))); int max_y = (int)round(fmaxf(a.y, fmaxf(b.y, c.y))); // iterate over the bounding box of the triangle for (int y = min_y; y <= max_y; y++) { for (int x = min_x; x <= max_x; x++) { vec3f p = {x, y, 0}; float u, v, w; barycentric(a, b, c, p, &u, &v, &w); // only draw pixels if the points are inside the triangle if (u >= 0 && v >= 0 && w >= 0) { drawPixel(pixels, x, y, color); } } } }
旧三角形渲染算法代码
void drawTriangle(uint32_t pixels[], vec2i P0, vec2i P1, vec2i P2, uint32_t color) { // sort the points by y-coordinate if (P1.y < P0.y) { swap(&P1.y, &P0.y); swap(&P1.x, &P0.x); } if (P2.y < P0.y) { swap(&P2.y, &P0.y); swap(&P2.x, &P0.x); } if (P2.y < P1.y) { swap(&P2.y, &P1.y); swap(&P2.x, &P1.x); } // degenerate triangle if all points on the same horizontal line if (P0.y == P1.y && P1.y == P2.y) return; if (P0.x<0 && P1.x<0 && P2.x<0) return; if (P0.y<0 && P1.y<0 && P2.y<0) return; float invslope1, invslope2, curx1, curx2; if (P0.y == P1.y) { // flat-top triangle if (P1.x < P0.x) { swap(&P1.x, &P0.x); } invslope1 = (float)(P2.x - P0.x) / (P2.y - P0.y); invslope2 = (float)(P2.x - P1.x) / (P2.y - P1.y); curx1 = P2.x; curx2 = P2.x; for (int y = P2.y; y >= P0.y; y--) { int start = (int)round(curx1); int end = (int)round(curx2); if (start > end) { swap(&start, &end); } for (int x = start; x <= end; x++) { drawPixel(pixels, x, y, color); } curx1 -= invslope1; curx2 -= invslope2; } } else if (P1.y == P2.y) { // flat-bottom triangle if (P2.x < P1.x) { swap(&P2.x, &P1.x); } invslope1 = (float)(P1.x - P0.x) / (P1.y - P0.y); invslope2 = (float)(P2.x - P0.x) / (P2.y - P0.y); curx1 = P0.x; curx2 = P0.x; for (int y = P0.y; y <= P1.y; y++) { int start = (int)round(curx1); int end = (int)round(curx2); if (start > end) { swap(&start, &end); } for (int x = start; x <= end; x++) { drawPixel(pixels, x, y, color); } curx1 += invslope1; curx2 += invslope2; } } else { // general case: split into a flat-top and flat-bottom triangle vec2i P3 = { P0.x + (int)((float)(P1.y - P0.y) / (P2.y - P0.y) * (P2.x - P0.x)), P1.y }; invslope1 = (float)(P1.x - P0.x) / (P1.y - P0.y); invslope2 = (float)(P3.x - P0.x) / (P3.y - P0.y); curx1 = P0.x; curx2 = P0.x; for (int y = P0.y; y <= P1.y; y++) { int start = (int)round(curx1); int end = (int)round(curx2); if (start > end) { swap(&start, &end); } for (int x = start; x <= end; x++) { drawPixel(pixels, x, y, color); } curx1 += invslope1; curx2 += invslope2; } invslope1 = (float)(P2.x - P1.x) / (P2.y - P1.y); invslope2 = (float)(P2.x - P3.x) / (P2.y - P3.y); curx1 = P2.x; curx2 = P2.x; for (int y = P2.y; y >= P1.y; y--) { int start = (int)round(curx1); int end = (int)round(curx2); if (start > end) { swap(&start, &end); } for (int x = start; x <= end; x++) { drawPixel(pixels, x, y, color); } curx1 -= invslope1; curx2 -= invslope2; } } }
渲染效果对比
重心坐标算法渲染效果:

旧算法渲染效果:

核对过算法逻辑未发现问题,请求定位边缘偏差原因并提供修复方案。
内容的提问来源于stack exchange,提问作者Nitaki
相关产品推荐
相关产品推荐

