2008年3月20日 星期四

JOGL - Java與OpenGl(四)

準備實戰

當你熟悉了前面的例子以後,我們來畫一張漂亮的圖。

這就是你接下來的程式。請確保你輸入了所有的code到你的編輯器中。調校這些程式可以使你快速地明白它們的工作原理。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.*;

/**
* This is a basic JOGL app. Feel free to reuse this code or modify it.
* 這是個基礎的JOGL程序,你可以隨意重用該代碼或者修改它。
*/
public class SecondJoglApp extends JFrame {

    public static void main(String[] args) {
        final SecondJoglApp app = new SecondJoglApp();

        // show what we've done
        // 看一下我們做了什麼
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                app.setVisible(true);
            }
        });
    }

    public SecondJoglApp() {
        // set the JFrame title
        // 設置JFrame標題
        super("Second JOGL Application");

        // kill the process when the JFrame is closed
        // 當JFrame關閉的時候,結束程式
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // only three JOGL lines of code ... and here they are
        // 只有三行JOGL code ... 如下
        GLCapabilities glcaps = new GLCapabilities();
        GLCanvas glcanvas = new GLCanvas(glcaps);
        glcanvas.addGLEventListener(new SecondGLEventListener());

        // add the GLCanvas just like we would any Component
        // 像其它元件一樣把GLCanvas加入
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        // center the JFrame on the screen
        // 使JFrame顯示在銀幕中央
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize = frame.getSize();

        if (frameSize.width > screenSize.width)
            frameSize.width = screenSize.width;
        if (frameSize.height > screenSize.height)
            frameSize.height = screenSize.height;
        frame.setLocation((screenSize.width - frameSize.width) >> 1,
                (screenSize.height - frameSize.height) >> 1);
    }
}



請注意這個class相較於前一個class只有少許的修改、class名稱、frame名稱、以及GLEventListener名稱。希望你能夠讀一讀code中的註解,否則你會搞不清它要做什麼。

我們實做的GLEventListener介面有了改進,可以畫出比上次漂亮一些的圖來。

import javax.media.opengl.*;
import javax.media.opengl.glu.*;

/**
 * For our purposes only two of the GLEventListeners matter. Those would be init() and display(). 
 * 對於我們來說,GLEventListener中只有兩個方法有用。 它們是init()和display()。
 */
public class SimpleGLEventListener implements GLEventListener {

    /**
     * Take care of initialization here. 注意這兒,初始化。
     */
    public void init(GLAutoDrawable gld) {
        GL gl = gld.getGL();
        GLU glu = new GLU();

        gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

        gl.glViewport(0, 0, 500, 300);
        gl.glMatrixMode(GL.GL_PROJECTION);
        gl.glLoadIdentity();
        glu.gluOrtho2D(0.0, 500.0, 0.0, 300.0);
    }

    /**
     * Take care of drawing here. 注意這兒,繪圖的地方。
     */
    public void display(GLAutoDrawable drawable) {
        float red = 0.0f;
        float green = 0.0f;
        float blue = 0.0f;

        GL gl = drawable.getGL();

        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
        gl.glPointSize(5.0f);

        for (int i=0; i<50; i++){
            red -= .09f;
            green -= .12f;
            blue -= .15f;

            if (red < 0.15) red = 1.0f;
            if (green < 0.15) green = 1.0f;
            if (blue < 0.15) blue = 1.0f;
            gl.glColor3f(red, green, blue);
            gl.glBegin(GL.GL_POINTS);
            gl.glVertex2i((i*10), 150);
            gl.glEnd();
        }
    }

    public void reshape(GLDrawable drawable, int x, int y, int width, int height) {
    }

    public void displayChanged(GLDrawable drawable, boolean modeChanged,
            boolean deviceChanged) {
    }
}

以上就是我們第一個有趣的JOGL程式。下圖是輸出,有很多好看的顏色。


當你看到GLEventListener的實做時,可能會感到不知所措。如果你有用C寫OpenGL的經驗的話,你也許能猜出一些。如果你覺得茫然,別擔心,也不要煩惱我會叫你記住這些東西,至少現在還不用。本書接下來的篇幅將會對SecondGLEventListener作出解釋。現在,你只需要試著去猜。試著去修改code,產生兩行,或者一行斜的,而不是一行水平線;或是讓所有的點都變成藍色或紅色。享受一下,這就是你接下來學習JOGL的方式。

