Java

JOGL(Java APIs for OpenGL) 소개

_침묵_ 2007. 3. 9. 08:14

출처 :http://sdnkorea.com/blog/200

 

Java SE/중급2005/03/30 10:42

JOGL(Java APIs for Open GL)는 그 이름에서 알 수 있듯이 OpenGL 3D graphics API에 설정된 Java 프로그래밍 언어이다. JOGL은 Java로 작성된 애플리케이션에 하드웨어 지원 3D 그래픽을 제공하기 위해 만들어졌다. 이번 테크팁에서는 JOGL을 이용하여 애플리케이션에 기본적인 2차원과 3차원 그래픽을 포함시키는 방법에 대해서 설명한다. JOGL은 BSD(Berkeley Software Distribution) 라이센스 하에java.net에서 다운로드할 수 있다. JOGL이 표준 OpenGL API 전반에 걸쳐 간단한 Java wrapper로 되어있으므로 이번 테크팁의 예제에서는 OpenGL에 대한 기본 정보는 다루지 않겠다.

JOGL을 설치하기 위해,jogl documents and files페이지에서JOGL의 마지막 버전을 다운로드 받기 바란다. 이번 테크팁은 November 19, 2004 1.1b07 릴리즈 버전에 의해 테스트되었다. 두 세트의 파일들을 다운로드하고 인스톨해야하는데, Chris Adamson이 작성한Jumping into JOGL아티클에서 어떤 파일을 다운로드하고 어떻게 설치하는 지를 설명하고 있다. 이 때, 플랫폼 지정 파일이 jar 파일에 있음을 명심하기 바란다. Jar파일을 추가하고 사용자의 런타임이 찾을 수 있는 디렉토리에 플랫폼 지정 파일을 넣어야할 것이다.

JOGL의 설치 후에는 이를 사용하여 애플리케이션에 hardware-accelerated 3D 그래픽을 제공할 수 있다. 우선, JOGL을 사용하여 검은색 공간 안에서 회전하는 빨간색 사각형을 만들어보자. 예제 프로그램JOGLRotatingSquare은 이번 테크팁의 뒷부분에 나온다.JOGLRotatingSquare의 생성자는 애플리케이션을 설정하기 위한 단계들의 개요를 잡는다.

JOGLRotatingSquare() {     GLCanvas canvas = getGLCanvas();     canvas.addGLEventListener(new RotatingSquareListener());     Animator anim = new Animator(canvas);     addCanvasToFrame(canvas);     anim.start();    }

이 예제는getGLCanvas()메소드를 통해GLCanvas타입의 오브젝트를 얻는 것에서 시작한다. 그 후GLEventListener를 생성하여 오브젝트에 첨부하고, 장면의 애니메이션을 주관하는Animator를 생성하며, 프레임에GLCanvas오브젝트를 추가한다. 마지막으로, 애니메이션을 시작하게 된다.

getGLCanvas()메소드는GLCapabilities오브젝트를 컨피규어링하는 것에서 시작한다. 이 때 이 오브젝트는 랜더링 환경이 반드시 지원해야하는OpenGL성능을 지정한다. 예를 들어 다음은 hardware-accelerated가 랜더링 환경을 지원 하도록 요청하는 방법이다.

GLCapabilities capabilities = new GLCapabilities();   capabilities.setHardwareAccelerated(true);

그 후 메소드는GLCapabilities오브젝트를 팩토리에 전달하여GLCanvas를 얻는다.GLCanvasGLDrawable인터페이스를 구현하며, 이 인터페이스는 OpenGL 랜더링을 지원한다.GLCanvas오브젝트는 장면을 랜더링하는 데 사용한다.

private GLCanvas getGLCanvas() {     GLCapabilities capabilities = new GLCapabilities();     return GLDrawableFactory.getFactory().                           createGLCanvas(capabilities);   }

GLEventListener는 대부분의 작업을 수행한다.JOGLRotatingSquare에서는 리스너가 이너 클래스로써 구현된다. 리스너는 반드시init(),display(),displayChanged(),reshape()메소드를 구현해야만 한다.

init()메소드는OpenGL환경이 처음으로 초기화되는 즉시 drawable에 의해 호출된다.init()는 사용자의 기본 환경을 설정하는 곳이다. 예를 들어 사용자는 지우는 색과 그릴 색을 설정할 수 있다.JOGLRotatingSquare예제에서는 이 두 색을 모두 검은색으로 초기화해놓았다.

public void init(GLDrawable drawable) {      GL gl = drawable.getGL();      gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color      gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color    }

displayChanged()메소드는 디스플레이 모드 또는 디스플레이 디바이스가 변경될 때 호출된다.JOGLRotatingSquare예제에서는displayChanged()메소드의 본문이 비어있다.

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

