如何绘制直线
文章目錄
- 前言
- 一、最簡單繪制直線的方法
- 1.1 向量
- 二、Bresenham’s Line Drawing Algorithm
- 2.1 屏幕是離散的
- 2.2 直線方程的應用
前言
本系列最終是要去繪制mesh的,既然這樣我們就需要從最簡單的開始,先繪制直線。
我們都知道有個算法-Bresenham’s Line Drawing Algorithm。但是為什么要用它,這個才是我們要講的。
參考
gamma公式
本系列主要參考Dmitry V. Sokolov的軟渲染教程,詳細教程可閱讀參考鏈接
一、最簡單繪制直線的方法
說起直線,我的第一想法就是直線方程
y=kx+by = kx+by=kx+b
但是直接利用直線方程,然后直接去編寫代碼還其實挺困難的,我們先換一種方式考慮。
1.1 向量
向量可以被拆分成兩條向量的和
那么我們利用這個性質就可以寫出這么一段代碼
for (float t=0.; t<1.; t+=.000001) {int x = x0 + (x1 - x0) * t;int y = y0 + (y1 - y0) * t;image.set(x, y, color); }這段代碼非常簡單,可以繪制任何方向上的直線,但是有個致命問題,就是非常消耗性能。t這個變量的取值決定了循環的次數,同時也影響了直線的連續性。因此我們必須改造這個方法。
二、Bresenham’s Line Drawing Algorithm
我對該算法的理解就是-只有加減法的算法
2.1 屏幕是離散的
注意以下只考慮第一象限,其他象限通過交換值即可解決
我們知道直線是連續的,但是我們的屏幕并非連續的,而是離散的,像素點之間是有1px距離的。
上面說了,直線其實是可以被拆開的,那么我們只讓某一個軸進行遞增,另外一軸跟著遞增不就行了么,那么現在我們就可以去掉循環條件中t這個變量。
for (int x = x0; x <= x1; x++) {float alpha = (x - x0) / (float)(x1 - x0);float y = y0 + (y1 - y0) * alpha;image.set(x, y, color); }循環條件中t變量是消除了,但是我們又引入了一個除法和一個乘法,那么這個肯定是不對的,因此我們是需要去除掉的。
2.2 直線方程的應用
為了消除循環中的乘除法,我們需要引入直線方程。
y=kx+by = kx+by=kx+b
觀察直線方程,當我們每前進一個像素時,也就是x+1,那么其實y軸其實也是固定增加的。每次只增加k。
y=k(x+1)+by = k(x+1)+by=k(x+1)+b
隨著k不斷在y軸上累加,當k累加大于1時,y就可以給自身加一了。
到這一步終于把循環內的乘除法給消除了。但是循環外還是存在一個除法,因此下一步就是去除循環外的除法。
推理過程就是以上。
到此終于完成這個沒有任何乘除法的算法。這個也是我們用Bresenham’s Line Drawing Algorithm的原因。
下面是完整代碼
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) {bool steep = false;if (std::abs(x0 - x1) < std::abs(y0 - y1)) {std::swap(x0, y0);std::swap(x1, y1);steep = true;}if (x0 > x1) {std::swap(x0, x1);std::swap(y0, y1);}int dy = y1 - y0;int dx = x1 - x0;int error = 0;int y = y0;int k = 1;// 處理 [-1, 0] 范圍內的斜率if (dy < 0){k = -1;dy = -dy;}for (int x = x0; x <= x1; x++){if (steep) {image.set(y, x, color);}else {image.set(x, y, color);}error += dy;if (error > dx){y += k;error -= dx;}} }總結
- 上一篇: 【指数编制系列七】价格指数编制方法
- 下一篇: 马士兵java ppt_[马士兵JAVA