在完成了渲染管线的几何阶段之后,我们得到了三维物体在屏幕上的二维坐标,接下来,我们将进行光栅化操作。
光栅化
计算机屏幕是由点阵构成的,这些点阵称为像素。显然,这些像素是离散的,我们是不可能精确的绘制出绝对意义平滑的直线。
所谓的光栅化,就是将图形填充到像素格上。
这里又分为线框模式的填充和实心填充。
所谓线框模式的填充,就是只描绘出三维物体的外边框。
实心的填充,即根据结点的各种几何信息,填充在像素格子上。
线框填充模式:只勾勒出了图形的外框
实体填充模式:图形内部也被填充上颜色
本文暂时只介绍线框填充模式。
很显然,线框填充的关键点在于如何根据两点在屏幕上画一条直线,这里介绍一种高效的绘线算法,
Bresenham绘线算法。(布雷森汉姆)
Bresenham绘线算法
原理
Bresenham直线算法是用来描绘由两点所决定的直线的算法,它会算出一条线段在n维位图上最接近的点。这个算法只会用到较为快速的整数加法、减法和位元移位,常用于绘制电脑画面中的直线。是计算机图形学中最先发展出来的算法。
为了描述算法的原理,我们做出如下假设,之后再推广到所有的情况:
(1)以常见的屏幕坐标系建立坐标系,左上角为(0,0)点,x向右方向增长,y向下增长
(2)直线的方程为y = mx + n, 且直线与x轴的夹角小于45度大于0度,也就是 0 < m < 1
如图所示:
根据直线的定义,每当x+1,y增加m。虽然x,y都是整数,但m = dy/dx未必为整数,因此y+m可能不是整数,是存在误差的,要进行该算法,我们要设定一个误差值。
误差定义为某点x实际的y与绘制的y之间的差值,例如在上图中:
x+1直线上实际对应的点是红绿线的交点(x+1,y+m+error),但是我们绘制的时候绘制的是(x+1,y+1).由图可知,每当x+1,error相应增加m,如果说error + m > 0.5,那么说明将要绘制的点距离y+1更近一点,否则距离y更近。
这个过程可以被以下代码描述:
BresenhameDrawLine(point p1, point p2){
//p1 左上 p2 右下
int dx = p2.x - p1.x;
int dy = p2.y - p1.y;
float error = 0f;
float m = dy/dx;
int y = p1.y;
for(x = p1.x ; x < p2.x; x++){
error += m;
if(abs(error) > 0.5){
plot(x,y+1);
error -= 1.0f;//为何-1.0f是因为m永远小于1
}
else{
plot(x,y);
}
y += 1;
}
}
一般化
虽然以上的算法只能绘画由左上至右下,且斜率小于或等于1的直线,但我们可以扩展此算法,使之可绘画任何的直线。
第一个扩展是绘画反方向,即由右下至左上的直线。这可以简单地通过在x1 > x2时交换起点和终点来做到。第二个扩展是绘画斜率为负的直线。可以检查y0 ≥ y1是否成立;若该不等式成立,误差超出0.5时y的值改为加-1。
第三个扩展是绘画斜率绝对值大于1的直线。要做到这点,我们可以利用大斜率直线对直线y=x的反射是一条小斜率直线的事实,在整个计算过程中交换 x 和 y,并一并将plot的参数顺序交换。扩展后的伪代码如下:
BresenhameDrawLine(point p1, point p2){
bool steep = abs(p2.y