android opengles 实现翻牌效果
16lz
2021-01-26
前一阵子通过android.graphics.Camera实现翻牌效果,看源码知道是3x3的矩阵,我就想opengles也可以实现,然后就开始动手了
首先设置初始矩阵
Matrix.perspectiveM(projectionMatrix, 0, 90f, 1, 1, 50);Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f,0.0f, 0.0f, -1.0f, 0.0f);
perspectiveM设置摄像机视角矩阵,setLookAtM设置摄像头矩阵
旋转
Matrix.multiplyMM(modelMatrix, 0, projectionMatrix, 0, viewMatrix, 0);if(radian > 90){ Matrix.rotateM(modelMatrix,0,radian - 180,rx,ry,0);}else{ Matrix.rotateM(modelMatrix,0,radian,rx,ry,0);}
沿着x轴旋转或沿着y轴旋转,当角度等于90度的时候会消失掉,大于90度的时候会变为镜像,所以反一下就正常了
不过有点问题,设置GLES20.glViewport(0,0, width, height);的时候超出的部分看不见了
没办法,就把glViewport宽高扩大一倍,然后把显示部分居中
2018/11/9更新
这方法不行,在fbo模式下不能用,所以换了,具体看完整代码
private Rect rect = new Rect();public void setRect(int x,int y,int showWidth,int showHeight,int screenWidth,int screenHeight){ int top = screenHeight - showHeight - y - showHeight/2; int left = x - showWidth/2; int right = showWidth*2; int bottom = showHeight*2; rect.set(left,top,right,bottom);}
GLES20.glViewport(rect.left, rect.top, rect.right, rect.bottom);
setLookAtM也要改一下
Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f,0.0f, 0.0f, -1.0f, 0.0f);
看看效果
和安卓自定义View进阶-Matrix Camera这篇里提到的bug一样,而opengles的矩阵是4x4的,和android.graphics.Camera的不一样,不过通过这篇文章的思路在Matrix.scaleM()的源码内很快就找到了
modelMatrix[3] = modelMatrix[3]/scale;modelMatrix[7] = modelMatrix[7]/scale;
看看效果
接下来贴代码,懒得上传了
ShaderUtils.java
public class ShaderUtils { private static final String TAG = "ShaderUtils"; public static void checkGlError(String label) { int error; while ((error = GLES20.glGetError()) != GLES20.GL_NO_ERROR) { Log.e(TAG, label + ": glError " + error); throw new RuntimeException(label + ": glError " + error); } } public static int createProgram(String vertexSource, String fragmentSource) { int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource); if (vertexShader == 0) { return 0; } int pixelShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource); if (pixelShader == 0) { return 0; } int program = GLES20.glCreateProgram(); if (program != 0) { GLES20.glAttachShader(program, vertexShader); checkGlError("glAttachShader"); GLES20.glAttachShader(program, pixelShader); checkGlError("glAttachShader"); GLES20.glLinkProgram(program); int[] linkStatus = new int[1]; GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0); if (linkStatus[0] != GLES20.GL_TRUE) { Log.e(TAG, "Could not link program: "); Log.e(TAG, GLES20.glGetProgramInfoLog(program)); GLES20.glDeleteProgram(program); program = 0; } } return program; } public static int loadShader(int shaderType, String source) { int shader = GLES20.glCreateShader(shaderType); if (shader != 0) { GLES20.glShaderSource(shader, source); GLES20.glCompileShader(shader); int[] compiled = new int[1]; GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compiled, 0); if (compiled[0] == 0) { Log.e(TAG, "Could not compile shader " + shaderType + ":"); Log.e(TAG, GLES20.glGetShaderInfoLog(shader)); GLES20.glDeleteShader(shader); shader = 0; } } return shader; } public static String readRawTextFile(Context context, int resId) { InputStream inputStream = context.getResources().openRawResource(resId); try { BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); StringBuilder sb = new StringBuilder(); String line; while ((line = reader.readLine()) != null) { sb.append(line).append("\n"); } reader.close(); return sb.toString(); } catch (IOException e) { e.printStackTrace(); } return null; }}
bitmap_fragment_sharder.glsl
precision mediump float;varying vec2 vTexCoord;uniform sampler2D sTexture;void main() { gl_FragColor = texture2D(sTexture,vTexCoord);}
bitmap_vertext_shader.glsl
attribute vec4 aPosition;attribute vec2 aTexCoord;varying vec2 vTexCoord;uniform mat4 uMatrix;void main() { vTexCoord=aTexCoord; gl_Position = uMatrix*aPosition;}
EGLUtils.java
public class EGLUtils { private static final int EGL_RECORDABLE_ANDROID = 0x3142; private EGLSurface eglSurface = EGL14.EGL_NO_SURFACE; private EGLContext eglCtx = EGL14.EGL_NO_CONTEXT; private EGLDisplay eglDis = EGL14.EGL_NO_DISPLAY; public void initEGL(Surface surface) { eglDis = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY); int[] version = new int[2]; EGL14.eglInitialize(eglDis, version, 0, version, 1); int confAttr[] = { EGL14.EGL_RED_SIZE, 8, EGL14.EGL_GREEN_SIZE, 8, EGL14.EGL_BLUE_SIZE, 8, EGL14.EGL_ALPHA_SIZE, 8, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT, EGL_RECORDABLE_ANDROID, 1, EGL14.EGL_SAMPLE_BUFFERS, 1, EGL14.EGL_SAMPLES, 4, EGL14.EGL_NONE }; EGLConfig[] configs = new EGLConfig[1]; int[] numConfigs = new int[1]; EGL14.eglChooseConfig(eglDis, confAttr, 0, configs, 0, 1, numConfigs, 0); int ctxAttr[] = { EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,// 0x3098 EGL14.EGL_NONE }; eglCtx = EGL14.eglCreateContext(eglDis, configs[0], EGL14.EGL_NO_CONTEXT, ctxAttr, 0); int[] surfaceAttr = { EGL14.EGL_NONE }; eglSurface = EGL14.eglCreateWindowSurface(eglDis, configs[0], surface, surfaceAttr, 0); EGL14.eglMakeCurrent(eglDis, eglSurface, eglSurface, eglCtx); } public EGLContext getContext() { return eglCtx; } public void swap() { EGL14.eglSwapBuffers(eglDis, eglSurface); } public void release() { if (eglSurface != EGL14.EGL_NO_SURFACE) { EGL14.eglMakeCurrent(eglDis, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT); EGL14.eglDestroySurface(eglDis, eglSurface); eglSurface = EGL14.EGL_NO_SURFACE; } if (eglCtx != EGL14.EGL_NO_CONTEXT) { EGL14.eglDestroyContext(eglDis, eglCtx); eglCtx = EGL14.EGL_NO_CONTEXT; } if (eglDis != EGL14.EGL_NO_DISPLAY) { EGL14.eglTerminate(eglDis); eglDis = EGL14.EGL_NO_DISPLAY; } }}
GLBitmap.java
public class GLBitmap { private int aPositionHandle; private int uMatrixHandle; private int uTextureSamplerHandle; private int aTextureCoordHandle; private int programId; private int textureId; private FloatBuffer vertexBuffer; private final float[] vertexData = { 1f, -1f,0, -1f, -1f,0, 1f, 1f,0, -1f, 1f,0 }; private FloatBuffer textureVertexBuffer; private final float[] textureVertexData = { 1f, 0f,//右下 0f, 0f,//左下 1f, 1f,//右上 0f, 1f//左上 }; private final float[] modelMatrix=new float[16]; private final float[] projectionMatrix= new float[16]; private final float[] viewMatrix = new float[16]; private Bitmap bitmap; private Context context; public GLBitmap(Context context,int id){ this.context = context; scale = context.getResources().getDisplayMetrics().density; vertexBuffer = ByteBuffer.allocateDirect(vertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(vertexData); vertexBuffer.position(0); textureVertexBuffer = ByteBuffer.allocateDirect(textureVertexData.length * 4) .order(ByteOrder.nativeOrder()) .asFloatBuffer() .put(textureVertexData); textureVertexBuffer.position(0); bitmap = BitmapFactory.decodeResource(context.getResources(),id); } public void surfaceCreated(){ String vertexShader = ShaderUtils.readRawTextFile(context, R.raw.bitmap_vertext_shader); String fragmentShader = ShaderUtils.readRawTextFile(context, R.raw.bitmap_fragment_sharder); programId = ShaderUtils.createProgram(vertexShader, fragmentShader); aPositionHandle = GLES20.glGetAttribLocation(programId, "aPosition"); uMatrixHandle=GLES20.glGetUniformLocation(programId,"uMatrix"); uTextureSamplerHandle=GLES20.glGetUniformLocation(programId,"sTexture"); aTextureCoordHandle=GLES20.glGetAttribLocation(programId,"aTexCoord"); final int[] texture=new int[1]; GLES20.glGenTextures(1,texture,0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,texture[0]); 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); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D,0,GLES20.GL_RGBA,bitmap,0); textureId = texture[0]; Matrix.perspectiveM(projectionMatrix, 0, 90f, 1, 1, 50); Matrix.setLookAtM(viewMatrix, 0, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f,0.0f, 0.0f, -1.0f, 0.0f); GLES20.glUseProgram(programId); GLES20.glEnableVertexAttribArray(aPositionHandle); GLES20.glVertexAttribPointer(aPositionHandle, 2, GLES20.GL_FLOAT, false, 12, vertexBuffer); GLES20.glEnableVertexAttribArray(aTextureCoordHandle); GLES20.glVertexAttribPointer(aTextureCoordHandle,2,GLES20.GL_FLOAT,false,8,textureVertexBuffer); GLES20.glUseProgram(0); } private int radian = 0; public void setRadian(int radian) { this.radian = radian; } private int rx = 0; private int ry = -1; public void setType(int type){ if(type == 0){ rx = 0; ry = -1; s = 1.6f; }else{ rx = -1; ry = 0; s = 1.5f; } } private Rect rect = new Rect(); public void setRect(int showWidth,int showHeight,int screenWidth,int screenHeight){ int left,top,viewWidth,viewHeight; float sh = screenWidth*1.0f/screenHeight; float vh = showWidth*1.0f/showHeight; if(sh < vh){ left = 0; viewWidth = screenWidth; viewHeight = (int)(showHeight*1.0f/showWidth*viewWidth); top = (screenHeight - viewHeight)/2; }else{ top = 0; viewHeight = screenHeight; viewWidth = (int)(showWidth*1.0f/showHeight*viewHeight); left = (screenWidth - viewWidth)/2; } int w = (int) (viewWidth*s); int f = (w - viewWidth)/2; rect.set(left - f,top,w,viewHeight); } private float scale = 1; private float s = 1.6f; public int getWidth(){ return bitmap.getWidth()/10; } public int getHeight(){ return bitmap.getHeight()/10; } void surfaceDraw(){ GLES20.glViewport(rect.left, rect.top, rect.right, rect.bottom); Matrix.multiplyMM(modelMatrix, 0, projectionMatrix, 0, viewMatrix, 0); if(radian > 90){ Matrix.rotateM(modelMatrix,0,radian - 180,rx,ry,0); }else{ Matrix.rotateM(modelMatrix,0,radian,rx,ry,0); } Matrix.scaleM(modelMatrix,0,1.0f,s,1f); modelMatrix[3] = modelMatrix[3]/scale; modelMatrix[7] = modelMatrix[7]/scale; GLES20.glClearColor(0,0,0,0); GLES20.glUseProgram(programId); GLES20.glUniformMatrix4fv(uMatrixHandle,1,false,modelMatrix,0); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId); GLES20.glUniform1i(uTextureSamplerHandle,0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,0); GLES20.glUseProgram(0); }}
MainActivity.java
public class MainActivity extends AppCompatActivity { private Handler bitmapHandler; private HandlerThread bitmapThread; private SurfaceView surfaceView; private EGLUtils eglUtils; private GLBitmap glBitmapX;// private GLBitmap glBitmapY; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SeekBar seekBar = findViewById(R.id.seek_bar); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { rotate(progress); } @Override public void onStartTrackingTouch(SeekBar seekBar) { } @Override public void onStopTrackingTouch(SeekBar seekBar) { } }); glBitmapX = new GLBitmap(this,R.drawable.ic_jn); glBitmapX.setType(1);// glBitmapY = new GLBitmap(this,R.drawable.ic_m);// glBitmapY.setType(1); bitmapThread = new HandlerThread("AudioMediaCodec"); bitmapThread.start(); bitmapHandler = new Handler(bitmapThread.getLooper()); surfaceView = findViewById(R.id.surface_view); surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() { @Override public void surfaceCreated(final SurfaceHolder holder) { bitmapHandler.post(new Runnable() { @Override public void run() { eglUtils = new EGLUtils(); eglUtils.initEGL(holder.getSurface()); glBitmapX.surfaceCreated();// glBitmapY.surfaceCreated(); } }); } @Override public void surfaceChanged(SurfaceHolder holder, int format, final int width, final int height) { bitmapHandler.post(new Runnable() { @Override public void run() { glBitmapX.setRect(glBitmapX.getWidth(),glBitmapX.getHeight(),width,height);// glBitmapY.setRect(width - glBitmapX.getWidth() - 50,height - glBitmapY.getHeight() - 100,glBitmapY.getWidth(),glBitmapY.getHeight(),width,height); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); glBitmapX.surfaceDraw();// glBitmapY.surfaceDraw(); eglUtils.swap(); } }); } @Override public void surfaceDestroyed(SurfaceHolder holder) { bitmapHandler.post(new Runnable() { @Override public void run() { eglUtils.release(); } }); } }); } private void rotate(final int rotate){ bitmapHandler.post(new Runnable() { @Override public void run() { GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); glBitmapX.setRadian(rotate); glBitmapX.surfaceDraw();// glBitmapY.setRadian(rotate);// glBitmapY.surfaceDraw(); eglUtils.swap(); } }); } @Override protected void onDestroy() { super.onDestroy(); bitmapThread.quit(); bitmapThread = null; }}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
更多相关文章
- Android底部导航栏之BottomNavigationBar
- android UI设计的一些心得与问题解决(无效果图)
- Android(安卓)动态控制控件的位置
- android的TextView在设置ellipsize可能显示方框的问题
- Android(安卓)项目配置之 Version Name & Code
- android 动态设置读取文件的编码格式(charset)
- 笔记33--ListView和ScrollView点击事件的冲突
- 关于EditText的属性和常识
- android中 关于listitem 获得焦点时,改变其背景