reshape()메소드는 컴포넌트의 사이즈가 변경된 후 처음으로 다시 색을 입힐 때 호출된다. 이는 컴포넌트가 화면상에 처음 나타날 때도 해당된다.JOGLRotatingSquare예제에서reshape()메소드는 보일 수 있는 영역을 맞추고,Frame의 사이즈가 변경되었을 때 사각형의 중심을 다시 잡도록GLCanvas를 설정하는 데 이용한다.

public class VarGreeter3 {   public void reshape(GLDrawable drawable,                        int x,                        int y,                        int width,                        int height) {      GL gl = drawable.getGL();      gl.glViewport(0, 0, width, height);      gl.glMatrixMode(GL.GL_PROJECTION);      gl.glLoadIdentity();      gl.glOrtho(-width, width, -height, height, -1, 1);    }

display()메소드가OpenGL랜더링을 초기화하기 위해 호출된다. 이는 화면을 업데이트 할 때도Animator오브젝트에 의해 호출되는 메소드이기도 하다.

JOGLRotatingSquare프로그램에서display()메소드는 화면을 깨끗이 지우고, 사각형을 다시 그리고 현재와 같은 각도로 회전시킨다. 이는 시계 반대 방향으로 재빨리 회전시키게된다.

public void display(GLDrawable drawable) {      GL gl = drawable.getGL();      gl.glClear(GL.GL_COLOR_BUFFER_BIT);      drawRedCenteredSquare(gl);      angle++;      gl.glMatrixMode(GL.GL_MODELVIEW);      gl.glLoadIdentity();      gl.glRotatef(angle, 0, 0, 1);    }

glRotatef()메소드에 대한 매개변수는 각도와 회전이 이뤄지는 축을 나타내는 세 개의 다른 점이다. 이 예제에서는, 회전은 z축에 관한 건이다. 이는 x-y 평면에서의 시계 반대 방향 회전과 같은 것이다.

다음은JOGLRotatingSquare에 대한 전체 코드 리스트이다.

import net.java.games.jogl.GLCanvas;   import net.java.games.jogl.GLCapabilities;   import net.java.games.jogl.GLDrawableFactory;   import net.java.games.jogl.Animator;   import net.java.games.jogl.GL;   import net.java.games.jogl.GLEventListener;   import net.java.games.jogl.GLDrawable;      import java.awt.Frame;   import java.awt.event.WindowAdapter;   import java.awt.event.WindowEvent;      public class JOGLRotatingSquare {        private static float angle = 0;     private static final int SIZE = 160;        JOGLRotatingSquare() {       GLCanvas canvas = getGLCanvas();       canvas.addGLEventListener(new RotatingSquareListener());       Animator anim = new Animator(canvas);       addCanvasToFrame(canvas, anim);       anim.start();     }        private void addCanvasToFrame(      GLCanvas canvas, final Animator anim) {       Frame f = new Frame("JOGL Rotating Square");       f.setSize(600, 400);       f.add(canvas);       f.setVisible(true);       f.addWindowListener(new WindowAdapter() {           public void windowClosing(WindowEvent e) {             anim.stop();             System.exit(0);           }         });     }        private GLCanvas getGLCanvas() {       GLCapabilities capabilities = new GLCapabilities();       return GLDrawableFactory.getFactory().                             createGLCanvas(capabilities);     }      public static void main(String[] args) {       new JOGLRotatingSquare();     }      private void drawRedCenteredSquare(GL gl) {       gl.glColor3f(1, 0, 0);       gl.glRecti(-SIZE / 2, -SIZE / 2, SIZE / 2, SIZE / 2);       gl.glColor3f(0.0f, 0.0f, 0.0f);     }        class RotatingSquareListener implements GLEventListener {          public void init(GLDrawable drawable) {         GL gl = drawable.getGL();         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color         gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color       }          public void display(GLDrawable drawable) {         GL gl = drawable.getGL();         gl.glClear(GL.GL_COLOR_BUFFER_BIT);         drawRedCenteredSquare(gl);         angle++;         gl.glMatrixMode(GL.GL_MODELVIEW);         gl.glLoadIdentity();         gl.glRotatef(angle, 0, 0, 1);       }          public void reshape(GLDrawable drawable,                           int x,                           int y,                           int width,                           int height) {         GL gl = drawable.getGL();         gl.glViewport(0, 0, width, height);         gl.glMatrixMode(GL.GL_PROJECTION);         gl.glLoadIdentity();         gl.glOrtho(-width, width, -height, height, -1, 1);       }          public void displayChanged(GLDrawable drawable,                                  boolean modeChanged,                                  boolean deviceChanged) {       }     }   }

JOGLRotatingSquare을 컴파일하고 구동하자. 검은색 영역 안에서 돌고 있는 빨간 색의 사각형이 보이게 될 것이다.

사용자 삽입 이미지

Frame의 크기를 키우면, 사각형이 중간 지점을 다시 찾고, 천천히 애니메이션 될 것이다display()메소드의 마지막 라인을 다름과 같이 변경하여 더욱 천천히 애니메이션되도록 만들 수도 있다.

gl.glRotatef(angle/16, 0, 0, 1);

16 대신 다른 값으로 구현할 수도 있다.

drawRedCenteredSquare()메소드는 특정 메소드들의 집합 내의 메소드 하나를 이용하여 직사각형을 그린다. 첫번째로 현재 색이 빨간색이라고 지정하며, 그 후SIZE와 동일한 길이의 변을 가지는 직사각형을 원본의 중앙에 그린다. 마지막으로 현재 색을 검은색으로 재설정한다.

private void drawRedCenteredSquare(GL gl) {     gl.glColor3f(1, 0, 0);     gl.glRecti(-SIZE / 2, -SIZE / 2, SIZE / 2, SIZE / 2);     gl.glColor3f(0.0f, 0.0f, 0.0f);   }

이는 사각형이 x-y 평면에 그려지기 때문에 가능하다. 다시 말해, 이 메소드는 3차원을 고려하지 않는다. 직사각형을 그리는 또다른 방법은 다음과 같다.

private static void drawRedCenteredSquare(GL gl) {       gl.glColor3f(1, 0, 0);       gl.glBegin(GL.GL_QUADS);       gl.glTexCoord2f(0, 0);       gl.glVertex3f(-SIZE / 2, SIZE / 2, 0);       gl.glTexCoord2f(0, 1);       gl.glVertex3f(-SIZE / 2, -SIZE / 2, 0);       gl.glTexCoord2f(1, 1);       gl.glVertex3f(SIZE / 2, -SIZE / 2, 0);       gl.glTexCoord2f(1, 0);       gl.glVertex3f(SIZE / 2, SIZE / 2, 0);       gl.glEnd();       gl.glColor3f(0.0f, 0.0f, 0.0f);     }

여기 이 사각형의 꼭지점들은 정확히 세트가 된다. 이 방법은 이전 방법보다 좀 장황한 면이 있지만, 2차원에서 3차원으로 넘어가는 다음의 예제에서는 유용할 것이다. 3차원의 경우도 2차원의 경우와 많이 다른 것은 없다. 첫째로,reshape()메소드의 마지막 라인을 변경하여z축에서 보이는 영역이 전체 정육면체를 포함하도록 늘어나게한다. 이 단계를 생략할 경우, 단지 x-y 축의 한 단위 상에 있는 정육면체의 가로면 밖에 볼 수 없을 것이다. 다음은 수정된 라인이다.

gl.glOrtho(-width, width, -height, height, -SIZE, SIZE);

다음으로, 정육면체를 기울이기 위해display()메소드의 마지막을 변경하여 꼭지점이 정면을 향하도록 한다. 그 후 정육면체를 비스듬히 회전시킨다. 다음은display()메소드 마지막 라인을 변경한 것이다.

gl.glRotatef(-80, 1, 1, 0);      gl.glRotatef(angle /16, 1, -1, 1);      drawCenteredCube(gl);

마지막으로, 위에서 묘사한 사각형을 그리는 메소드를 변경해야할 것이다. 새로운 메소드로 정육면체에 가장 가까운 면의 사각형을 그린다.

private void drawSquareFace(GL gl) {     gl.glBegin(GL.GL_QUADS);     gl.glTexCoord2f(0, 0);     gl.glVertex3f(-SIZE / 2, -SIZE / 2, SIZE / 2);     gl.glTexCoord2f(0, 1);     gl.glVertex3f(-SIZE / 2, SIZE / 2, SIZE / 2);     gl.glTexCoord2f(1, 1);     gl.glVertex3f(SIZE / 2, SIZE / 2, SIZE / 2);     gl.glTexCoord2f(1, 0);     gl.glVertex3f(SIZE / 2, -SIZE / 2, SIZE / 2);     gl.glEnd();   }

3차원 버전에 추가되는 것은 다음의drawCenteredCube()메소드이다. 이 예제에서는 세면만이 보이므로 실제로는 정육면체의 세 면만을 그린다.

private void drawCenteredCube(GL gl) {    gl.glColor4f(1, 0, 0, 0);    drawSquareFace(gl);    gl.glColor4f(1, 1, 0, 0);    gl.glRotatef(90, 1, 0, 0);    drawSquareFace(gl);    gl.glColor4f(0, 0, 1, 0);    gl.glRotatef(90, 0, 1, 0);    drawSquareFace(gl);    gl.glColor3f(0.0f, 0.0f, 0.0f);  }

첫번째로 빨간색 한면이 그려진다. 그 후 90도로 회전된다. 노란색으로 색이 변경되어 두번째 면이 그려진다. 반대쪽으로 90도 회전하여 세 면이 한 점에서 만날 수 있게 한다. 파란색으로 변경되어 세번째 면이 그려진다.

다음은 전체 코드의 리스트이다.

import net.java.games.jogl.GLCanvas;   import net.java.games.jogl.GLCapabilities;   import net.java.games.jogl.GLDrawableFactory;   import net.java.games.jogl.Animator;   import net.java.games.jogl.GL;   import net.java.games.jogl.GLEventListener;   import net.java.games.jogl.GLDrawable;      import java.awt.Frame;   import java.awt.event.WindowAdapter;   import java.awt.event.WindowEvent;      public class JOGLRotatingCube {        private static final int SIZE = 160;        private static float angle = 0;        JOGLRotatingCube() {       GLCanvas canvas = getGLCanvas();       canvas.addGLEventListener(new RotatingCubeListener());       Animator anim = new Animator(canvas);       addCanvasToFrame(canvas, anim);       anim.start();     }        private void addCanvasToFrame(      GLCanvas canvas, final Animator anim) {       Frame f = new Frame("JOGL Rotating Half - Cube");       f.setSize(600, 400);       f.add(canvas);       f.setVisible(true);       f.addWindowListener(new WindowAdapter() {           public void windowClosing(WindowEvent e) {             anim.stop();             System.exit(0);           }         });     }        private GLCanvas getGLCanvas() {       GLCapabilities capabilities = new GLCapabilities();       return GLDrawableFactory.getFactory().                             createGLCanvas(capabilities);     }        public static void main(String[] args) {       new JOGLRotatingCube();     }           private void drawCenteredCube(GL gl) {       gl.glColor4f(1, 0, 0, 0);       drawSquareFace(gl);       gl.glColor4f(1, 1, 0, 0);       gl.glRotatef(90, 1, 0, 0);       drawSquareFace(gl);       gl.glColor4f(0, 0, 1, 0);       gl.glRotatef(90, 0, 1, 0);       drawSquareFace(gl);       gl.glColor3f(0.0f, 0.0f, 0.0f);     }        private void drawSquareFace(GL gl) {       gl.glBegin(GL.GL_QUADS);       gl.glTexCoord2f(0, 0);       gl.glVertex3f(-SIZE / 2, -SIZE / 2, SIZE / 2);       gl.glTexCoord2f(0, 1);       gl.glVertex3f(-SIZE / 2, SIZE / 2, SIZE / 2);       gl.glTexCoord2f(1, 1);       gl.glVertex3f(SIZE / 2, SIZE / 2, SIZE / 2);       gl.glTexCoord2f(1, 0);       gl.glVertex3f(SIZE / 2, -SIZE / 2, SIZE / 2);       gl.glEnd();     }        class RotatingCubeListener implements GLEventListener {          public void init(GLDrawable drawable) {         GL gl = drawable.getGL();         gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); //erasing color         gl.glColor3f(0.0f, 0.0f, 0.0f); // drawing color       }          public void display(GLDrawable drawable) {         GL gl = drawable.getGL();         gl.glClear(GL.GL_COLOR_BUFFER_BIT);            angle++;         gl.glMatrixMode(GL.GL_MODELVIEW);         gl.glLoadIdentity();         gl.glRotatef(-80, 1, 1, 0);         gl.glRotatef(angle /16, 1, -1, 1);         drawCenteredCube(gl);       }          public void reshape(GLDrawable drawable,                           int x,                           int y,                           int width,                           int height) {         GL gl = drawable.getGL();         gl.glViewport(0, 0, width, height);         gl.glMatrixMode(GL.GL_PROJECTION);         gl.glLoadIdentity();         gl.glOrtho(-width, width, -height, height, -SIZE, SIZE);       }          public void displayChanged(GLDrawable drawable,                                  boolean modeChanged,                                  boolean deviceChanged) {       }     }   }

JOGLRotatingCube를 컴파일하고 구동한다. 여러색으로 이루어진, 회전되는 정육면체를 보게 될 것이다.

사용자 삽입 이미지

이 예제에서 JOGL의 시작에서 구동까지 얼마나 짧게 할 수 있는지 알아보았다. 첫번째 예제에서GLCanvas에 직사각형을 추가하여 움직일 수 있게 하였다. 두번째 예제에서, 이를 확장하여 세 면을 가진 정육면체를 생성하고 공통의 꼭지점으로 회전시켰다. 사용자가 직접 정육면체의 다른 면들을 추가하거나 다른 모양들을 2차원 예제에 추가시켜보는 등 이 예제들을 확장해볼 수 있을 것이다.