社区微信群开通啦,扫一扫抢先加入社区官方微信群
社区微信群
前一阵子通过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.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.hyq.hm.glesrotate3d.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="180"
android:layout_margin="20dp"/>
<SurfaceView
android:id="@+id/surface_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</android.support.constraint.ConstraintLayout>
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!