LoginSignup
9
3

More than 5 years have passed since last update.

AndroidでOpenGL ES2.0入門 その2

Posted at

背景を透過させたい。

qiita.java
glSurfaceView = new GLSurfaceView(getActivity().getApplicationContext());
glSurfaceView.setEGLContextClientVersion(2);
glSurfaceView.setZOrderOnTop(true);
glSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
glSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
mRenderer = new GameRenderer();
glSurfaceView.setRenderer(mRenderer);
glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY);
((FrameLayout)rootView.findViewById(R.id.base)).addView(glSurfaceView);

こんな感じにする。弊害としては、GLのViewが一番手前に来てしまう事。

画像を表示したい

1,shaderの登録は

qiita.java
private int shaderProgram;

    public Sprite(){
        shaderProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(shaderProgram, ShaderUtil.loadShader(GLES20.GL_VERTEX_SHADER, ShaderUtil.vertexShaderCode));
        GLES20.glAttachShader(shaderProgram, ShaderUtil.loadShader(GLES20.GL_FRAGMENT_SHADER, ShaderUtil.fragmentShaderCode));
        GLES20.glLinkProgram(shaderProgram);
    }

でShaderUtilはオリジナルのクラスで

qiita.java
public static int loadShader(int type, String shaderCode){
        int[] compiled = new int[1];
        int iShader = GLES20.glCreateShader(type);
        GLES20.glShaderSource(iShader, shaderCode);
        GLES20.glCompileShader(iShader);
        GLES20.glGetShaderiv(iShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
        if (compiled[0] == 0) {
            Log.e("Load Shader Failed", "Compilation\n" + GLES20.glGetShaderInfoLog(iShader));
            return 0;
        }
        return iShader;
    }
public static final String vertexShaderCode =
            "attribute vec4 vPosition;" +
                    "uniform mat4 uMVPMatrix;" +
                    "attribute vec2 a_texCoord;" +
                    "varying vec2 v_texCoord;" +
                    "void main() {" +
                    "  gl_Position = uMVPMatrix*vPosition;" +
                    "  v_texCoord = a_texCoord;" +
                    "}";
    public static final String fragmentShaderCode ="precision mediump float;" +
            "precision mediump float;" +
            "varying vec2 v_texCoord;" +
            "uniform sampler2D s_texture;" +
            "uniform float Opacity;" +
            "uniform float Brightness;" +
            "void main() {" +
            "lowp vec4 textureColor = texture2D( s_texture, v_texCoord );" +
            "gl_FragColor = vec4((textureColor.rgb + vec3(Brightness)), textureColor.w*Opacity);\n" +
            "}";

ここでは、一応、明度と、RGBと透過度を変更できるような仕様のデータをいれてあります。→ fragmentShaderCode

1、画像の登録

qiita.java
int[] texture;
    public void setupImage(Bitmap bitmap, int _uniq) {
        texture = new int[1];
        GLES20.glGenTextures(1, texture, 0);
        GLES20.glActiveTexture(ShaderUtil.getTextureLocation(_uniq));
        GLES20.glDeleteTextures(1, texture, 0);
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);
        ShaderUtil.setBitmapShaderFilter();
        // 画像をテクスチャに登録
        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
    }

でShaderUtilはオリジナルのクラスで

qiita.java
public static int getTextureLocation(int uniq){
        if(uniq>4){
            Log.e("CAUTION","端末によっては表示がされない可能性があります。");
        }
        if(uniq == 0){
            return GLES20.GL_TEXTURE0;
        }else if(uniq == 1){
            return GLES20.GL_TEXTURE1;
        }else if(uniq == 2){
            return GLES20.GL_TEXTURE2;
        }else if(uniq == 3){
            return GLES20.GL_TEXTURE3;
        }else if(uniq == 4){
            return GLES20.GL_TEXTURE4;
        }else if(uniq == 5){
            return GLES20.GL_TEXTURE5;
        }else if(uniq == 6){
            return GLES20.GL_TEXTURE6;
        }else if(uniq == 7){
            return GLES20.GL_TEXTURE7;
        }else if(uniq == 8){
            return GLES20.GL_TEXTURE8;
        }else if(uniq == 9){
            return GLES20.GL_TEXTURE9;
        }else if(uniq == 10){
            return GLES20.GL_TEXTURE10;
        }
        return GLES20.GL_TEXTURE0;
    }

qiita.java
 public static void setBitmapShaderFilter(){
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
    }

を読んでいる。

これテクスチャの登録に関しては、何枚か!を入れる事ができるのだけど、
問題があって、枚数が一定以上の場合には、表示が化ける。みたいな事が発生します。しかも、端末によって限界の枚数が違うというクソ仕様もあるので、
できれば1枚をいわゆるスプライトシート型にして使うのがベストです。

削除に関しては下記みたいなのを用意。

qiita.java
public void dispose(){
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
        GLES20.glDeleteTextures(1, texture, 0);
    }

で、描画をするときには、

qiita.java
float uvs[] = new float[3200];
float vertices[] = new float[4800];
FloatBuffer vertexBuffer;
FloatBuffer uvBuffer;
ByteBuffer bb1;
ByteBuffer bb2;

をあらかじめ用意しておくと都合がよかったりする。

