From Gossip@caterpillar

Computer Graphics: 頂點索引陣列

 

 


無論是2D或是3D繪製,頂點的使用是一個重要的課題,頂點的使用與座標息息相關,這也就是為何之前一直在談論座標系統的原因。

頂點的記錄方式有許多種,不同的繪圖目的應搭配不同的頂點資料結構,這邊介紹最簡單的幾個立體物件頂點記錄方式。

假設有一個正立方體,其中心位於原點,則可以如下圖先定出頂點的座標:


紙上作業與程式規劃所不同的是,如何使用這些頂點來繪製一個立方體,基本上必須以四個頂點為一個單位,使用繪製多邊型的函式來繪製一個四邊形,然後以較方 便的方式選擇四個頂點,通常會使用迴圈,但為了能使用迴圈,頂點資料結構必須有可重複索引的性質,在這邊介紹兩種規劃方式。

其中一個規劃方式是使用6*4=24個元素的陣列,每個面使用掉四個頂點,如下圖所示:


如此就可以使用迴圈取出頂點資訊,這個方法的好處是簡單,但由於頂點會有重複,因而會耗用大量的記憶體,對於複雜圖形並不適用。

可以使用頂點索引來解決頂點重複的問題,首先必須先將頂點編號,如下圖所示:


通常為了具有判別法向量的作用,頂點編號時使用右手定則,以逆時針的順序來編號同一個面的頂點;頂點編號完畢後,使用一個頂點索引陣列來記錄每個面所使用到的頂點編號,如下所示:
int v_ord[][] = {{0, 1, 2, 3}, {0, 7, 6, 1}, {4, 5, 6, 7},
                 {2, 5, 4, 3}, {0, 3, 4, 7}, {1, 6, 5, 2}};
 

使用索引陣列的好處是減少記憶體使用量,雖然額外使用了一個索引陣列,但對於頂點越多時,記憶體的減少使用會更顯著,但缺點就是必須額外耗用一些運算是處理頂點資訊。

下面是使用頂點索引陣列來繪製正立方體的Java Applet程式,您可以參考我們是如何處理頂點資訊的:

  • Vetex.java
import java.awt.*;
import java.applet.*;
import java.awt.event.*;

class Point3D {
public int x, y, z;

public Point3D() {
x = y = z = 0;
}

public Point3D(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
}

public class Vetex extends Applet {
public void paint(Graphics g) {
final int r = 100;

// 索引array
int v_ord[][] =
{{0, 1, 2, 3}, {0, 7, 6, 1}, {4, 5, 6, 7},
{2, 5, 4, 3}, {0, 3, 4, 7}, {1, 6, 5, 2}};

// 立方體頂點
Point3D[] vetex =
{new Point3D(r, r, r), new Point3D(r, -r, r),
new Point3D(r, -r, -r), new Point3D(r, r, -r),
new Point3D(-r, r, -r), new Point3D(-r, -r, -r),
new Point3D(-r, -r, r), new Point3D(-r, r, r)};

Point3D[] tp = new Point3D[4];

// 視窗中心
final int orgX = (int)getSize().width / 2,
orgY = (int)getSize().height / 2;
final double rd = Math.PI / 180;
double ax, ay;

int[] px = new int[4],
py = new int[4];

int i, j, k;

// 旋轉以斜角繪製圖形
ax = 30 * rd;
ay = -30 * rd;

setBackground(Color.black);
g.setColor(Color.yellow);

for(i = 0; i < 6; i++) {
for(j = 0; j < 4; j++) {
// 利用索引array取出正確的頂點
tp[j] = vetex[v_ord[i][j]];

// 旋轉以斜角繪製圖形
px[j] = (int) (tp[j].x*Math.cos(ay)
+ tp[j].z*Math.sin(ay));
py[j] = (int) (tp[j].y*Math.cos(ax)
- (-tp[j].x*Math.sin(ay)
+ tp[j].z*Math.cos(ay)) * Math.sin(ax));
px[j] = px[j] + orgX;
py[j] = -py[j] + orgY;
}
g.drawPolyline(px, py, 4);
}
}
}

如果您使用3D函式庫時,通常可以自行選擇使用哪一種頂點記錄方式,而且包裝成物件之後,您也無須親自處理索引的細節。