読者です 読者をやめる 読者になる 読者になる

モデルビュー行列と射影行列

今回はOpenGLで使う行列(マトリックス)の設定について解説します。
OpenGL2回目としては敷居が高いですが、わからなければとりあえず書かれているまま使っておけば大丈夫です。
とりあえず何とか次の回で3Dを実感できる移動・回転・拡大・縮小をやりたいので、その前準備です。

サンプル

ソースは前回のサンプルを流用します。
GLActivity.java、GLView.javaに関しては変更がありませんので、省略します。
サンプルは動かすと、ポリゴンを正面右上から見下ろしている画面が表示されます。



●GLThread.java
=========================================================================================

package jp.kambayashi.gl;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.egl.EGLDisplay;
import javax.microedition.khronos.egl.EGLSurface;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLU;
import android.util.Log;

public class GLThread extends Thread {
	//オブジェクト識別子
	private String mName;
	//画面管理オブジェクト
	private GLView mView;
	//実行完了フラグ
	private boolean mDone;
	//描画ヘルパー
	private Renderer mRenderer;

	//ESレンダリングコンテキスト
	private EGLContext mEglContext;
	//ESディスプレイコネクション
	private EGLDisplay mEglDisplay;
	//ESサーフェイス
	private EGLSurface mEglSurface;
	//ESコンフィグ
	private EGLConfig mEglConfig;
	
	/**
	 * コンストラクタ
	 * @param view	
	 */
	public GLThread(GLView view){
		mView = view;
		mName = "GLThread";
		mDone = false;
		mRenderer = new Renderer();
	}

	/**
	 * スレッド処理スタート
	 */
	@Override
	public void run() {
		//OpenGL ESの準備
		if( !initGLES() ){
			Log.e(mName, "OpenGL ES初期化失敗");
			mDone = true;
		}
			
		//終了要求がでるまで繰り返し
		GL10 gl = (GL10)mEglContext.getGL();
		while(!mDone){
			//描画
			drawFrame(gl);
		}
		
		//OpenGL ESの片付け
		endGLES();
	}
	
	/**
	 * 描画
	 * 
	 * @param gl	OpenGL操作ハンドル
	 */
	private void drawFrame( GL10 gl ){        
		{
			//射影行列設定
			gl.glMatrixMode(GL10.GL_PROJECTION);
			gl.glLoadIdentity();

			float aspect = (float)mView.getWidth() / (float)mView.getHeight();
			GLU.gluPerspective(gl, 45.0f, aspect, 0.1f, 10.0f);

			//ビューポート設定
			gl.glViewport(0,0,mView.getWidth(),mView.getHeight());

		}
		
		{
			//モデルビュー行列の設定
			gl.glMatrixMode(GL10.GL_MODELVIEW);
			//単位行列
			gl.glLoadIdentity();
			//カメラの設定
			GLU.gluLookAt(gl, 
					1.0f, 1.0f, 3.0f, 
					0.0f, 0.0f, 0.0f, 
					0.0f, 1.0f, 0.0f); 
		}
        
        
		//背景クリア
		gl.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
		
		//描画
		mRenderer.draw(gl);
		
		//画面に出力するバッファの切り替え
		EGL10 egl = (EGL10)EGLContext.getEGL();
		egl.eglSwapBuffers(mEglDisplay, mEglSurface);
	}
	