qiita.java
private int shaderProgram;

    public Sprite(){
        shaderProgram = GLES20.glCreateProgram();
        GLES20.glAttachShader(shaderProgram, ShaderUtil.loadShader(GLES20.GL_VERTEX_SHADER, ShaderUtil.vertexShaderCode));
        GLES20.glAttachShader(shaderProgram, ShaderUtil.loadShader(GLES20.GL_FRAGMENT_SHADER, ShaderUtil.fragmentShaderCode));
        GLES20.glLinkProgram(shaderProgram);

        //頂点座標をバッファーに変換
        if(bb1 == null) {
            bb1 = ByteBuffer.allocateDirect(vertices.length * 4);
            bb1.order(ByteOrder.nativeOrder());
            vertexBuffer = bb1.asFloatBuffer();
        }
        //画像側の頂点座標をバッファーに変換
        if(bb2 == null) {
            bb2 = ByteBuffer.allocateDirect(uvs.length * 4);
            bb2.order(ByteOrder.nativeOrder());
            uvBuffer = bb2.asFloatBuffer();
        }
    }

こんな感じにバッファー化を行い。

qiita.java
public void draw(float alpha,float bright,Data _Data,int _uniq){
        float uvs_s[] = _Data.GetUvs();
        float vertices_s[] = _Data.GetVertices();
        for(int k = 0 ; k < 12;k++) {
            uvs[k] = uvs_s[k];
        }
        for(int k = 0 ; k < 18;k++) {
            vertices[k] = vertices_s[k];
        }
        GLES20.glEnable(GLES20.GL_BLEND);
        GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
        GLES20.glUseProgram(shaderProgram);
        int mPositionHandle = GLES20.glGetAttribLocation(shaderProgram, "vPosition");
        GLES20.glEnableVertexAttribArray(mPositionHandle);
        int mTexCoordLoc = GLES20.glGetAttribLocation(shaderProgram, "a_texCoord");
        GLES20.glEnableVertexAttribArray(mTexCoordLoc);
        vertexBuffer.put(vertices);
        vertexBuffer.position(0);
        uvBuffer.put(uvs);
        uvBuffer.position(0);
        GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer);
        GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, uvBuffer);
        GLES20.glUniform1i(GLES20.glGetUniformLocation(shaderProgram, "s_texture"), _uniq);
        GLES20.glUniform1f(GLES20.glGetUniformLocation(shaderProgram, "Opacity"), alpha);
        GLES20.glUniform1f(GLES20.glGetUniformLocation(shaderProgram, "Brightness"), bright);
        GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix"), 1, false, mtrxProjectionAndView, 0);
        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, uvs_s.length/2);
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTexCoordLoc);
        GLES20.glDisable(GLES20.GL_BLEND);
    }

ってするのですが、

qiita.java
mtrxProjectionAndViewに関してはまだ定義していなかったのですが


// Our matrices
    public final float[] mtrxProjection = new float[16];
    public final float[] mtrxView = new float[16];
    public final float[] mtrxProjectionAndView = new float[16];


for(int i=0;i<16;i++) {
            mtrxProjection[i] = 0.0f;
            mtrxView[i] = 0.0f;
            mtrxProjectionAndView[i] = 0.0f;
        }

        // 画面の座標系を作成
        Matrix.orthoM(mtrxProjection, 0,
                0f, width,//左端→右端
                height, 0.0f, //下端→上端
                -1, 1//手前→奥
        );

        //カメラの向きを作成
        Matrix.setLookAtM(mtrxView, 0,
                0f, 0f, 1f,//カメラ位置
                0f, 0f, 0f,//カメラの向き
                0f, 1.0f, 0.0f//カメラの頭
        );

        //上記2つを合算。
        Matrix.multiplyMM(mtrxProjectionAndView, 0, mtrxProjection, 0, mtrxView, 0);
        mtrxProjectionAndView = mtrxProjectionAndView.clone();

こんな風に座標のデータを用意しておくと便利

で、

qiita.java
Data.GetUvs();

とかについては、UVSを用意してねって事

qiita.java
Data.GetVertices();

はVERTICESを用意してねって部分でデータは各々が用意する必要がありますが

UVSは画像の座標の事
VERTICESはステージに座標の事。

たとえば

qiita.java
public static float[] getUvs(){
        return new float[]{
                0,    0,
                0,    1,
                1,    0,
                0,    1,
                1,    0,
                1,    1
        };
    }

これは読み込んだ画像まるまる。

qiita.java
public static float[] getUvs(){
        return new float[]{
                0,    0,
                0,    1/4,
                1/4,    0,
                0,    1/4,
                1/4,    0,
                1/4,    1/4
        };
    }

これは読み込んだ画像の左上1/4

みたいに、いろいろverticesについては、上記のmtrxProjectionAndViewで設定した自分の座標系を考える必要があるのだけど、

qiita.java
public static float[] getVertices(){

        return new float[]{
                0, 0, 0.0f,
                0, height, 0.0f,
                width, 0, 0.0f,
                0, height, 0.0f,
                width, 0, 0.0f,
                width, height, 0.0f
        };
    }

これで全画面に貼りつける事が出来る。

でわりと自分は2Dで使うのだけど、これだけあれば、あとは上手く使うだけで、
これ以上余計な事を考える事もないので、一回画像を表示させて、好きな位置とスプライトシートをうまく使えば、カンタンにできるかと思います。

上記、結構大掛かりな案件でも使った内容ですが、OPENGL自体あまり、Androidで使う人がいないかと思いますが、悩んでる方などには、ある程度道しるべになるかと思います。

9
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
3