Androidアプリ開発 OpenGL ES 1.0 2Dで四角形を表示する

2016年07月31日(編集2016年07月31日)
このエントリーをはてなブックマークに追加

Androidアプリは、OpenGL ES 1.0を利用できます。
この記事は、OpenGL ES 1.0を使って2Dで四角形を描画する方法を記載した記事です。

環境はAndroid 7.0 (API level 24) です。

環境

  • OS X Yosemite
  • Oracle jdk version 1.8.0_72
  • Android Studio 2.1.2
  • android sdk 24

難易度

初心者向け

サンプルコード

Android_OpenGL_Demo

OpenGL ES

OpenGL ESは、3Dグラフィックス用ライブラリOpenGLのサブセットです。モバイル等のリソースの少ないデバイスでも動くように仕様を縮小したライブラリです。

OpenGL ES 1.0

OpenGL ES 1.0は固定機能パイプラインと呼ばれる定番の処理が行われます。これは、3Dオブジェクトの移動やカメラ位置に応じた変換、光源の処理などを含んでいて、基本的な3D表現が出来るようになっています。

OpenGL ES 2.0

OpenGL ES 2.0は、OpenGL ES 1.0と実装方法が変わっていて、シェーダー(プログラマブルシェーダ)の実装が必要になります。
これは、OpenGL ES 1.0で固定機能パイプラインが行っていた、カメラ位置に応じた変換や光源の処理を自分で 記述する必要があります。

また、1.xとは基本的に互換性がありません。

Activity実装

OpenGL ES 1.0で四角形を実装します。
まずはActivityを実装します。

SquareActivity.java
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;

public class SquareActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Turn off the window's title bar
        requestWindowFeature(Window.FEATURE_NO_TITLE);

        // Fullscreen mode
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

        // Create GLSurfaceView
        GLSurfaceView gLSurfaceView = new GLSurfaceView(this);

        // Create GLSurfaceView.Renderer
        SquareRenderer renderer = new SquareRenderer();

        // set Renderer to GLSurfaceView
        gLSurfaceView.setRenderer(renderer);

        setContentView(gLSurfaceView);
    }
}
        

■ 実装の解説

1. タイトルバーの削除

不要なタイトルバーを削除します。

// Turn off the window's title bar
requestWindowFeature(Window.FEATURE_NO_TITLE);
        

2. フルスクリーン表示

画面をフルスクリーン表示にします。

// Fullscreen mode
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
        

3. GLSurfaceViewの生成

SurfaceViewの実装には、OpenGLをレンダリング表示するための、特別な表層(GLSurfaceView)を使います。 GLSurfaceViewは、EGLの表示を管理し、OpenGLをsurface(表層)にレンダリングすることができます。(だからprefixがGL)

また、GLSurfaceViewは、EGL関連初期化を内部で勝手にやってくれるので、EGLの知識はあまり必要としません。

// Create GLSurfaceView
GLSurfaceView gLSurfaceView = new GLSurfaceView(this);
        

4. GLSurfaceView.Rendererの生成

OpenGLのレンダリングクラスを生成します。
GLSurfaceView.Rendererをimplementsしたクラスを作成します。

// Create GLSurfaceView.Renderer
SquareRenderer renderer = new SquareRenderer();
        

上記のSquareRendererクラスは、GLSurfaceView.Rendererを実装しています。

5. GLSurfaceViewの登録

GLSurfaceViewを登録します。

// register Renderer
gLSurfaceView.setRenderer(renderer);
        

GLSurfaceViewを初期化するために、setRendererメソッドを呼び出します。
rendererは、OpenGLのレンダリングを行います。

GLSurfaceView.Renderer実装

GLSurfaceViewでOpenGLを利用するために、GLSurfaceView.Rendererを実装します。
四角形を描写するSquareRendererクラスを実装します。

