Lesson 1 Bresenham’s Line Drawing Algorithm

  1. 插值画线算法
  2. 逐像素直线算法
  3. 改进逐像素直线算法
  4. Bresenham算法
  5. 优化的Bresenham算法
  6. Wireframe rendering(线框渲染)

插值画线算法

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像素绘制线段。

逐像素直线算法

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);
  }
}