JOGL - Java與OpenGl(三)

一個好用的程式樣板

讓我們繼續來看兩個class,當你對JOGL感到有點頭昏腦脹時,你也許會發現這個程式樣板相當好用。我已經不止一次把它們當成樣板用了。你可以隨意地使用它們。

這個樣板由兩個class組成。第一個是如下所示的SimpleJoglApp,在簡短說明之後,第二個是SimpleGLEventListener。你必須建立兩個class來編譯樣板。主程式如下:

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.media.opengl.*;

/**
* This is a basic JOGL app. 
* Feel free to reuse this code or modify it.
* 這是個基本的JOGL程式,你可以隨意重用或修改它。
*/
public class SimpleJoglApp extends JFrame {
    public static void main(String[] args) {
        final SimpleJoglApp app = new SimpleJoglApp();

        // show what we've done
        // 看一下我們做了什麼
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                app.setVisible(true);
            }
        });
    }

    public SimpleJoglApp() {
        // set the JFrame title
        // 設置JFrame標題
        super("Simple JOGL Application");

        // kill the process when the JFrame is closed
        // 當JFrame關閉的時候,結束程式
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // only three JOGL lines of code ... and here they are
        // 只有三行JOGL code ... 如下
        GLCapabilities glcaps = new GLCapabilities();
        GLCanvas glcanvas = new GLCanvas(glcaps);
        glcanvas.addGLEventListener(new SimpleGLEventListener());

        // add the GLCanvas just like we would any Component
        // 像其它組件一樣把GLCanvas加入
        getContentPane().add(glcanvas, BorderLayout.CENTER);
        setSize(500, 300);

        // center the JFrame on the screen
        // 使JFrame顯示在螢幕中央
        centerWindow(this);
    }

    public void centerWindow(Component frame) {
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension frameSize = frame.getSize();

        if (frameSize.width > screenSize.width)
            frameSize.width = screenSize.width;
        if (frameSize.height > screenSize.height)
            frameSize.height = screenSize.height;

        frame.setLocation((screenSize.width - frameSize.width) >> 1,
        (screenSize.height - frameSize.height) >> 1);
    }
}



code就是這些。讓我們把注意力集中在第一個class中與JOGL相關的三行code上。首先:
這決定了我們的JOGL Library和JVM可以使用哪些OpenGL/圖形特性。

接著:

GLCanvas glcanvas = new GLCanvas(glcaps);

我們創建了可以在上面畫畫的GLCanvas。如果我們不想用AWT,改用Swing,則可以 new GLJPanel()。
注意我們把先前創建的GLCapabilities物件傳了進去,這使我們的GLDrawable(這裡指GLCanvas)適當建立。

最後,我們準備在GLCanvas上加入GLEventListener。

我們實現GLEventListener介面的class是SimpleGLEventListener。當它收到GLDrawable或 GLCanvas的呼叫時,它會注意所需要完成的繪圖工作。如你所見,我不打算在這個程式裡畫東西。下面是GLEventListener的code:

import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;

/**
 * For our purposes only two of the GLEventListeners matter. 
 * Those would be init() and display(). 
 * 為了達到我們的目的,GLEventListener中只有兩個方法有用。
 * 它們是init()和display()。
 */
public class SimpleGLEventListener implements GLEventListener {

    /**
     * Take care of initialization here. 注意這兒,我們初始化的地方。
     */
    public void init(GLAutoDrawable drawable) {
    }

    /**
     * Take care of drawing here. 
     * 注意這兒,繪圖的地方。
     */
    public void display(GLAutoDrawable drawable) {
    }

    /**
     * Called when the GLDrawable (GLCanvas or GLJPanel) has changed in size. 
     * We won't need this, but you may eventually need it -- just not yet.
     * 當GLDrawable(GLCanvas或GLJPanel)大小改變時被呼叫。 
     * 我們不需要它,但你可能最後會用到 — 雖然現在並不需要。
     */
    public void reshape(GLAutoDrawable drawable, int x, int y, 
    int width, int height) {
    }

