-1-
GUI プログラミング第 4 回演習 3DChemical
~ OpenGL(JOGL)による3D 描画:水の化学式モデルを書いてみる ~ ↑実際は背景は黒くする <Eclipse を起動する> 1.eclipse.zip を D:ドライブにコピーし、右クリック→ここに解凍 2.workspace を S:ドライブから D:ドライブにコピー 3.eclipse.exe を起動 4.workspace を D:workspace に設定 <ステップ> ステップ1-1 O 原子となる球を1つ描画 ステップ1-2 水の化学式モデルを描画 ステップ2 マウスで回転できるようにする ステップ3 描画した立体の 2D 上の表示位置を取得し、元素名を 2D 描画する 104.45°-2-
【
3D 描画 Step1-1】
まず、O 原子となる球を1つ描画する <WaterPanel.java の作成> 1.Chemical プロジェクトをインポート ・共有フォルダからChemical.zip をコピーして workspace 内にはりつけ ・パッケージ・エクスプローラで右クリック→インポート→一般 →既存のプロジェクトをワークスペースへ→「次へ」 ・アーカイブ・ファイルの参照からD:workspace¥Chemical.zip を選択して開く ・「完了」ボタンを押下 2.パッケージを作成 src フォルダを選択→右クリック→新規→パッケージ パッケージ名water 3.WaterPanel クラスを作成…GLJPanel を継承(extends)、GLEventListener, MouseListener, MouseMotionListener を実装 (implements)したクラス water パッケージを選択→右クリック→新規→クラス ・クラス名WaterPanel ・スーパークラスの参照をクリック→「GLJPanel」とうつと、javax.media.opengl の GLJPanel が出てくるので、それを選択してOK ・インターフェースの追加をクリック →「GLEventListener」で出てくるクラスを選択して「追加」 →「MouseListener」で出てくるクラスを選択して「追加」 →「MouseMotionListener」で出てくるクラスを選択して「追加」・「OK」 ・「完了」ボタンを押下 4.ソースを編集(次ページ以降を参照)
変数→コンストラクタ→init メソッド→reshape メソッド→display メソッドの順に記述すると理解 しやすい。
WaterPanel.java -3- package chemical.water; 1 2 import java.awt.event.MouseEvent; 3 import java.awt.event.MouseListener; 4 import java.awt.event.MouseMotionListener; 5 6 import javax.media.opengl.GL; 7 import javax.media.opengl.GLAutoDrawable; 8 import javax.media.opengl.GLEventListener; 9 import javax.media.opengl.GLJPanel; 10 import javax.media.opengl.glu.GLU; 11 12 import com.sun.opengl.util.GLUT; 13 14
public class WaterPanel extends GLJPanel implements GLEventListener,
15 MouseListener, MouseMotionListener { 16 17 private GL gl; // OpenGL の関数群をもつオブジェクトその1 18
private GLU glu; // OpenGL の関数群をもつオブジェクトその2
19
private GLUT glut; // OpenGL の関数群をもつオブジェクトその3
20
private int div = 20; // 球、円柱の分割数
21 22
// 軸と O-H のなす角(※水の O-H がなす角は 104.45 度)
23
private float degree = (180-104.45f)/2.0f;
24 25 /* 光 ---*/ 26 // 光の位置 {x 座標, y 座標, z 座標, 光源までの距離(0 は無限円)} 27
private float[] lPosition = { -10.0f, 10.0f, 10.0f, 0.0f };
28
// 光の色 {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで
29
private float[] lSpecular = { 0.8f, 0.8f, 0.8f, 1.0f }; // 鏡面
30
private float[] lDiffuse = { 0.8f, 0.8f, 0.8f, 1.0f }; // 拡散
31
private float[] lAmbient = { 0.4f, 0.4f, 0.4f, 1.0f }; // 環境
32 33 /* 物体の反射率 ---*/ 34 // {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで 35
private float[] mSpecular = { 0.3f, 0.3f, 0.3f, 1.0f }; // 鏡面
36
private float[] mDiffuse = { 0.2f, 0.2f, 0.2f, 1.0f }; // 拡散
37
// 鏡面係数:きらめきの度合い(0~128)
38
private float mShininess = 10.0f;
39 40 /* 色の定義 ---*/ 41 // {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで 42
private float[] blue = { 0.0f, 0.0f, 1.0f, 1.0f }; // 青
43
private float[] red = { 1.0f, 0.0f, 0.0f, 1.0f }; // 赤
44
private float[] green = {0.0f, 1.0f, 0.5f, 1.0f }; // 緑
45 46 47 /** 48 * コンストラクタ 49 */ 50 public WaterPanel() { 51 // MouseEvent を受け取れるようにする 52 addMouseListener(this); 53 // MouseMotionEvent を受け取れるようにする 54 addMouseMotionListener(this); 55 // 3D 描画できるようにリスナ登録 56 addGLEventListener(this); 57 } 58 59 60 /* 3D 描画 ************************************************************/ 61 62 /** 63 * GLEventListener のメソッド:3D 描画メソッド 64
WaterPanel.java -4- */ 65 @Override 66
public void display(GLAutoDrawable drawable) {
67 // 背景色で塗りつぶし 68 gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); 69 // 現時点のマトリクス(Modelview 行列)を保存 ※必ず glPopMatrix()と対で使用する 70 /* glPushMatrix と glPopMatrix()は、座標を回転したり平行移動したり 71 * したものを、する前の状態を覚えておくために使う。 72 * これを使わないと、回転などがどんどん重なっていってしまう。 */ 73 gl.glPushMatrix(); 74 // z軸上 15 の位置から原点を見る,上方向は y 軸 75
// glulookat(視点の位置 x,y,z, 視点から見る座標 x,y,z, 上方向ベクトル x,y,z)
76 glu.gluLookAt(0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 77 // y 軸正方向に少しずらす(平行移動) 78 gl.glTranslatef(0.0f, 0.4f, 0.0f); 79 80 /* O 原子を原点に描画 ---*/ 81 // 色指定 ※環境光の反射率とは、つまり物体の色になる 82
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, red, 0);
83
// 塗りつぶしの球を描画
84
// glutSolidSphere(半径, Z 軸まわりの分割数, Z 軸に沿った分割数)
85
glut.glutSolidSphere(0.3, div, div);
86 87 // glPushMatrix()を呼ぶ前の行列の状態に戻す 88 gl.glPopMatrix(); 89 } 90 91 /** 92 * GLEventListener のメソッド:表示の切り替えが発生した場合に呼ばれる 93 * 今回は特に記述することはない 94 */ 95 @Override 96
public void displayChanged(GLAutoDrawable drawable, boolean modeChanged,
97 boolean deviceChanged) { 98 } 99 100 /** 101 * GLEventListener のメソッド:初期化時に呼ばれる 102 */ 103 @Override 104
public void init(GLAutoDrawable drawable) {
105
// OpenGL の関数群をもつオブジェクトを取得
106
gl = drawable.getGL();
107
glu = new GLU();
108
glut = new GLUT();
109 // 背景色を黒に設定 110 gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 111 // 影に隠れて見えないものは表示しない 112 gl.glEnable(GL.GL_DEPTH_TEST); 113 // 光を有効にする 114 gl.glEnable(GL.GL_LIGHTING); 115 gl.glEnable(GL.GL_LIGHT0); 116 // 光の反射の法線ベクトルを正規化 117 gl.glEnable(GL.GL_NORMALIZE); 118 // 光の位置を設定 119 /* 4 番目の数字はオフセット。 120 * JOGL は c++のソースを JAVA に自動変換している。 121 * c++では配列はポインタなので、オフセットの概念がある 122 * JAVA にポインタはないので引数が増やされているのだが、 123 * オフセットの数字は 0 でよい。 */ 124
gl.glLightfv(GL.GL_LIGHT0, GL.GL_POSITION, lPosition, 0);
125
// 光の色を設定
126
gl.glLightfv(GL.GL_LIGHT0, GL.GL_SPECULAR, lSpecular, 0); // 鏡面
127
gl.glLightfv(GL.GL_LIGHT0, GL.GL_DIFFUSE, lDiffuse, 0); // 拡散
WaterPanel.java
-5-
gl.glLightfv(GL.GL_LIGHT0, GL.GL_AMBIENT, lAmbient, 0); // 環境
129
// 物体の反射率を設定
130
gl.glMaterialfv(GL.GL_FRONT, GL.GL_SPECULAR, mSpecular, 0); // 鏡面
131
gl.glMaterialfv(GL.GL_FRONT, GL.GL_DIFFUSE, mDiffuse, 0); // 拡散
132
gl.glMaterialf(GL.GL_FRONT, GL.GL_SHININESS, mShininess); // 鏡面係数
133 } 134 135 /** 136 * GLEventListener のメソッド:表示領域が変更されたときに呼ばれる 137 */ 138 @Override 139
public void reshape(GLAutoDrawable drawable, int x, int y, int width,
140
int height) {
141
// ビューポート(描画領域)を設定
142
gl.glViewport(0, 0, width, height);
143 // マトリクスとして Projection 行列を対象にする 144 gl.glMatrixMode(GL.GL_PROJECTION); 145 // マトリクスを単位行列で初期化 146 gl.glLoadIdentity(); 147 // 視点の設定(参考資料を参照) 148
// gluPerspactive(視野角, 縦横比, near, far);
149 glu.gluPerspective(10.0, (double)width/(double)height, 1.0, 100.0); 150 // マトリクスとして ModelView 行列を対象にする 151 gl.glMatrixMode(GL.GL_MODELVIEW); 152 } 153 154 /* MouseListener のメソッド **************************************************/ 155 156 @Override 157
public void mouseClicked(MouseEvent e) {
158
// TODO Auto-generated method stub
159 160 } 161 162 @Override 163
public void mouseEntered(MouseEvent e) {
164
// TODO Auto-generated method stub
165 166 } 167 168 @Override 169
public void mouseExited(MouseEvent e) {
170
// TODO Auto-generated method stub
171 172 } 173 174 @Override 175
public void mousePressed(MouseEvent e) {
176
// TODO Auto-generated method stub
177 } 178 179 @Override 180
public void mouseReleased(MouseEvent e) {
181
// TODO Auto-generated method stub
182 } 183 184 /* MouseMotionListener のメソッド *************************************************/ 185 186 @Override 187
public void mouseDragged(MouseEvent e) {
188
// TODO Auto-generated method stub
189 190
}
191 192
WaterPanel.java
-6-
@Override
193
public void mouseMoved(MouseEvent e) {
194
// TODO Auto-generated method stub
195 196 } 197 198 } 199 <WaterFrame.java の作成> 1.フレームを作成 chemical パッケージ選択→右クリック→新規→その他を選択 GUI Forms→Swing→JFrame を選んで「次へ」
Class Name は WaterFrame として「完了」 2.Jigloo のメッセージが出てきたら「OK」 3.Look&Feel(プログラムの見た目)を設定 フレームを選択→右クリック→Set Look&Feel→Windows を選択 4.レイアウトを設定 フレームを選択→右クリック→Set Layout→BorderLayout を選択 5.フレームのサイズを大きめにする ソースの方をみて、initGUI()の中の下の方にあるサイズ指定部分を編集 setSize(400,300); → setSize(400,400); 6.フレームのプレビューに戻り、パネルをSouth に追加 上部の「Containers」右から 5 番目の「JPanel」を選び、フレームに貼り付ける 名前はsouthPanel、Constraints の中の direction に South を選択
パネルの縦幅はボタンがひとつ入る位にしておく。 7.WaterPanel を Center に追加
フレームを選択(中央をクリック)→右クリック→Add…→Add Custom →Add custom class or layout を選択
「WaterPanel」と入力して OK
名前をcenterPanel とし、Constraints の中の direction が Center になっていることを確認して OK 8.southPanel にボタンを追加
Step2 以降で使うのもだが、ここで追加しておく。
Components の一番左にある「JButton」を選んで southPanel に貼り付け 名前「resetButton」、テキスト「元に戻す」
9.ボタンが押されたときに実行されるメソッドのひな型を作成
resetButton を選択し、Event Name のリスト中の ActionListner を開いて actionPerformed を inline に設定する
10. 実行する 11. 描画を確認
-7-
【
3D 描画 Step1-2】
水の化学式モデルを描画する <円柱の描画・ポリゴンについて> OpenGL において例えば立方体を描画したい場合、 glut.glutWireCube(2.0f); のように関数を1つ利用すれば描画できる。このようにあらかじめ用意されている立体は、 ≪立方体・4 面体・8 面体・12 面体・20 面体・円錐・球・ドーナツ型・ティーポット≫ である。この中に円柱は含まれていないので、「ポリゴン」で表現する。 ポリゴンとは、多角形のことで、複雑なモデルを構成するために使う。CG では普通 3 角形と 4 角形を 要素として組み合わせて物体を表現する。どのような曲面も、小さいポリゴンに分割することで擬似的 に曲面として描画できる。 ポリゴンでつくられた曲面の例 今回は、原子を結ぶ線を円柱で表現する。この円柱は、円柱を縦に小さく分割するような四角形を連 続描画することで疑似的に表現する。 ポリゴンを使ったOpenGL の実際の描画は gl.glBegin(モード) ・・・ gl.glEnd() の構文で書く。主なモードは次の表のようになる。 モード 意味 GL.GL_POLYGON 凸多角形 (頂点を右回りで指定) GL.GL_LINE_STRIP 折れ線 (頂点を順次指定) GL.GL_TRIANGLE_STRIP 隣接した 3 角形 GL.GL_QUAD_STRIP 隣接した 4 角形 GL.GL_TRIANGLE_FAN 隣接した 3 角形 (扇形) gl.glBegin と gl.glEnd()の間で glVertex3f(x1, y1, z1); を何度もよびだし、頂点を順に指定していく。各モードごとの頂点の指定順は次ページの表のとおり。-8- 頂点指定の順番 GL_TRIANGLE_STRIP GL_QUAD_STRIP GL_TRIANGLE_FAN <作成手順> WaterPanel.java に必要部分を書きたす
-9- package chemical.water; 1 2 import java.awt.event.MouseEvent; 3 import java.awt.event.MouseListener; 4 import java.awt.event.MouseMotionListener; 5 6 import javax.media.opengl.GL; 7 import javax.media.opengl.GLAutoDrawable; 8 import javax.media.opengl.GLEventListener; 9 import javax.media.opengl.GLJPanel; 10 import javax.media.opengl.glu.GLU; 11 12 import com.sun.opengl.util.GLUT; 13 14
public class WaterPanel extends GLJPanel implements GLEventListener,
15 MouseListener, MouseMotionListener { 16 17 private GL gl; // OpenGL の関数群をもつオブジェクトその1 18
private GLU glu; // OpenGL の関数群をもつオブジェクトその2
19
private GLUT glut; // OpenGL の関数群をもつオブジェクトその3
20
private int div = 20; // 球、円柱の分割数
21 22
// 軸と O-H のなす角(※水の O-H がなす角は 104.45 度)
23
private float degree = (180-104.45f)/2.0f;
24 25 /* 光 ---*/ 26 // 光の位置 {x 座標, y 座標, z 座標, 光源までの距離(0 は無限円)} 27
private float[] lPosition = { -10.0f, 10.0f, 10.0f, 0.0f };
28
// 光の色 {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで
29
private float[] lSpecular = { 0.8f, 0.8f, 0.8f, 1.0f }; // 鏡面
30
private float[] lDiffuse = { 0.8f, 0.8f, 0.8f, 1.0f }; // 拡散
31
private float[] lAmbient = { 0.4f, 0.4f, 0.4f, 1.0f }; // 環境
32 33 /* 物体の反射率 ---*/ 34 // {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで 35
private float[] mSpecular = { 0.3f, 0.3f, 0.3f, 1.0f }; // 鏡面
36
private float[] mDiffuse = { 0.2f, 0.2f, 0.2f, 1.0f }; // 拡散
37
// 鏡面係数:きらめきの度合い(0~128)
38
private float mShininess = 10.0f;
39 40 /* 色の定義 ---*/ 41 // {R, G, B, アルファ(透明度)} ※数値は 0 から 1 まで 42
private float[] blue = { 0.0f, 0.0f, 1.0f, 1.0f }; // 青
43
private float[] red = { 1.0f, 0.0f, 0.0f, 1.0f }; // 赤
44
private float[] green = {0.0f, 1.0f, 0.5f, 1.0f }; // 緑
45 46 47 /** 48 * コンストラクタ 49 */ 50 public WaterPanel() { 51 // MouseEvent を受け取れるようにする 52 addMouseListener(this); 53 // MouseMotionEvent を受け取れるようにする 54 addMouseMotionListener(this); 55 // 3D 描画できるようにリスナ登録 56 addGLEventListener(this); 57 } 58 59 60 /* 3D 描画 ************************************************************/ 61 62 /** 63 * x 軸周りに円柱を描画 64
-10-
* @param r 円柱の半径
65
* @param length 円柱の長さ
66
* @param offset offset ≦x≦offset+length に描かれる
67
*/
68
public void drawCylinder(float r, float length, float offset) {
69 double t; 70 // 要素として連続する四角形を使う 71 gl.glBegin(GL.GL_QUAD_STRIP); 72
for (int i=0; i<=div; i++) {
73 // 角度を計算(ラジアン) 74 t = 2.0 * Math.PI * i / div; 75 // 光の反射の法線ベクトル 76
gl.glNormal3f(0, (float)Math.cos(t), (float)Math.sin(t));
77
// 頂点を設定
78
gl.glVertex3f(offset, (float)(r*Math.cos(t)), (float)(r*Math.sin(t)));
79
gl.glVertex3f(offset+length, (float)(r*Math.cos(t)), (float)(r*Math.sin(t)));
80 } 81 gl.glEnd(); 82 } 83 84 /** 85 * GLEventListener のメソッド:3D 描画メソッド 86 */ 87 @Override 88
public void display(GLAutoDrawable drawable) {
89 // 背景色で塗りつぶし 90 gl.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT); 91 // 現時点のマトリクス(Modelview 行列)を保存 ※必ず glPopMatrix()と対で使用する 92 /* glPushMatrix と glPopMatrix()は、座標を回転したり平行移動したり 93 * したものを、する前の状態を覚えておくために使う。 94 * これを使わないと、回転などがどんどん重なっていってしまう。 */ 95 gl.glPushMatrix(); 96 // z軸上 15 の位置から原点を見る,上方向は y 軸 97
// glulookat(視点の位置 x,y,z, 視点から見る座標 x,y,z, 上方向ベクトル x,y,z)
98 glu.gluLookAt(0.0, 0.0, 15.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); 99 // y 軸正方向に少しずらす(平行移動) 100 gl.glTranslatef(0.0f, 0.4f, 0.0f); 101 102 /* 棒 1 を描画(z 軸周りに-degree 度回転)---*/ 103 // ModelView 行列保存 104 gl.glPushMatrix(); 105 // 色指定 ※環境光の反射率とは、つまり物体の色になる 106
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, green, 0);
107 // 座標軸を z 軸周りに-degree 度回転 108 // ※2,3,4 番目の引数は回転軸を表す(順に x, y, z) 109 gl.glRotatef(-degree, 0.0f, 0.0f, 1.0f); 110 // 半径 0.07 の円柱を 0≦x≦1 に描画 111 drawCylinder(0.07f, 1.0f, 0.0f); 112 // 行列を保存した時の状態に戻す 113 gl.glPopMatrix(); 114 115 /* 棒 2 を描画(z 軸周りに degree 度回転)---*/ 116 gl.glPushMatrix(); 117
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, green, 0);
118 gl.glRotatef(degree, 0.0f, 0.0f, 1.0f); 119 drawCylinder(0.07f, 1.0f, -1.0f); // -1≦x≦0 120 gl.glPopMatrix(); 121 122 /* O 原子を原点に描画 ---*/ 123
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, red, 0);
124
// 塗りつぶしの球を描画
125
// glutSolidSphere(半径, Z 軸まわりの分割数, Z 軸に沿った分割数)
126
glut.glutSolidSphere(0.3, div, div);
127 128
-11-
// H 原子 1 を描画 ---*/
129
gl.glPushMatrix();
130
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, blue, 0);
131
gl.glRotatef(-degree, 0.0f, 0.0f, 1.0f);
132
gl.glTranslatef(1.0f, 0.0f, 0.0f); // x 方向に 1 だけ平行移動
133
glut.glutSolidSphere(0.2, div, div);
134 gl.glPopMatrix(); 135 136 // H 原子 2 を描画 ---*/ 137 gl.glPushMatrix(); 138
gl.glMaterialfv(GL.GL_FRONT, GL.GL_AMBIENT, blue, 0);
139
gl.glRotatef(degree, 0.0f, 0.0f, 1.0f);
140
gl.glTranslatef(-1.0f, 0.0f, 0.0f); // x 方向に-1 だけ平行移動
141
glut.glutSolidSphere(0.2, div, div);
142 gl.glPopMatrix(); 143 144 // glPushMatrix()を呼ぶ前の行列の状態に戻す 145 gl.glPopMatrix(); 146 } 147 148 ・・・(以下は変更ないので略)・・・ 149 150 } 151