OpenGL.光柵化就是掃描轉換

  • 進一步說,對於一些幾何,我們還只是有數學的抽象表達,但實際的圖像確實離散的像素點
  • 所以光柵化其實就是把數學表達轉化爲像素表達,得到的數據我們稱爲“片段”,用於進一步的片段處理

線段的掃描轉換

  • 對於線段來說,其數學描述只是兩個頂點(x1,y1)和(x2,y2)
  • 這兩個頂點自然可以很容易的對應兩個像素點,但這兩個頂點的連線也應該對應着若干個像素點纔行
  • 這裏默認像素位置均爲整數,儘管也有不是整數的情況

DDA算法

  • 一個很顯然的思路,就是求出線段的斜率,然後遞增x,y每次加上斜率並取整
  • 設x1 < x2
DDA(int x1,int y1,int x2,int y2){
    int x = 0;
    double y = y1, m = (y2-y1)/(double)(x2-x1);
    for(x=x1;x<=x2;++x){
        y += m;
        output(x,(int)y);
    }
}
  • 但是這樣寫僅對於 0<=|m|<=1 的情況才能得到連續的像素位置
  • 對於 |m|>1 的情況就 遞增y 就行了唄

Bresenham算法

  • DDA涉及了大量的浮點運算,這樣很耗時間的
  • 如果能有完全是整數運算的就好了,那肯定是快很多啊
  • 分析一下線段的轉換,只考慮 0 < m < 1的情況,其餘的對稱
    Bresenham1

  • 對於已經確定的點p,其下一個要選擇的點

    • 如果是綠色線,則更靠近m,則選擇m
    • 如果是紅色線,則更靠近n,則選擇n
  • 就是說,下一個選擇只有兩個選擇,如何選擇就看線段更靠近那個點
  • 怎樣來衡量“靠近”呢?
    • 取 Δx = x2 - x1 , Δy = y2 - y1
    • 設置決策變量d
    • 決策變量
      d=Δx(ab)
    • a爲交點與候選點n(更高的點)的距離
    • b爲交點與候選點m(同樣高度的點)的距離
    • Δx的存在是爲了使結果爲整數,之後會看到作用
    • 若d>0,則更靠近m,選m,否則選n
  • 對於已有的點p,其決策變量爲d1,考慮下面兩種情況
    • 情況1
    • 情況1圖
    • 對於點p的決策變量d1是大於0的,那麼下一次的決策變量
      • a2 = a1 - m
      • b2 = b1 + m
      • d2 = Δx * (a2 - b2)
      • d2 = Δx * (a1 - b1 - 2 * m)
      • d2 = d1 - 2 * Δy
    • 情況2
    • 情況1圖
    • 否則,那麼下一次的決策變量
      • a2 = a1 + 1 - m
      • b2 = b1 + m - 1
      • d2 = Δx * (a2 - b2) = Δx * (a1 - b1 + 2 - 2 * m)
      • d2 = Δx * (a1 - b1 + 2 * (1 - m))
      • d2 = Δx * (a1 - b1)+2 * Δx * (1-m)
      • d2 = d1 + 2 * (Δx - Δy)
    • PS:如果d1是整數,那麼d2就是整數
  • 儘管還有如下兩種情況,但實際上並無影響(應該是沒影響的吧)

    • 特例1
    • -
    • 特例2
  • 那隻要我們確定了決策變量的初值,之後的決策變量就都可以由整數運算的遞推公式得出

  • 那麼對於初始點(x1,y1)
    • a = 1
    • b = 0
    • d = Δx > 0 爲整數
  • 接下來的運算就都是整數了 =、=

多邊形的掃描轉換

  • 牽扯到多邊形其實會麻煩很多

內外點檢測

  • 針對多邊形,需要判斷某個點是不是在其內部
  • 對於凸多邊形很容易,如果一個點都在其邊的同一側,那麼就在其內部
    凸多邊形

  • 那麼對於任意多邊形呢?

  • 奇偶檢測法
    • 從該點向任意一個方向引射線
    • 如果與多邊形的交點個數爲偶數個(包括0),則該點爲外部點
    • 如果爲奇數個,則爲內部點
    • 對於計數
      • 與邊相交,計數增加一
      • 如果與頂點相交
        • 形成頂點的線如果在射線兩側,則增加一
        • 如果在射線同側,則增加2或不增加
  • 環繞數法
    • 同樣是從該點向任意一個方向引射線
    • 選多變形的一個頂點,沿着邊來一次走完整個多邊形的外框
      • 如果是從左到右經過射線,則環繞數減一
      • 如果是從右到左經過射線,則環繞數加一
    • 最後環繞數不爲零,則該點爲內部點
    • 一個很神奇的算法

填充算法####

  • 種子填充法
    • 先根據掃描線算法把多邊形的邊界轉換成像素位置信息
    • 再選取多邊形內部的一個點,進行遞歸遍歷
flood_fill(int x,int y){
    if(read_pixel(x,y)==NULL){
        get(x,y);
        flood_fill(x-1,y);
        flood_fill(x+1,y);
        flood_fill(x,y-1);
        flood_fill(x,y+1);
    }
}
  • 掃描線法
    • 從下往上建立新邊表 NET
    • 新邊表在對應y值出現新邊的一個頂點的時候
      • 記錄這個頂點的 x 值
      • 記錄在這條邊上,如果y增加1,則x的變化Δx
      • 記錄這條邊終點的y值
      • 每條記錄之間用鏈表連接
    • 對於活動邊表 AET
      • 從最小的y開始,即從下往上的掃描線,y遞增
      • 看對應的y值是否有新邊(根據NET),有則加入到AET中
      • 看當前AET中是否有終點y值等於當前y值的記錄,有則刪除
      • 對AET中所有的記錄進行排序,從小到大,先按照x值排序,再按照Δx值排序
      • 從前往後兩兩組合,填充當前兩條記錄的x值之間的所有點
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章