插值画线算法
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) {
for (float t=0.; t<1.; t+=.01) {
int x = x0 + (x1-x0)*t;
int y = y0 + (y1-y0)*t;
image.set(x, y, color);
}
}
- 存在问题
- 效率低下,不好确定插值精度k
- 算法控制精度的方式无关屏幕像素点,无法做到真正的逐点绘制,x和y是一个近似的int值,可能存在重复绘制和缺省绘制的情况,我们需要逐x像素或逐y像素绘制线段。
- 效率低下,不好确定插值精度k
逐像素直线算法
void line(int x1,int y1,int x2,int y2,TGAImage& image,TGAColor color)
{
for(int x = x1; x <= x2; ++x)
{
float t = (x - x1) / (float)(x2 - x1);
int y = y1 * (1.f - t) + y2 * t;
image.set(x,y,color);
}
}
line(13, 20, 80, 40, image, white);
line(20, 13, 40, 80, image, red);
line(80, 40, 13, 20, image, red);
- 存在问题:
- 第一条直线比较好
- 第二条直线有很多洞,不太行
- 未出现第三条线,第一第三条线是一样的只是颜色和方向不一样
- 因为我们的算法从默认是x1 < x2的,如果按照直线3的画法将导致进入不了循环
改进逐像素直线算法
- 如果斜率的绝对值大于1,以y=x做对称
保证从直线中坐标小的位置绘制到坐标大的位置
void line(int x1,int y1,int x2,int y2,TGAImage& image,TGAColor color) { bool yDir = false; //如果斜率的绝对值大于1,以y=x做对称 if(abs(x1-x2) < abs(y1-y2)) { swap(x1,y1); swap(x2,y2); yDir = true; } //保证从直线中坐标小的位置绘制到坐标大的位置 if(x1 > x2) { swap(x1,x2); swap(y1,y2); } for(int x = x1; x <= x2; ++x) { float t = (x - x1) /(float)(x2-x1); int y = y1*(1.f - t) + y2*t; if(yDir) image.set(y,x,color); else image.set(x,y,color); } }
存在问题
- 效率低下
Bresenham算法
方法见之前的笔记:画线算法
void line(int x1,int y1,int x2,int y2,TGAImage& image,TGAColor color) { bool yDir = false; //如果斜率的绝对值大于1,以y=x做对称 if(abs(x1-x2) < abs(y1-y2)) { swap(x1,y1); swap(x2,y2); yDir = true; } //保证从直线中坐标小的位置绘制到坐标大的位置 if(x1 > x2) { swap(x1,x2); swap(y1,y2); } int dx = x2-x1; int dy = y2-y1; float derror = abs(dy/(float)dx); float error = 0.f; int y = y1; for(int x = x1; x <= x2; ++x) { if(yDir) image.set(y,x,color); else image.set(x,y,color); error += derror; if(error > 0.5) { y += (y1 > y2) ? -1.f : 1.f ; error--; } } }
存在问题
- 涉及到浮点数运算,效率有所下降.
优化的Bresenham算法
- 方法见之前的笔记:画线算法
公式推导如下:
void line(int x1,int y1,int x2,int y2,TGAImage& image,TGAColor color) { bool yDir = false; //如果斜率的绝对值大于1,以y=x做对称 if(abs(x1-x2) < abs(y1-y2)) { swap(x1,y1); swap(x2,y2); yDir = true; } //保证从直线中坐标小的位置绘制到坐标大的位置 if(x1 > x2) { swap(x1,x2); swap(y1,y2); } int dx = x2-x1; int dy = y2-y1; int error = 0; int y = y1; for(int x = x1; x <= x2; ++x) { if(yDir) image.set(y,x,color); else image.set(x,y,color); error += 2 * dy; if(error > dx) { y += (y1 > y2) ? -1.f : 1.f; error -= 2*dx; } } }
Wireframe rendering(线框渲染)
- obj文件特点:
- v代表定点
- f代表面
// 实例化模型
model = new Model("obj/african_head.obj");
// 循环模型里的所有三角形
for (int i = 0; i < model->nfaces(); i++) {
std::vector<int> face = model->face(i);
// 循环三角形三个顶点,每两个顶点连一条线
for (int j = 0; j < 3; j++) {
Vec3f v0 = model->vert(face[j]);
Vec3f v1 = model->vert(face[(j + 1) % 3]);
// 因为模型空间取值范围是 [-1, 1]^3,我们要把模型坐标平移到屏幕坐标中
// 下面 (point + 1) * width(height) / 2 的操作学名为视口变换(Viewport Transformation)
int x0 = (v0.x + 1.) * width / 2.;
int y0 = (v0.y + 1.) * height / 2.;
int x1 = (v1.x + 1.) * width / 2.;
int y1 = (v1.y + 1.) * height / 2.;
// 画线
line(x0, y0, x1, y1, image, white);
}
}