OpneGLの座標
図1 OpneGLの座標

先週は立方体を描画するところまで行いました。でも,全然3Dに見えないのがつらいところです。そこで,今週は回転や移動を行うことで,より3Dらしくしてみましょう。

回転や移動の前にほんの少しだけ座標の話をしなくてはなりません。OpneGLでの座標を図1に示しました。

Java 2Dなどで使用されるy軸は画面上の下にいくほど大きい値になります。これに対してOpneGLでは上にいくほど大きくなります。また,z軸は手前方向が正になります。

回転,移動,拡大・縮小は座標に対する行列演算で実現できます。これをアフィン変換と呼びます。Java 2Dにはjava.awt.geom.AffineTransformクラスがあり,2Dの回転,移動,拡大・縮小を行うことができます。

OpenGLでの回転などの変換は,このAffineTransformクラスの3D版と考えることができます。

とはいうものの,AffineTransformクラスのような行列を表しているクラスは出てこないので,どういう行列演算をしているかは気にしなくても大丈夫です。

後は実践あるのみ。ということで,さっそく先週のサンプルを改造していきましょう。

回転

立方体を回転させるということで,クラス名はRotateCubeにしました。

完成版はこちらからダウンロードできます RotateCube.java

モデルの回転はGL#glRotatefメソッド,もしくはglRotatedメソッドを使用します。メソッド名の最後のfとdは引数の型を示しています。glRotatefメソッドはfloatを使用し,glRotatedメソッドはdoubleを使用します。

両者とも引数の数は四つあります。第1引数が回転の角度で,単位は度です(ラジアンではありません)。第2引数から第4引数で,回転の中心となる軸を表します。

    public void display(GLAutoDrawable drawable) {
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
 
        // 行列を保存
        gl.glPushMatrix();
         
        // x 軸を中心に回転
        gl.glRotatef(angleX, 1.0f, 0.0f, 0.0f);
        // y 軸を中心に回転
        gl.glRotatef(angleY, 0.0f, 1.0f, 0.0f);
 
        // 立方体の描画
        glut.glutWireCube(2.0f);
 
        // 行列を元に戻す
        gl.glPopMatrix();
    }
RotateCubeの実行結果
図2 RotateCubeの実行結果

glRotatef/glRotatedメソッドをコールすることは前述した行列を変更することを意味しています。変更してしまうと,この後に描画するものはすべて回転した状態で表示されます。

指定した物体だけを回転させるには,行列を元に戻さなくてはいけません。

そのために,まず,現在使用している行列を,glPushMatrixメソッドを使用して保存しておきます。

そして,最後にglPopMatrixメソッドを使用して,行列を元に戻しておきます。

ここでは回転をわかりやすくするために,x軸を回転軸にした回転と,y軸を回転軸にした回転の2回に分けて行っています。気をつけなくてはならないのが,回転の順番によって結果が異なるということです。

さて,これを実行してみましょう。実行した結果を図2に示します。ここでは,x軸を中心に30度,y軸を中心に60度回転させました。

移動

次は移動です。

完成版はこちらからダウンロードできます MovedCube.java

移動も回転とほとんど変わりません。使用するメソッドはglTranslatefメソッド,もしくはglTranslatedメソッドです。回転と同様に前者が引数がfloat,後者が引数がdoubleになります。

    public void display(GLAutoDrawable drawable) {
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
 
        // 行列を保存
        gl.glPushMatrix();
        
        // 移動
        gl.glTranslatef(disX, disY, disZ);
 
        // 立方体の描画
        glut.glutWireCube(2.0f);
 
        // 行列を元に戻す
        gl.glPopMatrix();
    }
MovedCubeの実行結果
図3 MovedCubeの実行結果

disXがx軸方向の移動量,disYがy軸方向の移動量,そしてdisZがz軸方向の移動量です。それぞれ値は2,1,3になっています。

実行すると立方体が画面の右上の方に移動しました。

また,先週の立方体より大きく描画されていることがおわかりでしょうか。これはz軸の正方向に移動したため,視点位置に近づいてきたからです。

拡大・縮小

最後に残ったのが,拡大・縮小です。

拡大・縮小はGL#glScalefメソッド,もしくはglScaledメソッドを使用します。fとdの違いはこれまでと同じで,引数の型がfloatかdoubleかの違いです。

引数は三つあり,それぞれx軸,y軸,z軸方向の拡大率です。値が1より大きければ拡大,小さければ縮小します。

サンプルの完成版はこちらからダウンロードできます ScaledCube.java

    public void display(GLAutoDrawable drawable) {
        gl.glClear(GL.GL_COLOR_BUFFER_BIT);
 
        // 行列を保存
        gl.glPushMatrix();
         
        // 拡大・縮小
        gl.glScalef(scaleX, scaleY, scaleZ);
 
        // 立方体の描画
        glut.glutWireCube(2.0f);
 
        // 行列を元に戻す
        gl.glPopMatrix();
    }
ScaledCubeの実行結果
図4 ScaledCubeの実行結果

図4はscaleX=0.5,scaleY=2,scaleZ=5として実行した結果です。立方体ではなく直方体になりました。

最後に回転,移動,拡大・縮小をすべてやってみましょう。図5は,回転,移動,拡大・縮小の順に行ったものです。

一方図6は,拡大・縮小,回転,移動の順に行った結果です。回転量などはすべて同じなのですが,変換の順序が違うとこれだけ結果が変わってしまいます。

拡大・縮小した後に回転させると物体をゆがませることもできます。いろいろとやってみると楽しいので,ぜひお試しください。

回転,移動,拡大・縮小の順で変換を行なった結果 拡大・縮小,移動,回転の順で変換を行なった結果
図5 回転,移動,拡大・縮小の順で変換を行なった結果 図6 拡大・縮小,移動,回転の順で変換を行なった結果

来月は

今月は立方体を例にして,簡単な3D描画を行い,回転・移動などの座標変換を行いました。意外に簡単だったのではないでしょうか。

来月は,今月説明をしなかった,視点や見える範囲について,そしてライティングなどについて解説していきます。お楽しみに。

著者紹介 櫻庭祐一

横河電機の研究部門に勤務。同氏のJavaプログラマ向け情報ページ「Java in the Box」はあまりに有名