、昔仕事でお世話になった人から依頼で、JavaでOpenGLを使った調査が必要になったので、メモ。
JavaでOpenGLを使う
ライブラリがいくつかあるらしいが、とりあえず今回はJOGLを使う。
ITPProの「OpenGLを使ってJavaでも3Dを楽しもう」を参考にしたが、記事が2006年と古いのでリンク切れがあったりして微妙。
まぁ、気を取り直して、JOGLのサイトに行って、ITProの記事を見ながらやってたけど、なんITか違う感じがしたので、Googlingで新しめの記事を探す。
- ma38su's blog JOGLことはじめ。(2012年の記事)
- 趣味の記録保存館 JOGLアプレットが動いたー!! (2011年の記事)
- OGL (Java OpenGL) - 3D アプレットの作成まで(2012年の記事)
とりあえず、JOGLことはじめ。を参考に環境を構築する。
- http://jogamp.org/deployment/jogamp-current/archive/からjogamp-all-platforms.7zをダウンロード
- ダウンロードしたファイルを解凍する。どこでもいいらしいけど、Javaなので、日本語や半角スペースを含むパスは避けた方がよいでしょう。
- Eclipseでプロジェクトを作って、クラスパス(ビルドパス)に解凍したフォルダの中から、gluegen.jarとjogl-all.jarを登録する。
- 解凍したフォルダの中のgluegen-rt-natives-xxxx.jarとjoal-natives-xxxx.jarを環境にあったものを選んで、その中のdllをEclipseで作ったプロジェクトにいれる。
あとは、ITProのソースを入力して、実行。
ちなみに、ITProのソースはJOGL2のソースではないので、若干の手直しが発生する。
手直ししたソースは以下の通り。
import java.awt.Frame;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.gl2.GLUT;
public class SimpleCube implements GLEventListener {
private GL gl;
private GL2 gl2;
private GLUT glut;
public SimpleCube() {
Frame frame = new Frame("Simple Cube");
// 3Dを描画するコンポーネント
GLCanvas canvas = new GLCanvas();
canvas.addGLEventListener(this);
frame.add(canvas);
frame.setSize(300, 300);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
public void init(GLAutoDrawable drawable) {
// 初期化処理
gl = drawable.getGL();
gl2 = gl.getGL2();
glut = new GLUT();
}
public void reshape(GLAutoDrawable drawable,
int x, int y,
int width, int height) {
// 描画領域変更処理
float ratio = (float)height / (float)width;
gl2.glViewport(0, 0, width, height);
gl2.glMatrixMode(GL2.GL_PROJECTION);
gl2.glLoadIdentity();
gl2.glFrustum(-1.0f, 1.0f, -ratio, ratio,
5.0f, 40.0f);
gl2.glMatrixMode(GL2.GL_MODELVIEW);
gl2.glLoadIdentity();
gl2.glTranslatef(0.0f, 0.0f, -20.0f);
}
public void display(GLAutoDrawable drawable) {
// 描画処理
gl2.glClear(GL.GL_COLOR_BUFFER_BIT);
// 大きさ 2 の線画の立方体を描画
glut.glutWireCube(2.0f);
}
public void displayChanged(GLAutoDrawable drawable,
boolean modeChanged,
boolean deviceChanged) {
}
public static void main(String[] args) {
new SimpleCube();
}
@Override
public void dispose(GLAutoDrawable arg0) {
}
}
手直しに参考にしたサイトは以下のところ
http://stackoverflow.com/questions/7606006/cant-use-glloadidentity-as-expected-after-a-translation-in-jogl
OpenGLで作った画像の保存
現在調査中だけど、以下のサイトを参考にしたらできそうな予感。
JOGLの情報ではないけど、OpenGLと同じメソッドが使えるという前提(でないと困る)
ちょこちょこっとやった感じではいけそうだけど、バッファの部分とかをどうしていいかわからない。
とりあえず、glReadPixels()当たりで取ってきたデータをつかってbmpやpng,jpgにできるのではないかと予想。
調べてみると、タイミングによっては真っ暗になるらしい。
で、やってみたのが以下のソース。
うまくかなかったりしたけど、とりあえず、保存できるようになった。が、色がおかしい・・・
import java.awt.Dimension;
import java.awt.Frame;
import java.awt.Insets;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.awt.image.DataBufferByte;
import java.awt.image.Raster;
import java.awt.image.SampleModel;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.nio.Buffer;
import java.nio.ByteBuffer;
import java.util.Calendar;
import javax.imageio.ImageIO;
import javax.media.opengl.GL;
import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;
import javax.media.opengl.GLEventListener;
import javax.media.opengl.awt.GLCanvas;
import com.jogamp.opengl.util.gl2.GLUT;
public class SimpleCube implements GLEventListener, KeyListener {
private GL gl;
private GL2 gl2;
private GLUT glut;
private boolean isSaveGL = false;
private GLCanvas canvas = null;
private Frame frame = null;
public SimpleCube() {
frame = new Frame("Simple Cube");
// 3Dを描画するコンポーネント
canvas = new GLCanvas();
canvas.addGLEventListener(this);
canvas.addKeyListener(this);
frame.add(canvas);
frame.setSize(300, 300);
frame.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
}
public void init(GLAutoDrawable drawable) {
// 初期化処理
gl = drawable.getGL();
gl2 = gl.getGL2();
glut = new GLUT();
}
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
int height) {
// 描画領域変更処理
float ratio = (float) height / (float) width;
// 透視投影(遠近投影法)の行列を指定する。
gl2.glViewport(0, 0, width, height);
// 射影行列を設定
gl2.glMatrixMode(GL2.GL_PROJECTION);
// 現在選択されている行列に単位行列をロードする。
gl2.glLoadIdentity();
// ビューボリューム(視体積)を指定します。
gl2.glFrustum(-1.0f, 1.0f, -ratio, ratio, 5.0f, 40.0f);
// モデルビューの行列を用いて作業する事を指定する。
gl2.glMatrixMode(GL2.GL_MODELVIEW);
// 現在選択されている行列に単位行列をロードする。
gl2.glLoadIdentity();
// 物体を平行移動をさせる
gl2.glTranslatef(0.0f, 0.0f, -20.0f);
// 物体を回転させる場合。
gl2.glRotatef(15, 1.0f, 0.0f, 0.0f);
}
public void display(GLAutoDrawable drawable) {
// 描画処理
gl2.glClear(GL.GL_COLOR_BUFFER_BIT);
// 大きさ 2 の線画の立方体を描画
// glut.glutWireCube(2.0f);
glut.glutWireTeapot(2.0f);
if (isSaveGL == true) {
try {
Calendar cal = Calendar.getInstance();
long mil = cal.getTimeInMillis();
Dimension dim = frame.getSize();
Insets insets = frame.getInsets();
int imgWidth = dim.width - (insets.left + insets.right);
int imgHeight = dim.height - (insets.top + insets.bottom);
saveGL("./test1_" + Long.toString(mil) + ".jpg", 300, 300);
saveGL("./test2_" + Long.toString(mil) + ".jpg", imgWidth, imgHeight);
System.out.println("OK");
} catch (Exception e) {
System.out.println("faild");
e.printStackTrace();
} finally {
isSaveGL = false;
}
}
}
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
boolean deviceChanged) {
}
public static void main(String[] args) {
new SimpleCube();
}
@Override
public void dispose(GLAutoDrawable arg0) {
}
/**
* http://d.hatena.ne.jp/npal_shared/20121107/1352284053 を参考に保存処理を作成 保存は
* http://www.geocities.jp/inu_poti/java/meida/image05.html
* http://www.metareal
* .org/2007/04/02/convert-awt-image-to-buffered-image-or-byte-array/ を参考
*
* @param filename
*/
public void saveGL(String filename, int imageWidth, int imageHeight) {
// RGBなら3, RGBAなら4
int channelNum = 4;
int allocSize = imageWidth * imageHeight * channelNum;
ByteBuffer byteBuffer = ByteBuffer.allocate(allocSize);
//gl2.glFlush();
// 読み取るOpneGLのバッファを指定 GL_FRONT:フロントバッファ GL_BACK:バックバッファ
gl2.glReadBuffer( GL2.GL_BACK );
// OpenGLで画面に描画されている内容をバッファに格納
gl2.glReadPixels(0, // 読み取る領域の左下隅のx座標
0, // 読み取る領域の左下隅のy座標
imageWidth, // 読み取る領域の幅
imageHeight, // 読み取る領域の高さ
GL2.GL_BGRA, // 取得したい色情報の形式
GL2.GL_UNSIGNED_BYTE, // 読み取ったデータを保存する配列の型
(Buffer) byteBuffer // ビットマップのピクセルデータ(実際にはバイト配列)へのポインタ
);
printGLError();
// glReadBufferで取得したデータ(ByteBuffer)をDataBufferに変換する
byte[] buff = byteBuffer.array();
// データがBGRAをABGR
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] abgrBuff = new byte[4];
try{
for(int y = imageHeight-1; y >= 0; y--){
for(int x = 0; x < imageWidth; x++){
// ここがおかしいので色もおかしい
// 0 1 2 3
// from B G R A
// to A B G R
int offset = imageWidth * y * channelNum;
// A
abgrBuff[0] = buff[x * channelNum + offset + 3];
// B
abgrBuff[1] = buff[x * channelNum + offset + 0];
// G
abgrBuff[2] = buff[x * channelNum + offset + 1];
// R
abgrBuff[3] = buff[x * channelNum + offset + 2];
baos.write(abgrBuff);
}
}
}catch(IOException ie){
return;
}
buff = baos.toByteArray();
DataBufferByte dataBuffer = new DataBufferByte(buff, buff.length);
// DtaaBuffereをつかって、BufferedImageを作成
BufferedImage imageBuffer = new BufferedImage(
imageWidth,
imageHeight,
BufferedImage.TYPE_4BYTE_ABGR);
// BufferedImageのSampleModelを取得
SampleModel sm = imageBuffer.getSampleModel();
// DataBuffereとSampleModelをつかってRasterを作成
Raster raster = Raster.createRaster(sm, dataBuffer, null);
// BufferedImageにRasterを設定
imageBuffer.setData(raster);
try {
// ImageIOクラスを使って、画像を保存
String[] fnSplit = filename.split("\\.");
String ext = fnSplit[fnSplit.length - 1].toLowerCase();
String fileType = "bmp";
if(ext.equals("jpg") || ext.equals("jpeg")){
fileType = "jpg";
}
else if(ext.equals("gif")){
fileType = "gif";
}
else if(ext.equals("bmp")){
fileType = "bmp";
}
ImageIO.write(imageBuffer, fileType, new File(filename));
} catch (IOException e1) {
e1.printStackTrace();
}
}
private int printGLError(){
int err = gl2.glGetError();
System.out.println(err);
return err;
}
@Override
public void keyPressed(KeyEvent ke) {
}
@Override
public void keyReleased(KeyEvent ke) {
switch (ke.getKeyCode()) {
case KeyEvent.VK_S:
isSaveGL = true;
// 再描画要求
canvas.display();
break;
}
}
@Override
public void keyTyped(KeyEvent arg0) {
}
}

(1.00 / 1)
(0.00 / 1)