SquareRenderer.java
import android.opengl.GLSurfaceView;

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

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class SquareRenderer implements GLSurfaceView.Renderer {

    public static final String TAG = "SquareRenderer";

    // screen resolution
    int   mScreenWidth = 1280;
    float   mScreenHeight = 768;

    @Override
    public void onSurfaceCreated(GL10 gl10, EGLConfig eglConfig) {

    }

    @Override
    public void onSurfaceChanged(GL10 gl10, int width, int height) {
        mScreenWidth = width;
        mScreenHeight = height;
    }

    @Override
    public void onDrawFrame(GL10 gl10) {
        gl10.glViewport(0, 0, (int)mScreenWidth, (int)mScreenHeight);
        gl10.glMatrixMode(GL10.GL_PROJECTION);

        gl10.glLoadIdentity();
        // 座標系の設定
        gl10.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f,0.5f, -0.5f);
        gl10.glMatrixMode(GL10.GL_MODELVIEW);
        gl10.glLoadIdentity();

        gl10.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
        gl10.glClear(GL10.GL_COLOR_BUFFER_BIT);

        // 座標
        float [] vertices = new float[]{
                -0.5f, -0.5f,
                0.5f, -0.5f,
                -0.5f, 0.5f,
                0.5f, 0.5f,
        };

        // 色
        float [] colors = new float[]{
                1.0f, 1.0f, 0.0f, 1.0f,
                0.0f, 1.0f, 1.0f, 1.0f,
                0.0f, 0.0f, 0.0f, 0.0f,
                1.0f, 0.0f, 1.0f, 1.0f,
        };

        // メモリに確保
        // The vertex buffer.
        ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
        bb.order(ByteOrder.nativeOrder());
        FloatBuffer vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);

        // The colors buffer.
        ByteBuffer dlb = ByteBuffer.allocateDirect(colors.length * 4);
        dlb.order(ByteOrder.nativeOrder());
        FloatBuffer colorsBuffer = dlb.asFloatBuffer();
        colorsBuffer.put(colors);
        colorsBuffer.position(0);

        gl10.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
        gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
        gl10.glColorPointer(4, GL10.GL_FLOAT, 0, colorsBuffer);
        gl10.glEnableClientState(GL10.GL_COLOR_ARRAY);
        gl10.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
    }
}
        

■ 実装の解説

1. 座標系の指定

OpenGLは、独自の座標系が適用されます。座標の指定には、glOrthofメソッドを使います。
glOrthofは、平行投影処理を行います。

gl10.glMatrixMode(GL10.GL_PROJECTION);
gl10.glOrthof(-1.0f, 1.0f, -1.0f, 1.0f,0.5f, -0.5f);
        

引数の内容は、(左端のX座標, 右端のX座標, 下端のY座標, 上端のY座標, 手前のZ座標, 奥のZ座標)になります。

glOrthof arguments
   

このサンプルは2D表示なので、z座標の値(0.5f, -0.5f)に意味はありません。

2. 色の指定

色を指定します。

gl10.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
gl10.glClear(GL10.GL_COLOR_BUFFER_BIT);
        

(R, G, B, アルファ)で引数を渡します。
GL_COLOR_BUFFER_BITは塗りつぶしです。

3. 座標の指定

座標は、ポリゴンの頂点の座標を配列で用意します。
2Dでは、頂点1のX座標, 頂点1のY座標, 頂点2のX座標, 頂点2のY座標, ....という順番で座標を指定します。

float [] vertices = new float[]{
        -0.5f, -0.5f,
        0.5f, -0.5f,
        -0.5f, 0.5f,
        0.5f, 0.5f,
};
        
Coordinates Array

4. システムメモリの確保

OpenGLは、VM上に確保したメモリ領域にアクセスできないので、作成した配列をシステムメモリ転送する必要があります。

ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4);
bb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = bb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
        

float型の配列を入れるためのデータ領域(allocateDirect)を用意し、そこにデータを転送しています。
データ領域はバイト列で表されます。float型は4byteなので、配列のサイズは「配列のサイズ×4」バイトになります。

5. 描写

最後に描写処理を記述します。

gl10.glVertexPointer(2, GL10.GL_FLOAT, 0, vertexBuffer);
gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl10.glColorPointer(4, GL10.GL_FLOAT, 0, colorsBuffer);
gl10.glEnableClientState(GL10.GL_COLOR_ARRAY);
gl10.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, 4);
        

glVertexPointerの2は、1つの頂点に必要なデータの個数を表しています。このサンプルは2Dなので、x,yの2つの値で1つの座標になるので2を指定します。
GL_FLOATはデータの型です。
0は、配列の何番目からデータを読みこむかを指定しています。通常は0です。

gl10.glEnableClientState(GL10.GL_VERTEX_ARRAY)は、OpenGLにデータをセットしたことを伝えています。

gl10.glDrawArraysで描写をします。GL_TRIANGLE_STRIPは、[頂点1, 頂点2, 頂点3], [頂点2, 頂点3, 頂点4]の順に描写されます。

ビルドして実行

実装を終えたら、ビルドしてアプリを実行します。

Square app

四角形が表示されました。

まとめ

OpenGL ESは1.0と2.0で実装が異なりますが、基本となる考え方は同じです。
最初はシンプルな1.0から覚えると、2.0の処理も理解しやすいです。
基礎のサンプルコードをたくさん実装して、OpenGLに慣れていきましょう。

タグ検索で調べてみよう

Android7.0 OpenGL ES 1.0