|
如果我們要繪製的圖形很多時,在動畫製作時,如果直接畫在前景上,可能會發生圖形一個一個顯現出來的畫面,讓整個畫面不完整呈現。
這時候可使用雙緩衝區(double-buffer)的方法,要準備兩個繪圖物件,其中一個是前景區,也就是您所看到的畫面,另一個繪圖物件您看不到,在
畫面準備好之前,所有的圖形先繪製在您看不到的畫面,當所有的圖形都繪製完畢,再將所有的圖形繪製到前景,此時前景會整個被後繪圖區所覆蓋,完成一次畫面
的播放,下圖為簡單的示意圖:

實作雙緩衝區的方法依所使用的API而有所不同,在Java中可以用Image物件來完成,而在使用DirectDraw時,可以使用
Surface來建立,下面這個程式是使用Java來製作,這個程式是至今所有文件內容驗收,包括了頂點配置、索引陣列、動畫、雙緩衝區等等,您可以先觀
看結果:一個繞Y軸旋轉的空心立方塊。
package onlyfun.caterpillar.graphics.animation; import javax.swing.JApplet; import java.awt.*; class Point3D { public double x, y, z; public Point3D() { x = y = z = 0; } public Point3D(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } } // 立方體類別 class Cubic { public int NFc; // 平面數量 public int NVt; // 頂點的數量 public int Nvt; // 1個平面的頂點數 public Point3D[] Vt; // 頂點的座標 public int[][] Ord; // 頂點索引 public Cubic(int r) { NFc = 6; NVt = 8; Nvt = 4; Vt = new Point3D[NVt]; Ord = new int[NFc][Nvt]; double sq3 = Math.sqrt(3.0); Vt[0] = new Point3D(r/sq3,r/sq3,r/sq3); Vt[1] = new Point3D(r/sq3,r/sq3,-r/sq3); Vt[2] = new Point3D(-r/sq3,r/sq3,-r/sq3); Vt[3] = new Point3D(-r/sq3,r/sq3,r/sq3); for(int i = 0; i < NVt/2; i++) Vt[NVt/2+i] = new Point3D( -Vt[i].x, -Vt[i].y, -Vt[i].z); int[][] ord={{0,1,2,3},{0,3,5,6},{0,6,7,1}, {4,7,6,5},{4,2,1,7},{4,5,3,2}}; for(int i = 0; i < NFc; i++) for(int j = 0; j < Nvt; j++) Ord[i][j]=ord[i][j]; } } public class DoubleBuffer extends JApplet implements Runnable { private Image OffScreen; private Graphics drawOffScreen; private int orgX, orgY; private Cubic cubic; public void init() { setBackground(Color.black); orgX = (int)(getSize().width / 2); orgY = (int)(getSize().height / 2); OffScreen = createImage(getSize().width, getSize().height); drawOffScreen = OffScreen.getGraphics(); cubic = new Cubic(100); } public void start() { (new Thread(this)).start(); } public void run() { int[] px = new int[4], py = new int[4]; Point3D[] tp = new Point3D[4]; drawOffScreen.setColor(Color.yellow); // 旋轉以斜角繪製圖形 double sinAx = Math.sin(Math.toRadians(30)); double cosAx = Math.cos(Math.toRadians(30)); double[] cosAys = new double[360]; double[] sinAys = new double[360]; for(int i = 0; i < 360; i++) { cosAys[i] = Math.cos(Math.toRadians(i)); sinAys[i] = Math.sin(Math.toRadians(i)); } int angle = 330; double sinAy = sinAys[angle]; double cosAy = cosAys[angle]; // 動畫迴圈 while(true) { drawOffScreen.clearRect(0, 0, getSize().width, getSize().height); for(int i = 0; i < cubic.NFc; i++) { for(int j = 0; j < cubic.Nvt; j++) { // 利用索引array取出正確的頂點 tp[j] = cubic.Vt[cubic.Ord[i][j]]; // 旋轉以斜角繪製圖形 px[j] = (int) (tp[j].x*cosAy + tp[j].z*sinAy); py[j] = (int) (tp[j].y*cosAx - (-tp[j].x*sinAy + tp[j].z*cosAy) * sinAx); px[j] = px[j] + orgX; py[j] = -py[j] + orgY; }
// 在緩衝區上縮圖 drawOffScreen.drawPolyline(px, py, 4); } // 暫停 20 毫秒 try { Thread.sleep(20); } catch(InterruptedException e) { } repaint(); // 重繪畫面 // 繞 y 軸轉動 angle = (angle+1) % 360; sinAy = sinAys[angle]; cosAy = cosAys[angle]; } } // 重新定義update() public void update(Graphics g) { paint(g); } public void paint(Graphics g) { // 將緩衝區畫面繪到前景 g.drawImage(OffScreen, 0, 0, this); } }
|