    /**
     * If the display depth is changed while the program is running this method
     * is called. Nowadays this doesn't happen much, unless a programmer has his
     * program do it. 
     * 當程序運行中顯示設定被改變時,會調用此方法。 
     * 現在這種事發生得不多,除非我們在程式裡面刻意觸發此事。
     */
    public void displayChanged(GLAutoDrawable drawable, boolean modeChanged, boolean deviceChanged) {
    }
}



以上就是我們要完成的JOGL核心工作。注意下面的UML圖。SimpleJoglApp是一個JFrame。它包含了GLDrawable,實際上是一個GLCanvas(但別那樣叫它)。我們加入了 SimpleGLEventListener。SimpleGLEventListener實現了相對於GLCanvas的 GLEventListener介面,這樣當它想執行任何的OpenGL工作時,GLCanvas就可以知道。GLDrawables能自動執行,所以你確實得使你的GLEventListener最優化。



這個程式跑起來可能會根據你的作業系統而顯得有點亂七八糟。這是預料中事,因為你在這裡只是在銀幕顯示隨機的內容。所以恭喜你,現在具備基本了繪圖能力了。

JOGL - Java與OpenGl(二)


Hello World!


我是一個傳統的人,所以我們理所當然從「Hello World」開始。這個「Hello World」程式將檢驗我們的安裝是否全部或只有一部分安裝正確。回憶一下安裝JOGL的2個部分,分別是jar文件裡的Java Library以及其它的Native code。

以下就是我們的程式:
import javax.media.opengl.*;

public class HelloWorld {
    public static void main (String args[]) {
        try {
            System.loadLibrary("jogl");
            System.out.println("Hello World! (The native libraries are installed.)");
            GLCapabilities caps = new GLCapabilities();
            System.out.println("Hello JOGL! (The jar appears to be available.)");
       } catch (Exception e) {
            System.out.println(e);
       }
    }
}

首先,這個程式測試 Native code 和 Java library 是否已經安裝正確了。只有當jogl.jar和Native code(諸如gluegen-rt.dll或者 jogl.dll)兩者都安裝好了的時候,JOGL才算安裝完全。如果native code不可用,程式會拋出 java.lang.UnsatisfiedLinkError例外。如果classpath裡沒有安裝JAR,程序則根本編譯都過不了。Javac編譯器會報諸如此類的錯「javax.media.opengl Package不存在」。當這個程序編譯通過且運行起來沒有異常的話,你可以繼續學習JOGL了。

(按:已修改為相容JOGL 2008的code)

JOGL - Java與OpenGl(一)

在這篇文章裡,摘錄了《學習Java對於OpenGL的綁定》。作者Gene Davis解釋了如何開始用Java對於OpenGl的綁定開發圖形增強的程式。

這些年來,為了創建一個圖形增強的程式,從而出售給使用各種不同操作系統的用戶,程式員有一個選擇——OpenGL。GL代表圖形庫(graphics library)。OpenGL是SGI(美國圖形工作站生產廠商)的註冊商標。OpenGL顯示了它是一個跨平台的C語言編程API。但是事實上,在程式介面上,它是一個獨立於硬體的規格。

OpenGL用於繪圖,速度非常快。大多數場合下,它使用硬體加速。似乎OpenGL可以實現一切你想要完成的圖形界面。

不幸的是,OpenGL是為C語言而寫的。不得不承認,C語言不是用來編寫複雜應用程式的流行語言。關於OpenGL一個最大的缺點就是:如果你不創建一個視窗(用來把你的圖形放入其中),你就什麼都做不了。但是OpenGL沒有提供給你創建視窗的方法。這使得OpenGL對於初學者來說顯得比較難。

幸運地是,出現了GLUT (OpenGL Utility Toolkit)(OpenGL工具包)。它被用來輕鬆應對視窗、按鈕以及使用者事件。儘管如此,對於想要使用物件導向的程式員來說,學習用C或者C++來寫OpenGL程式仍然是一件痛苦的事。


然後出現了JOGL

