再談繪製直線
再談繪製直線
之前已經在[從零開始計算機圖形學]之七畫線 寫過繪製直線了,現在再來仔細的看一下這個問題。
此篇文章思想來自此處:
tinyrenderer嘗試一: 按照參數繪製直線
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);
}
}
這裡的問題有兩個:
- 效率低
- t如何控制
t取大了畫出來的並不是線,而是一堆點。t取小了會浪費,有很多重複的x和y。
嘗試二: 按x的增加畫線
void line(int x0, int y0, int x1, int y1, TGAImage &image, TGAColor color) {
for (int x=x0; x<=x1; x++) {
float t = (x-x0)/(float)(x1-x0);
int y = y0 + (y1 - y0)*t;
image.set(x, y, color);
}
}
我們想著要節約,就每次 x 增加1,然後來畫y。
這樣畫線是對的因為我們假設 , 直線斜率m, 截距b
所以
同時它的問題是我們也已經指出:
- 如果直線斜率太大,比如 m = 3, 那麼x每增加1個像素,y增加3個像素,這樣畫出來就是分離的點。
- 只能適用於 x0 < x1的狀況
嘗試三
所以想法是:
- 如有必要交換 x0 和 x1,這樣使得 x0 一定小於 x1
- 如果斜率比較大,則交換 x 和 y
看代碼:
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)) { // if the line is steep, we transpose the image
std::swap(x0, y0);
std::swap(x1, y1);
steep = true;
}
if (x0>x1) { // make it left?to?right
std::swap(x0, x1);
std::swap(y0, y1);
}
for (int x=x0; x<=x1; x++) {
float t = (x-x0)/(float)(x1-x0);
int y = y0 + (y1 - y0)*t;
if (steep) {
image.set(y, x, color); // if transposed, de?transpose
} else {
image.set(x, y, color);
}
}
}
這樣就可以完善上述出現的問題來畫線了。
嘗試四:優化
其實上述代碼加上 compiler 本身的優化已經可以很快了 (g++ -O3),但是因為歷史原因我們繼續來看它的優化畫法,首先是上述演算法中
- 每次都出現了相同的被除數 → float t = (x-x0)/(float)(x1-x0)
- 我們只需要畫在整數的像素上
我們現在一定有 , 畢竟如果 都已經被我們交換了。