今天看golang的書,裡面有個程式示例用到了圖形學裡面的中點圓畫法和直線的光柵畫法。專業選修課老師講過這倆種演算法,但是睡過去了 t_t 。中點圓畫法和直線光柵畫法的基本思想是一致的(增量,增量修正,中點判斷,判別式去乘法去除法去浮點運算,迭代,圓還使用了對稱性),閒著無事推一下簡單的直線光柵畫法。
螢幕上的每乙個影象都是由乙個個微小的畫素點構成,這些畫素點構成了畫素陣列,作圖就是給這個陣列上的畫素點設定顏色的過程。
畫素點之間是有間隔的,但是很小,被肉眼忽略。一條直線y=kx+b
是連續的,我們無法在陣列上嚴格的畫出他,只能選取直線軌跡附近的畫素點近似地表現出來。
如果通過解直線方程去確定每乙個x
對應的y
然後畫點,雖然簡單準確,但無疑是低效的,首先每乙個點都要根據x
去求y
,而且避免不了乘法和浮點運算。
設p1(x1,y1),p2(x2,y2)
是直線的倆個端點(x2>x1
),並且我們在斜率0<=k<=1
的情況下討論(其他情況可以通過平移對稱等變換對映過去)。
dx=x2-x1
dy=y2-y1
k=dy/dx
若當前已畫點為(x0,y0)
,則下乙個點有倆個待選點pa(x0+1,y0)
和pb(x0+1,y0+1)
:
if y0+k>y0+0.5
則選擇pb
,否則選擇pa
.其中y0+k
表示按直線方程來計算下乙個點y
值的精確值,y0+0.5
表示倆個待選點的中點的y
值。這個判斷等價於:
note:選擇中點y值來作判斷其實是乙個間接的判斷,嚴格的講應是判斷這倆個待選點到直線的垂直距離的大小,但是作圖之後你會發現和這種判斷方式是等價的,因為構成了倆個相似的直角三角形。
dj=y0+k-y0-0.5
if dj>0else
令dg=2*dj*dx
,則dg=2*dy-dx
,那麼原判斷等價於
if dg>0else
這裡我們通過dg
判斷出了下乙個點,那麼再下乙個點呢?
在下乙個點y
的精確值為y0+k+k
,待選點的y
值是上乙個點的y
值或是在其上加1
後的值,即假設上乙個點是(x,y)
則下乙個點是(x,y)
或(x,y+1)
這是由該該直線的斜率定死了的。
dj=(y0+k+k)-(y0+1)-0.5
if dj>0else
dj
在原來的基礎上增加了ddj=k-1
.對應dg
增加ddg=2*dy-2*dx
dj=(y0+k+k)-y0-0.5
if dj>0else
dj
在原來的基礎上增加了ddj=k
.對應dg
增加ddg=2*dy
所以有如下的偽**:
另外我正在看的《programming in go》這本書中的實現是(連他的注釋也copy了過來):dg=2*dy-dx;
還有乘法,將其除以2即可,下面這段**為全象限的完整演算法:point point(x1,y1);
for(int i=x1;i0)else
}std::cout<<"draw line done!"《判別式dg
//by yyrdl ,2015/12/18
func drawline(img draw.image, start, end image.point,fill color.color)
y := y0
ystep := 1
if y0 > y1
remainder := δy-δx/2
for x := x0; x <= x1; x++ else
}} else
x := x0
xstep := 1
if x0 > x1
δx,δy=δy,δx
remainder := δy-δx/2
for y := y0; y <= y1; y++ else}}
}
// based on my perl image::base.pm module's line() method
func drawline(img draw.image, start, end image.point,fill color.color)
y := y0
ystep := 1
if y0 > y1
remainder := float64(int(δx/2)) - δx
for x := x0; x <= x1; x++
}} else
x := x0
xstep := 1
if x0 > x1
remainder := float64(int(δy/2)) - δy
for y := y0; y <= y1; y++ }}
}
倆段**99%
相似,remainder
的迭代更新規則也是一模一樣的,只是初始值不一樣,我不明白他的初始值是怎麼來的,嚴重懷疑作者疏忽了,但是倆個**畫出的圖卻都是正常的。。。。
ps: 中點圓畫法也是按照這樣的思路推出來的,沒事可以推一下
-----記錄,分享
直線的「橡皮筋」畫法
微軟的畫圖程式中畫直線時,會有畫線提示,形象的說就像 橡皮筋 一樣,在未最終確定直線的長度和角度前都會有一根類似 橡皮筋 的直線,便於客戶可以方便的畫出理想的直線,具體實現方法如下 void cdrawview onlbuttondown uint nflags,cpoint point void ...
canvas直線 圓和正方形的畫法
var oca document.getelementbyid canvas var oev oca.getcontext 2d 設定繪圖環境 var w oca.width window.innerwidth var h oca.height window.innerheight 直線畫法 oev...
網頁光柵化
資料 總結來說,分塊的光柵化過程包含了以下三個主要的步驟 1.根據分塊的可見性,將它們劃分到不同的bin中。2.根據記憶體限制策略,從優先順序較高的bin中選集出需要光柵化的分塊。3.為每乙個需要光柵化的分塊分配光柵化記憶體,並且分別為它們建立光柵化任務。光柵化任務的執行過程又主要分為以下兩個步驟 ...