Java 也許是最流行的真正物件導向程式語言。有許多用Java去結合OpenGL的嘗試,但是第一個被大家認可並注意的OpenGL的Java綁定 (Java Bindings for OpenGL), 或者稱為JOGL。因為它得到Sun(Java的創建者)和SGI(OpenGL的創建者)的支持。

如今,Sun的遊戲開發小組正在開發JOGL。它以肯·拉塞爾和克里斯·克蘭開發的Jungle開始。拉塞爾是Sun的員工,研發「HotSpot虛擬機器」,擁有多年的3D經驗。克蘭則研發「荒謬的遊戲」,對3D圖學也相當有經驗。

我個人對他們以及所有其它在JOGL上工作的人表示感謝。曾經有許多人嘗試通過友好的Java API來使用OpenGL——其中包括Java 3D, OpenGL for Java Technology (gl4java)(給Java技術的OpenGL),Lightweight Java Game Library (LWJGL)(輕量級的Java遊戲庫)。JOGL第一個使我感到滿意。

JOGL是由Sun支持的、OpenGL的Java class綁定。哇!這句話說得太妙了。

OpenGL 被用來展示3D模型。它強大、快速,而且可能是自Swing出現以來最棒的一樣東西。通過JOGL來使用OpenGL,你可以製作出很酷的遊戲或是模型位置什麼的,而在這之前創建它們需要非常昂貴的成本。有人寫了很厚很厚的書來描述OpenGL,當你熟悉了它們以後這些書會很有用,但現在不行。你必須學習 展現在你面前的OpenGL是如何使用Java API的。同樣你還得看一下關於javax.media.opengl的基礎介紹,可能還得複習一下數學知識。



獲取JOGL?

如果你想使用JOGL,你需要jogl.jar以及附帶的Native code。我希望有一天它可以成為Java的標準,但現在它只是一個夢想。

第一步要找到你的作業系統對應的壓縮檔,並進行解壓縮。我是在https://jogl.dev.java.net/上找到的。不同的作業系統有所區別,但需要安裝2個部分。系統的環境變數classpath裡 一定要有jogl.jargluegen-rt.jar。我們的第一個sample code特別用來測試環境是否安裝正確,所以對於測試安裝你不必緊張。
(按:這裡已經修改為2008新版JOGL安裝方法)


JOGL的Javadocs

同樣可以在和JOGL的發佈位置獲得Javadocs。Javadocs會命名成類似jogl-1.0-usrdoc.tar的名字。

如果你瀏覽一下Package:javax.media.opengl,你很快會注意到有些class非常大。GL便是一個顯著的例子。別被嚇跑了,你很快會發現只需一點點JOGL的知識,就可以完成一些相當複雜的事了。現在你需要掃視一下的class有:
*GLDrawable
*GLCanvas
*GLJPanel
*GLCapabilities
*GLDrawableFactory

這些是進入圖形世界基本介面。如果你還記得,前面我提到初學OpenGL的人有一個大麻煩,那就是缺乏標準的視窗系統。對於C語言,GLUT起了相當大作用。而我們則有Swing和AWT。很可能你已經用過AWT或者Swing了,所以你不會覺得自己從頭學起。這非常好。在很短的介紹,關於把JOGL組件放到屏幕上以後,我們不需要多長時間就可以跑一個相當酷而且流行的程式了。


GlueGen...幾乎和JOGL一樣酷?

你應該意識到,OpenGL是為了C程式員而寫的。這意味著 Java 要利用它,必須要用到本機介面。用不怎麼有趣的 JNI (Java本機介面) 進行連接。然而 OpenGL太大了,手寫所有的連接太費時。想稍微做出一點複雜的程式,有許多特別的特性,OpenGL則保持改進,那意味著得有相應的變化來 跟上OpenGL的步伐。簡而言之,對於任何試著寫與OpenGL保持同步,包含所有Java到本機介面的程式碼的嘗試,非常困難。

讓我們進入JOGL家族看看。他們打算利用C頭文件寫一些程式碼來實現一切 JNI 做的事。他們管這個叫做 GlueGen。GlueGen解析C頭文件然後魔法般地創建出Java和JNI代碼以便連接到本機函數庫。這意味著OpenGL的升級可以迅速地在JOGL裡體現。