qiitaにおいて、一連のOpenGL ES2.0の知見をまとめましたので、もしよかったらそちらもご覧ください
利用をしやすいように、クラスを作って見ました。
一応、3Dは自分で実装してはいないので、2Dに特化してる作りかもしれません。
deleteTextireは一応いれてるのだけど、捨てた後も参照できてしまうので、消せないのかもしれません。
public class ShaderUtil { public static final String simpleVertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + "gl_Position = uMVPMatrix*vPosition;" + "}"; public static final String simpleFragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; 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;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "uniform float Opacity;" + "void main() {" + "lowp vec4 textureColor = texture2D( s_texture, v_texCoord );" + "gl_FragColor = vec4(textureColor.rgb, textureColor.w*Opacity);" + "}"; 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 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); } public static int getTextureLocation(int uniq){ 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; }else if(uniq == 11){ return GLES20.GL_TEXTURE11; }else if(uniq == 12){ return GLES20.GL_TEXTURE12; }else if(uniq == 13){ return GLES20.GL_TEXTURE13; }else if(uniq == 14){ return GLES20.GL_TEXTURE14; }else if(uniq == 15){ return GLES20.GL_TEXTURE15; }else if(uniq == 16){ return GLES20.GL_TEXTURE16; }else if(uniq == 17){ return GLES20.GL_TEXTURE17; }else if(uniq == 18){ return GLES20.GL_TEXTURE18; }else if(uniq == 19){ return GLES20.GL_TEXTURE19; }else if(uniq == 20){ return GLES20.GL_TEXTURE20; }else if(uniq == 21){ return GLES20.GL_TEXTURE21; }else if(uniq == 22){ return GLES20.GL_TEXTURE22; }else if(uniq == 23){ return GLES20.GL_TEXTURE23; }else if(uniq == 24){ return GLES20.GL_TEXTURE24; }else if(uniq == 25){ return GLES20.GL_TEXTURE25; }else if(uniq == 26){ return GLES20.GL_TEXTURE26; }else if(uniq == 27){ return GLES20.GL_TEXTURE27; }else if(uniq == 28){ return GLES20.GL_TEXTURE28; }else if(uniq == 29){ return GLES20.GL_TEXTURE29; }else if(uniq == 30){ return GLES20.GL_TEXTURE30; }else if(uniq == 31){ return GLES20.GL_TEXTURE31; } return GLES20.GL_TEXTURE0; } private static float xRotate(float _width,float _height,float rotate){ return _width/2f* (float)Math.cos(Math.PI * 2f * rotate / 360f) - _height/2f* (float)Math.sin(Math.PI * 2f * rotate / 360f); } private static float yRotate(float _width,float _height,float rotate){ return _width / 2f * (float)Math.sin(Math.PI * 2f * rotate / 360f) + _height / 2f * (float)Math.cos(Math.PI * 2f * rotate / 360f); } public static float[] getVertices(float _centerX, float _centerY, float width,float height,float rotate){ float uvs[] = new float[]{ _centerX + xRotate(-width, -height, rotate), _centerY + yRotate(-width, -height, rotate), 0.0f, _centerX + xRotate(-width, height, rotate), _centerY + yRotate(-width, height, rotate), 0.0f, _centerX + xRotate(width, -height, rotate), _centerY + yRotate(width, -height, rotate), 0.0f, _centerX + xRotate(-width, height, rotate), _centerY + yRotate(-width, height, rotate), 0.0f, _centerX + xRotate(width, -height, rotate), _centerY + yRotate(width, -height, rotate), 0.0f, _centerX + xRotate(width, height, rotate), _centerY + yRotate(width, height, rotate), 0.0f }; return uvs; } public static float[] getUvs(int _num,int _x,int _y){ float num = _num; float x = _x; float y = _y; float size = 1f/num; float uvs[] = new float[]{ x*size, y*size, x*size, y*size+size, x*size+size, y*size, x*size, y*size+size, x*size+size, y*size, x*size+size, y*size+size }; return uvs; } }
ただ、使い方は、
public class Sprite { private int shaderProgram; private int uniq = 0; public Sprite(Bitmap bitmap,int _uniq){ uniq = _uniq; 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); setupImage(bitmap); } int[] texture; private void setupImage(Bitmap bitmap) { texture = new int[1]; GLES20.glGenTextures(1, texture, 0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]); GLES20.glActiveTexture(ShaderUtil.getTextureLocation(uniq)); ShaderUtil.setBitmapShaderFilter(); // 画像をテクスチャに登録 GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); } public void draw(float[] m){ //8等分した、左から[5]上から[0]の画像を取得 float uvs[] = ShaderUtil.getUvs(8, 5, 0); //(210,220)を中心点 横幅230 縦幅240 角度は45度 float vertices[] = ShaderUtil.getVertices(210, 220, 230, 240, 45); 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"); // シェーダー:ON GLES20.glEnableVertexAttribArray(mPositionHandle); //シェーダーの準備(テクスチャ側) int mTexCoordLoc = GLES20.glGetAttribLocation(shaderProgram, "a_texCoord"); // シェーダー:ON GLES20.glEnableVertexAttribArray(mTexCoordLoc); //////////////////////////////////START //////////////////////////////////START //////////////////////////////////START //頂点座標をバッファーに変換 ByteBuffer bb1 = ByteBuffer.allocateDirect(vertices.length * 4); bb1.order(ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = bb1.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); //画像側の頂点座標をバッファーに変換 ByteBuffer bb2 = ByteBuffer.allocateDirect(uvs.length * 4); bb2.order(ByteOrder.nativeOrder()); FloatBuffer uvBuffer = bb2.asFloatBuffer(); uvBuffer.put(uvs); uvBuffer.position(0); //vertexBufferをデータ列から頂点データという解釈に変換 GLES20.glVertexAttribPointer(mPositionHandle, 3, GLES20.GL_FLOAT, false, 0, vertexBuffer); //uvBufferをデータ列から頂点データという解釈に変換 GLES20.glVertexAttribPointer(mTexCoordLoc, 2, GLES20.GL_FLOAT, false, 0, uvBuffer); // 描画に利用をする画像のデータをする GLES20.glUniform1i(GLES20.glGetUniformLocation(shaderProgram, "s_texture"), uniq); GLES20.glUniform1f(GLES20.glGetUniformLocation(shaderProgram, "Opacity"), 1.0f); GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix"), 1, false, m, 0); // 描画に利用をする画像のデータをする //GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]); // 描画する。何で描くのかは、「関数内で登録してある」という暗黙の了解的な。 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 6); //////////////////////////////////END //////////////////////////////////END //////////////////////////////////END // シェーダー:OFF GLES20.glDisableVertexAttribArray(mPositionHandle); GLES20.glDisableVertexAttribArray(mTexCoordLoc); GLES20.glDisable(GLES20.GL_BLEND); GLES20.glUseProgram(0); } public void dispose(){ GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0); GLES20.glDeleteTextures(1, texture, 0); } }
qiitaにおいて、一連のOpenGL ES2.0の知見をまとめましたので、もしよかったらそちらもご覧ください
今回は、matrixを用いてshaderを作ってみます。
そもそも最初に、
http://androidblog.reindustries.com/a-real-open-gl-es-2-0-2d-tutorial-part-1/
で勉強していたのに、matrixと、shaderの関係性がイマイチわかってなかったのですが、
今であれば、matrixを作って、uniformとしてshaderの中に突っ込むという動作をしていたのがわかります。
なので、今回はマトリックスを作って、適合をしてみます。
マトリックスは下記にしました
// 画面の座標系を作成 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);
座標系として、OpenGLは左下が基準となっていますが、左上に基準を変更して、スケールを解像度に揃えました。
でshaderを
public final String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix*vPosition;" + "}";
みたいにuMVPMatrixっていうのを考えて、
GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix"), 1, false, mtrxProjectionAndView, 0);
ってして、する事で、マトリックスを適応したシェーダーにする事で、思い通りの座標になります。
ソースコードは下記です
public class GLRenderer implements GLSurfaceView.Renderer { Context ctx; public GLRenderer(Context _ctx){ ctx = _ctx; } Triangle triangle; @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { triangle = new Triangle(); } // Our matrices private final float[] mtrxProjection = new float[16]; private final float[] mtrxView = new float[16]; private final float[] mtrxProjectionAndView = new float[16]; @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); 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); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClearColor(0.0f, 0.0f, 0.7f, 1); GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT); triangle.draw(mtrxProjectionAndView); } private void callLog(String str){ Log.e("LOG",str); } public void onPause(){ } public void onResume(){ } }
で三角形の記述は
public class Triangle { //三角形の頂点座標をx,y,zで記述 private float vertices[] = { 0f, 0f, 0.0f, 50f, 150f, 0.0f, 150f, 50f, 0.0f }; //色データを赤、緑、青、アルファで記述 float[] color = new float[]{0.0f,0.6f,1.0f,1f}; public final String vertexShaderCode = "uniform mat4 uMVPMatrix;" + "attribute vec4 vPosition;" + "void main() {" + " gl_Position = uMVPMatrix*vPosition;" + "}"; public final String fragmentShaderCode = "precision mediump float;" + "uniform vec4 vColor;" + "void main() {" + " gl_FragColor = vColor;" + "}"; public static int loadShader(int type, String shaderCode){ int shader = GLES20.glCreateShader(type); GLES20.glShaderSource(shader, shaderCode); GLES20.glCompileShader(shader); return shader; } private int shaderProgram; FloatBuffer vertexBuffer; public Triangle(){ //頂点座標をバッファーに変換 ByteBuffer bb = ByteBuffer.allocateDirect(vertices.length * 4); bb.order(ByteOrder.nativeOrder()); vertexBuffer = bb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); //ここからシェーダーを使うっていう、定形 int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode); int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode); shaderProgram = GLES20.glCreateProgram(); GLES20.glAttachShader(shaderProgram, vertexShader); GLES20.glAttachShader(shaderProgram, fragmentShader); GLES20.glLinkProgram(shaderProgram); } public void draw(float[] m){ //利用するシェーダーの選択 GLES20.glUseProgram(shaderProgram); // シェーダーの準備 int positionAttrib = GLES20.glGetAttribLocation(shaderProgram, "vPosition"); // シェーダー:ON GLES20.glEnableVertexAttribArray(positionAttrib); // vertexBufferをデータ列から頂点データという解釈に変換 //GLES20.glVertexAttribPointer(mPositionHandle, 頂点の数, GLES20.GL_FLOAT, 正規化をする, オフセット, vertexBuffer); GLES20.glVertexAttribPointer(positionAttrib,vertices.length/3, GLES20.GL_FLOAT, false, 0, vertexBuffer); //ユニフォーム変数にアクセスの準備 //色情報をユニフォーム変数に変換 GLES20.glUniform4fv(GLES20.glGetUniformLocation(shaderProgram, "vColor"), 1, color, 0); GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(shaderProgram, "uMVPMatrix"), 1, false, m, 0); //第二引数はオフセット、第三引数は、頂点の数 GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, vertices.length/3); // シェーダー:OFF GLES20.glDisableVertexAttribArray(positionAttrib); } }
で、画像を表示する時用のクラスができました!
qiitaにおいて、一連のOpenGL ES2.0の知見をまとめましたので、もしよかったらそちらもご覧ください
何度か、shaderを扱ってきまして、今回もshaderについてですが
public final String fragmentShaderCode = "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" + "}";
でこれをして、
GLES20.glUniform1f(GLES20.glGetUniformLocation(shaderProgram, "Brightness" ), 0.3f);
こんな漢字に明るさの値を決定。これではついでに透明度も入っていますが、vec3(Brightness)みたいな感じでRGBを各々追加するっていう形で明るさの変更ができました
http://xissburg.com/faster-gaussian-blur-in-glsl/
こちらを参考にして、
画像をぼかすには(横方向のみですが)
private String fragmentShaderCode = "precision mediump float;" + "varying vec2 v_texCoord;" + "uniform sampler2D s_texture;" + "uniform float Opacity;" + "uniform float Distance;" + "void main() {" + "vec4 sum = vec4(0.0);" + "gl_FragColor = vec4(0.0);" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.028*Distance, 0.0))*0.0044299121055113265;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.024*Distance, 0.0))*0.00895781211794;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.020*Distance, 0.0))*0.0215963866053;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.016*Distance, 0.0))*0.0443683338718;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.012*Distance, 0.0))*0.0776744219933;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.008*Distance, 0.0))*0.115876621105;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(-0.004*Distance, 0.0))*0.147308056121;" + "gl_FragColor += texture2D(s_texture, v_texCoord)*0.159576912161;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.004*Distance, 0.0))*0.147308056121;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.008*Distance, 0.0))*0.115876621105;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.012*Distance, 0.0))*0.0776744219933;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.016*Distance, 0.0))*0.0443683338718;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.020*Distance, 0.0))*0.0215963866053;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.024*Distance, 0.0))*0.00895781211794;" + "gl_FragColor += texture2D(s_texture, v_texCoord+vec2(0.028*Distance, 0.0))*0.0044299121055113265;" + "gl_FragColor = vec4( gl_FragColor.r,gl_FragColor.g,gl_FragColor.b,gl_FragColor.a*Opacity );" + "}";
にする
GLES20.glUniform1f(GLES20.glGetUniformLocation(shaderProgram, "Distance"), 2.0f);
みたいにfloatの値を入力する事でボケるのだけど、値が大きいとボケの密度が高すぎるのでボケでなくなってしまうので、小さい値を入れておく。
ここは使い方の1つ。という事でちょっと特異的な事ですが、アップしてみました