求助:基于CImg库用中点算法绘制三角形及线性插值边缘填色
使用CImg实现中点算法画三角形 + 线性插值填充
嘿,我来帮你一步步搞定这个需求!我知道刚接触CImg的时候会有点懵,咱们从基础到功能慢慢拆解,最后给你完整的可运行代码。
第一步:先搞定CImg的基础操作
你已经能生成黑色空白图了,那咱们先补一下怎么修改像素颜色。CImg里访问像素的方式是img(x, y, c),其中x是横坐标,y是纵坐标,c是通道(0=红,1=绿,2=蓝)。比如给(100,100)的像素设为红色:
img(100, 100, 0) = 255; img(100, 100, 1) = 0; img(100, 100, 2) = 0;
显示图像用img.display("Window Title");,记得要包含头文件#include "CImg.h",并用using namespace cimg_library;简化代码(当然你也可以不用,不过新手用这个省事儿)。
第二步:用中点算法实现直线绘制
三角形是三条直线组成的,所以先写一个中点画直线的函数。中点算法的核心是通过判断中点位置来决定下一个像素,避免浮点运算,效率很高。
这里给你封装成一个函数,参数是图像对象、起点(x1,y1)、终点(x2,y2),还有RGB颜色:
void midPointLine(CImg<unsigned char>& img, int x1, int y1, int x2, int y2, int r, int g, int b) { int dx = abs(x2 - x1); int dy = abs(y2 - y1); int sx = (x1 < x2) ? 1 : -1; int sy = (y1 < y2) ? 1 : -1; int err = dx - dy; while (true) { // 设置当前像素颜色 if (x1 >= 0 && x1 < img.width() && y1 >=0 && y1 < img.height()) { img(x1, y1, 0) = r; img(x1, y1, 1) = g; img(x1, y1, 2) = b; } if (x1 == x2 && y1 == y2) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x1 += sx; } if (e2 < dx) { err += dx; y1 += sy; } } }
这个函数会处理所有方向的直线,还加了边界判断,避免越界访问像素。
第三步:线性插值实现三角形颜色填充
接下来是填充部分,咱们用扫描线线性插值的思路:遍历三角形的每一行(y从最小顶点到最大顶点),找到该行与三角形两条边的交点,然后在两个交点之间,根据顶点颜色做线性插值填充。
首先,咱们需要一个辅助函数,用来计算两个点之间的颜色插值:
// 计算两点之间的线性插值颜色,t是0~1的比例 void interpolateColor(int x_start, int y, int x_end, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, CImg<unsigned char>& img) { int dx = abs(x_end - x_start); if (dx == 0) { // 单点,直接赋值 img(x_start, y, 0) = r1; img(x_start, y, 1) = g1; img(x_start, y, 2) = b1; return; } float step_r = (float)(r2 - r1) / dx; float step_g = (float)(g2 - g1) / dx; float step_b = (float)(b2 - b1) / dx; float current_r = r1; float current_g = g1; float current_b = b1; int x_min = min(x_start, x_end); int x_max = max(x_start, x_end); for (int x = x_min; x <= x_max; x++) { if (x >=0 && x < img.width() && y >=0 && y < img.height()) { img(x, y, 0) = (unsigned char)round(current_r); img(x, y, 1) = (unsigned char)round(current_g); img(x, y, 2) = (unsigned char)round(current_b); } current_r += step_r; current_g += step_g; current_b += step_b; } }
然后是填充三角形的主函数,这里我们先把三个顶点按y坐标排序,分成上半部分和下半部分处理,分别计算每一行的左右交点,再插值填充:
void fillTriangle(CImg<unsigned char>& img, int x1, int y1, unsigned char r1, unsigned char g1, unsigned char b1, int x2, int y2, unsigned char r2, unsigned char g2, unsigned char b2, int x3, int y3, unsigned char r3, unsigned char g3, unsigned char b3) { // 先按y坐标排序顶点,v1是最上面,v3是最下面 int vx[3] = {x1, x2, x3}; int vy[3] = {y1, y2, y3}; unsigned char vr[3] = {r1, r2, r3}; unsigned char vg[3] = {g1, g2, g3}; unsigned char vb[3] = {b1, b2, b3}; // 冒泡排序按y从小到大 for (int i = 0; i < 2; i++) { for (int j = 0; j < 2 - i; j++) { if (vy[j] > vy[j+1]) { swap(vx[j], vx[j+1]); swap(vy[j], vy[j+1]); swap(vr[j], vr[j+1]); swap(vg[j], vg[j+1]); swap(vb[j], vb[j+1]); } } } int y_start = vy[0]; int y_mid = vy[1]; int y_end = vy[2]; // 计算斜率相关的增量,用于求每一行的x交点 float dx13 = (float)(vx[2] - vx[0]) / (y_end - y_start); float dx12 = (float)(vx[1] - vx[0]) / (y_mid - y_start); float dx23 = (float)(vx[2] - vx[1]) / (y_end - y_mid); // 颜色增量 float dr13 = (float)(vr[2] - vr[0]) / (y_end - y_start); float dg13 = (float)(vg[2] - vg[0]) / (y_end - y_start); float db13 = (float)(vb[2] - vb[0]) / (y_end - y_start); float dr12 = (float)(vr[1] - vr[0]) / (y_mid - y_start); float dg12 = (float)(vg[1] - vg[0]) / (y_mid - y_start); float db12 = (float)(vb[1] - vb[0]) / (y_mid - y_start); float dr23 = (float)(vr[2] - vr[1]) / (y_end - y_mid); float dg23 = (float)(vg[2] - vg[1]) / (y_end - y_mid); float db23 = (float)(vb[2] - vb[1]) / (y_end - y_mid); // 处理上半部分:y从y_start到y_mid float x_left = vx[0]; float x_right = vx[0]; float cr_left = vr[0], cg_left = vg[0], cb_left = vb[0]; float cr_right = vr[0], cg_right = vg[0], cb_right = vb[0]; for (int y = y_start; y <= y_mid; y++) { interpolateColor((int)round(x_left), y, (int)round(x_right), (unsigned char)round(cr_left), (unsigned char)round(cg_left), (unsigned char)round(cb_left), (unsigned char)round(cr_right), (unsigned char)round(cg_right), (unsigned char)round(cb_right), img); x_left += dx12; x_right += dx13; cr_left += dr12; cg_left += dg12; cb_left += db12; cr_right += dr13; cg_right += dg13; cb_right += db13; } // 处理下半部分:y从y_mid到y_end x_left = vx[1]; cr_left = vr[1]; cg_left = vg[1]; cb_left = vb[1]; for (int y = y_mid; y <= y_end; y++) { interpolateColor((int)round(x_left), y, (int)round(x_right), (unsigned char)round(cr_left), (unsigned char)round(cg_left), (unsigned char)round(cb_left), (unsigned char)round(cr_right), (unsigned char)round(cg_right), (unsigned char)round(cb_right), img); x_left += dx23; x_right += dx13; cr_left += dr23; cg_left += dg23; cb_left += db23; cr_right += dr13; cg_right += dg13; cb_right += db13; } }
第四步:完整代码示例
把所有部分整合起来,你可以直接运行这个代码(记得把CImg.h放在项目目录里,或者链接正确的头文件路径):
#include "CImg.h" #include <iostream> #include <algorithm> // 用于swap和min/max using namespace cimg_library; // 中点画直线函数 void midPointLine(CImg<unsigned char>& img, int x1, int y1, int x2, int y2, int r, int g, int b) { int dx = abs(x2 - x1); int dy = abs(y2 - y1); int sx = (x1 < x2) ? 1 : -1; int sy = (y1 < y2) ? 1 : -1; int err = dx - dy; while (true) { if (x1 >= 0 && x1 < img.width() && y1 >=0 && y1 < img.height()) { img(x1, y1, 0) = r; img(x1, y1, 1) = g; img(x1, y1, 2) = b; } if (x1 == x2 && y1 == y2) break; int e2 = 2 * err; if (e2 > -dy) { err -= dy; x1 += sx; } if (e2 < dx) { err += dx; y1 += sy; } } } // 线性插值颜色填充一行 void interpolateColor(int x_start, int y, int x_end, unsigned char r1, unsigned char g1, unsigned char b1, unsigned char r2, unsigned char g2, unsigned char b2, CImg<unsigned char>& img) { int dx = abs(x_end - x_start); if (dx == 0) { if (x_start >=0 && x_start < img.width() && y >=0 && y < img.height()) { img(x_start, y, 0) = r1; img(x_start, y, 1) = g1; img(x_start, y, 2) = b1; } return; } float step_r = (float)(r2 - r1) / dx; float step_g = (float)(g2 - g1) / dx; float step_b = (float)(b2 - b1) / dx; float current_r = r1; float current_g = g1; float current_b = b1; int x_min = std::min(x_start, x_end); int x_max = std::max(x_start, x_end); for (int x = x_min; x <= x_max; x++) { if (x >=0 && x < img.width() && y >=0 && y < img.height()) { img(x, y, 0) = (unsigned char)round(current_r); img(x, y, 1) = (unsigned char)round(current_g); img(x, y, 2) = (unsigned char)round(current_b); } current_r += step_r; current_g += step_g; current_b += step_b; } } // 填充三角形 void fillTriangle(CImg<unsigned char>& img, int x1, int y1, unsigned char r1, unsigned char g1, unsigned char b1, int x2, int y2, unsigned char r2, unsigned char g2, unsigned char b2, int x3, int y3, unsigned char r3, unsigned char g3, unsigned char b3) { int vx[3] = {x1, x2, x3}; int vy[3] = {y1, y2, y3}; unsigned char vr[3] = {r1, r2, r3}; unsigned char vg[3] = {g1, g2, g3}; unsigned char vb[3] = {b1, b2, b3}; // 按y坐标排序 for (int i = 0; i < 2; i++) { for (int j = 0; j < 2 - i; j++) { if (vy[j] > vy[j+1]) { std::swap(vx[j], vx[j+1]); std::swap(vy[j], vy[j+1]); std::swap(vr[j], vr[j+1]); std::swap(vg[j], vg[j+1]); std::swap(vb[j], vb[j+1]); } } } int y_start = vy[0]; int y_mid = vy[1]; int y_end = vy[2]; if (y_start == y_end) { // 水平线三角形,直接填充一行 interpolateColor(vx[0], y_start, vx[2], vr[0], vg[0], vb[0], vr[2], vg[2], vb[2], img); return; } float dx13 = (float)(vx[2] - vx[0]) / (y_end - y_start); float dx12 = (float)(vx[1] - vx[0]) / (y_mid - y_start); float dx23 = (float)(vx[2] - vx[1]) / (y_end - y_mid); float dr13 = (float)(vr[2] - vr[0]) / (y_end - y_start); float dg13 = (float)(vg[2] - vg[0]) / (y_end - y_start); float db13 = (float)(vb[2] - vb[0]) / (y_end - y_start); float dr12 = (float)(vr[1] - vr[0]) / (y_mid - y_start); float dg12 = (float)(vg[1] - vg[0]) / (y_mid - y_start); float db12 = (float)(vb[1] - vb[0]) / (y_mid - y_start); float dr23 = (float)(vr[2] - vr[1]) / (y_end - y_mid); float dg23 = (float)(vg[2] - vg[1]) / (y_end - y_mid); float db23 = (float)(vb[2] - vb[1]) / (y_end - y_mid); // 上半部分 float x_left = vx[0]; float x_right = vx[0]; float cr_left = vr[0], cg_left = vg[0], cb_left = vb[0]; float cr_right = vr[0], cg_right = vg[0], cb_right = vb[0]; for (int y = y_start; y <= y_mid; y++) { interpolateColor((int)
相关产品推荐
相关产品推荐