	/**
	 * OpenGL ES初期化
	 * 
	 * 
	 * @return	正常終了なら真、エラーなら偽
	 */
	private boolean initGLES(){
		//GL ES操作モジュール取得 
		EGL10 egl = (EGL10)EGLContext.getEGL();
		
		{
			//ディスプレイコネクション作成
			mEglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
			if( mEglDisplay == EGL10.EGL_NO_DISPLAY ){
				Log.e(mName, "ディスプレイコネクション作成失敗");
				return false;
			}
			
			//ディスプレイコネクション初期化
			int[] version = new int[2];
			if( !egl.eglInitialize(mEglDisplay, version) ){
				Log.e(mName, "ディスプレイコネクション初期化失敗");
				return false;
			}
		}
		
		{
			//コンフィグ設定
			int[] configSpec = {
				EGL10.EGL_ALPHA_SIZE, 8,	//アルファチャンネル:8ビット
				EGL10.EGL_RED_SIZE, 8,		//赤要素:8ビット
				EGL10.EGL_GREEN_SIZE, 8,	//緑要素:8ビット
				EGL10.EGL_BLUE_SIZE, 8,		//青要素:8ビット
				EGL10.EGL_DEPTH_SIZE, 16,	//深度バッファ:16ビット
				EGL10.EGL_NONE				//終端にはEGL_NONEを入れる
			};
			EGLConfig[] configs = new EGLConfig[1];
			int[] numConfigs = new int[1];
			if( !egl.eglChooseConfig(mEglDisplay, configSpec, configs, 1, numConfigs) ){
				Log.e(mName, "コンフィグ設定失敗");
				return false;
			}
			mEglConfig = configs[0];
		}
		
		{
			//レンダリングコンテキスト作成
			mEglContext = 
				egl.eglCreateContext(mEglDisplay, mEglConfig, EGL10.EGL_NO_CONTEXT, null);
			if( mEglContext == EGL10.EGL_NO_CONTEXT ){
				Log.e(mName, "レンダリングコンテキスト作成失敗");
				return false;
			}
		}
		
		{
			//サーフェイス作成(あとで分けるので別メソッド)
			if( !createSurface() ){
				Log.e(mName, "サーフェイス作成失敗");
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * サーフェイス作成
	 * 
	 * サーフェイスを作成して、レンダリングコンテキストと結びつける
	 * 
	 * @return	正常終了なら真、エラーなら偽
	 */
	private boolean createSurface(){
		EGL10 egl = (EGL10)EGLContext.getEGL();
		
		{
			//サーフェイス作成
			mEglSurface = 
				egl.eglCreateWindowSurface(mEglDisplay, mEglConfig, mView.getHolder(), null);
			if( mEglSurface == EGL10.EGL_NO_SURFACE ){
				Log.e(mName, "サーフェイス作成失敗");
				return false;
			}
		}
		
		{
			//サーフェイスとレンダリングコンテキスト結びつけ
			if( !egl.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) ){
				Log.e(mName, "レンダリングコンテキストとの結びつけ失敗");
				return false;
			}
		}
		
		return true;
	}
	
	/**
	 * OpenGL ES片付け
	 */
	private void endGLES(){
		EGL10 egl = (EGL10)EGLContext.getEGL();
		
		//サーフェイス破棄
		if( mEglSurface != null){
			//レンダリングコンテキストとの結びつけは解除
			egl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
			
			egl.eglDestroySurface(mEglDisplay, mEglSurface);
			mEglSurface = null;
		}
		
		//レンダリングコンテキスト破棄
		if( mEglContext != null ){
			egl.eglDestroyContext(mEglDisplay, mEglContext);
			mEglContext = null;
		}
		
		//ディスプレイコネクション破棄
		if( mEglDisplay != null){
			egl.eglTerminate(mEglDisplay);
			mEglDisplay = null;
		}
	}
	
	/**
	 * スレッド終了要求
	 * 
	 * スレッドに終了要求を出して、停止するのを待つ
	 */
	public void RequestExitAndWait(){
		synchronized (this) {
			//終了要求を出す
			mDone = true;
		}
		
		try{
			//スレッド終了を待つ
			join();
		}
		catch( InterruptedException ex ){
			Thread.currentThread().interrupt();
		}
	}
	
	/**
	 * 描画補助クラス
	 */
	class Renderer{
		private FloatBuffer buffer;
		
		/**
		 * コンストラクタ
		 */
		public Renderer(){
			//頂点数x頂点構成要素数x4バイトのサイズのバッファ作成
			ByteBuffer vb = ByteBuffer.allocateDirect(4 * 3 * 4);
			vb.order(ByteOrder.nativeOrder());	//ビッグエンディアンかリトルエンディアンにあわせてくれる
			buffer = vb.asFloatBuffer();
		
			//頂点データで頂点バッファ作成
			float[] vertices = {
					-0.5f, -0.5f, 0.0f,
					0.5f, -0.5f, 0.0f,
					-0.5f, 0.5f, 0.0f,
					0.5f, 0.5f, 0.0f,
			};
			buffer.put(vertices);
			buffer.position(0);
		}
		
		/**
		 * 描画
		 * @param gl	GL操作ハンドル
		 */
		public void draw( GL10 gl ){
			//ポリゴンカラー設定
			gl.glColor4f(0.8f, 0.8f, 1.0f, 1.0f);
			//頂点バッファ機能ON
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			//頂点バッファ設定
		 	gl.glVertexPointer(3, GL10.GL_FLOAT, 0, buffer);
			//描画
			gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
		}
	}
}