if (isCreate) {
return;
}
onCreatePre();
onClearColor();
onInitBlend();
onInitVertexBuffer();
onInitCoordinateBuffer();
onInitVbo();
onInitProgram();
onCreateAfter();
isCreate = true;
}
@Override
public void onChange(int width, int height) {
if (isChange) {
return;
}
onChangePre();
setWidth(width);
setHeight(height);
onViewport();
onInitFbo();
onChangeAfter();
isChange = true;
}
@Override
public void onDraw(int textureId) {
if (!onReadyToDraw()) {
return;
}
onDrawPre();
onClear();
onUseProgram();
onInitLocation();
onBindFbo();
onBindVbo();
onActiveTexture(textureId);
onEnableVertexAttributeArray();
onSetVertexData();
onSetCoordinateData();
onSetOtherData();
onDraw();
onDisableVertexAttributeArray();
onUnBind();
onDrawAfter();
}
@Override
public void onRelease() {
onDeleteProgram(program);
onDeleteShader(vertexShader);
onDeleteShader(fragShader);
onDeleteTexture(textureId);
onDeleteTexture(fboTextureId);
onDeleteFbo(fboId);
onDeleteVbo(vboId);
}
/**
- 创建之前
*/
public void onCreatePre() {
}
/**
- 设置背景颜色
*/
public void onClearColor() {
GLES20.glClearColor(0, 0, 0, 1);
}
/**
- 是否启用混色
*/
public boolean onEnableBlend() {
return false;
}
/**
- 初始化混色
*/
private void onInitBlend() {
if (!onEnableBlend()) {
return;
}
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
}
/**
- 初始化顶点坐标
*/
public void onInitVertexBuffer() {
vertexBuffer = OpenGLESUtils.getSquareVertexBuffer();
}
/**
- 初始化纹理坐标
*/
public void onInitCoordinateBuffer() {
if (isBindFbo) {
coordinateBuffer = OpenGLESUtils.getSquareCoordinateReverseBuffer();
} else {
coordinateBuffer = OpenGLESUtils.getSquareCoordinateBuffer();
}
}
/**
- 初始化Vbo
*/
public void onInitVbo() {
vboId = OpenGLESUtils.getVbo(vertexBuffer, coordinateBuffer);
}
/**
- 初始化Program
*/
public void onInitProgram() {
String vertexShaderCode = OpenGLESUtils.getShaderCode(context, vertexFilename);
String fragShaderCode = OpenGLESUtils.getShaderCode(context, fragFilename);
vertexShader = OpenGLESUtils.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
fragShader = OpenGLESUtils.loadShader(GLES20.GL_FRAGMENT_SHADER, fragShaderCode);
program = OpenGLESUtils.linkProgram(vertexShader, fragShader);
}
/**
- 创建之后
*/
public void onCreateAfter() {
}
/**
- 设置尺寸之前
*/
public void onChangePre() {
}
/**
- 设置窗口尺寸
*/
public void onViewport() {
GLES20.glViewport(0, 0, width, height);
}
/**
- 初始化Fbo
*/
public void onInitFbo() {
if (!isBindFbo) {
return;
}
int[] fboData = OpenGLESUtils.getFbo(width, height);
fboId = fboData[0];
fboTextureId = fboData[1];
}
/**
- 设置尺寸之后
*/
public void onChangeAfter() {
}
/**
- 绘制之前的准备
*/
public boolean onReadyToDraw() {
return true;
}
/**
- 绘制之前
*/
public void onDrawPre() {
}
/**
- 清屏
*/
public void onClear() {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
}
/**
- 使用Program
*/
public void onUseProgram() {
GLES20.glUseProgram(program);
}
/**
- 初始化着色器各个位置
*/
public void onInitLocation() {
aPosLocation = GLES20.glGetAttribLocation(program, “aPos”);
aCoordinateLocation = GLES20.glGetAttribLocation(program, “aCoordinate”);
uSamplerLocation = GLES20.glGetUniformLocation(program, “uSampler”);
}
/**
- 绑定Fbo
*/
public void onBindFbo() {
if (!isBindFbo) {
return;
}
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, fboId);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
GLES20.GL_TEXTURE_2D, fboTextureId, 0);
GLES20.glViewport(0, 0, width, height);
}
/**
- 绑定Vbo
*/
public void onBindVbo() {
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboId);
}
/**
- 激活并绑定纹理
*/
public void onActiveTexture(int textureId) {
this.textureId = textureId;
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(uSamplerLocation, 0);
}
/**
- 启用顶点坐标
*/
public void onEnableVertexAttributeArray() {
GLES20.glEnableVertexAttribArray(aPosLocation);
GLES20.glEnableVertexAttribArray(aCoordinateLocation);
}
/**
- 设置顶点坐标
*/
public void onSetVertexData() {
GLES20.glVertexAttribPointer(aPosLocation, vertexSize, GLES20.GL_FLOAT, false, vertexStride, 0);
}
/**
- 设置纹理坐标
*/
public void onSetCoordinateData() {
GLES20.glVertexAttribPointer(aCoordinateLocation, coordinateSize, GLES20.GL_FLOAT, false, coordinateStride, vertexBuffer.limit() * 4);
}
/**
- 设置其他数据
*/
public void onSetOtherData() {
}
/**
- 绘制
*/
public void onDraw() {
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, vertexCount);
}
/**
- 禁用顶点坐标
*/
public void onDisableVertexAttributeArray() {
GLES20.glDisableVertexAttribArray(aPosLocation);
GLES20.glDisableVertexAttribArray(aCoordinateLocation);
}
/**
- 解除绑定
*/
public void onUnBind() {
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
}
/**
- 绘制之后
*/
public void onDrawAfter() {
}
/**
- 删除Program
*/
public void onDeleteProgram(int program) {
GLES20.glDeleteProgram(program);
}
/**
- 删除Shader
*/
public void onDeleteShader(int shader) {
GLES20.glDeleteShader(shader);
}
/**
- 删除纹理
*/
public void onDeleteTexture(int textureId) {
GLES20.glDeleteTextures(1, new int[]{textureId}, 0);
}
/**
- 删除Fbo
*/
public void onDeleteFbo(int fboId) {
GLES20.glDeleteFramebuffers(1, new int[]{fboId}, 0);
}
/**
- 删除Vbo
*/
public void onDeleteVbo(int vboId) {
GLES20.glDeleteBuffers(1, new int[]{vboId}, 0);
}
public Context getContext() {
return context;
}
public void setContext(Context context) {
this.context = context;
}
public FloatBuffer getVertexBuffer() {
return vertexBuffer;
}
public void setVertexBuffer(FloatBuffer vertexBuffer) {
this.vertexBuffer = vertexBuffer;
}
public FloatBuffer getCoordinateBuffer() {
return coordinateBuffer;
}
public void setCoordinateBuffer(FloatBuffer coordinateBuffer) {
this.coordinateBuffer = coordinateBuffer;
}
public int getVertexSize() {
return vertexSize;
}
public void setVertexSize(int vertexSize) {
this.vertexSize = vertexSize;
}
public int getCoordinateSize() {
return coordinateSize;
}
public void setCoordinateSize(int coordinateSize) {
this.coordinateSize = coordinateSize;
}
public int getVertexStride() {
return vertexStride;
}
public void setVertexStride(int vertexStride) {
this.vertexStride = vertexStride;
}
public int getCoordinateStride() {
return coordinateStride;
}
public void setCoordinateStride(int coordinateStride) {
this.coordinateStride = coordinateStride;
}
public int getVertexCount() {
return vertexCount;
}
public void setVertexCount(int vertexCount) {
this.vertexCount = vertexCount;
}
public int getCoordinateCount() {
return coordinateCount;
}
public void setCoordinateCount(int coordinateCount) {
this.coordinateCount = coordinateCount;
}
public int getProgram() {
return program;
}
public void setProgram(int program) {
this.program = program;
}
public int getFboTextureId() {
return fboTextureId;
}
public void setFboTextureId(int fboTextureId) {
this.fboTextureId = fboTextureId;
}
public int getFboId() {
return fboId;
}
public void setFboId(int fboId) {
this.fboId = fboId;
}
public int getVboId() {
return vboId;
}
public void setVboId(int vboId) {
this.vboId = vboId;
}
public String getVertexFilename() {
return vertexFilename;
}
public void setVertexFilename(String vertexFilename) {
this.vertexFilename = vertexFilename;
}
public String getFragFilename() {
return fragFilename;
}
public void setFragFilename(String fragFilename) {
this.fragFilename = fragFilename;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public boolean isBindFbo() {
return isBindFbo;
}
public void setBindFbo(boolean bindFbo) {
isBindFbo = bindFbo;
}
public int getPosLocation() {
return aPosLocation;
}
public void setPosLocation(int aPosLocation) {
this.aPosLocation = aPosLocation;
}
public int getCoordinateLocation() {
return aCoordinateLocation;
}
public void setCoordinateLocation(int aCoordinateLocation) {
this.aCoordinateLocation = aCoordinateLocation;
}
public int getSamplerLocation() {
return uSamplerLocation;
}
public void setSamplerLocation(int uSamplerLocation) {
this.uSamplerLocation = uSamplerLocation;
}
public boolean isCreate() {
return isCreate;
}
public void setCreate(boolean create) {
isCreate = create;
}
public boolean isChange() {
return isChange;
}
public void setChange(boolean change) {
isChange = change;
}
public BaseRenderBean getRenderBean() {
return renderBean;
}
public void setRenderBean(BaseRenderBean renderBean) {
this.renderBean = renderBean;
}
public void updateRenderBean(BaseRenderBean renderBean) {
setRenderBean(renderBean);
}
}
代码有点长,但是里面尽可能地考虑到了渲染和扩展的需求
顶点着色器 vertex.frag
attribute vec4 aPos;
attribute vec2 aCoordinate;
varying vec2 vCoordinate;
void main(){
vCoordinate = aCoordinate;
gl_Position = aPos;
}
片元着色器 frag.frag
precision mediump float;
uniform sampler2D uSampler;
varying vec2 vCoordinate;
void main(){
gl_FragColor = texture2D(uSampler, vCoordinate);
}
注意到,里面有用到一个工具类OpenGLESUtils和实体类BaseRenderBean具体就不贴出来了,可以到Github
上查看
四、BaseOesRender
注意到,BaseRender
里面绑定的纹理是2D纹理
,而如果想实现相机预览,则需要使用Oes纹理
,所以需要创建一个BaseOesRender
为相机做渲染
package com.yk.media.opengles.render.base;
import android.content.Context;
import android.graphics.SurfaceTexture;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import com.yk.media.utils.OpenGLESUtils;
public class BaseOesRender extends BaseRender {
/**
- oes纹理id
*/
private int oesTextureId;
/**
- 顶点变换矩阵位置
*/
private int uMatrixLocation;
/**
- 纹理变换矩阵位置
*/
private int uOesMatrixLocation;
/**
- oes尺寸
*/
private int oesW = -1;
private int oesH = -1;
/**
- 顶点变换矩阵
*/
private float[] mMVPMatrix = new float[16];
/**
- 纹理变换矩阵
*/
private float[] mOesMatrix = {
1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
0, 0, 0, 1
};
/**
- 是否准备好绘制
*/
private boolean isReadyToDraw = false;
/**
- SurfaceTexture
*/
private SurfaceTexture surfaceTexture;
/**
- SurfaceTexture回调
*/
private OnSurfaceTextureListener onSurfaceTextureListener;
public BaseOesRender(Context context) {
super(context, “render/base/oes/vertex.frag”, “render/base/oes/frag.frag”);
setBindFbo(true);
oesTextureId = OpenGLESUtils.getOesTexture();
}
@Override
public void onInitCoordinateBuffer() {
setCoordinateBuffer(OpenGLESUtils.getSquareCoordinateBuffer());
}
@Override
public boolean onReadyToDraw() {
if (!isReadyToDraw) {
if (onSurfaceTextureListener != null) {
if (surfaceTexture != null) {
surfaceTexture.release();
surfaceTexture = null;
}
surfaceTexture = new SurfaceTexture(oesTextureId);
onSurfaceTextureListener.onSurfaceTexture(surfaceTexture);
isReadyToDraw = true;
} else if (surfaceTexture != null) {
surfaceTexture.attachToGLContext(oesTextureId);
isReadyToDraw = true;
} else {
return false;
}
}
return oesW != -1 && oesH != -1;
}
@Override
public void onDrawPre() {
super.onDrawPre();
mMVPMatrix = OpenGLESUtils.getMatrix(getWidth(), getHeight(), oesW, oesH);
surfaceTexture.updateTexImage();
float[] oesMatrix = new float[16];
surfaceTexture.getTransformMatrix(oesMatrix);
if (!OpenGLESUtils.isIdentityM(oesMatrix)) {
mOesMatrix = oesMatrix;
}
}
@Override
public void onInitLocation() {
super.onInitLocation();
uMatrixLocation = GLES20.glGetUniformLocation(getProgram(), “uMatrix”);
uOesMatrixLocation = GLES20.glGetUniformLocation(getProgram(), “uOesMatrix”);
}
@Override
public void onActiveTexture(int textureId) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES20.glUniform1i(getSamplerLocation(), 0);
}
@Override
public void onSetOtherData() {
super.onSetOtherData();
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, mMVPMatrix, 0);
GLES20.glUniformMatrix4fv(uOesMatrixLocation, 1, false, mOesMatrix, 0);
}
@Override
public void onRelease() {
super.onRelease();
onDeleteTexture(oesTextureId);
}
/**
- 绘制
*/
public void onDrawSelf() {
super.onDraw(oesTextureId);
}
/**
- 设置oes尺寸
*/
public void setOesSize(int width, int height) {
oesW = width;
oesH = height;
}
/**
- 设置SurfaceTexture
*/
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
this.surfaceTexture = surfaceTexture;
isReadyToDraw = false;
}
/**
- 设置SurfaceTexture回调
*/
public void setOnSurfaceTextureListener(OnSurfaceTextureListener onSurfaceTextureListener) {
this.onSurfaceTextureListener = onSurfaceTextureListener;
isReadyToDraw = false;
}
}
顶点着色器 vertex.frag
attribute vec4 aPos;
attribute vec4 aCoordinate;
uniform mat4 uMatrix;
uniform mat4 uOesMatrix;
varying vec2 vCoordinate;
void main(){
vCoordinate = (uOesMatrix * aCoordinate).xy;
gl_Position = uMatrix * aPos;
}
片元着色器 frag.frag
#extension GL_OES_EGL_image_external:require
precision mediump float;
uniform samplerExternalOES uSampler;
varying vec2 vCoordinate;
void main(){
gl_FragColor = texture2D(uSampler, vCoordinate);
}
BaseOesRender
是继承BaseRender
的
加入了一些变量
-
oesW
和oesH
用于计算矩阵
mMVPMatrix
通过width、height、oesW、oesH
计算的矩阵
mOesMatrix
通过SurfaceTexture
获取的矩阵
isReadyToDraw
是否准备好渲染
重写了一些方法
onInitCoordinateBuffer
BaseOesRender
需要绑定FBO
,所有纹理坐标需要设置好,不然会出现倒立
onReadyToDraw
此处做一些SurfaceTexture
绑定纹理和回调
onDrawPre
此处做矩阵相关的计算
onInitLocation
获取矩阵的Location
onActiveTexture
前面有说到,预览相机使用的是SurfaceTexture
,所有需要修改绑定为Oes纹理
onSetOtherData
此处传入矩阵的数据
onRelease
此处做一些释放资源的工作
OnSurfaceTextureListener
public interface OnSurfaceTextureListener {
/**
- SurfaceTexture回调
*/
void onSurfaceTexture(SurfaceTexture surfaceTexture);
}
onDrawSelf
public void onDrawSelf() {
super.onDraw(oesTextureId);
}
因为oesTextureId
是内部创建的,故渲染的话,直接调用super.onDraw(oesTextureId)
,方便外部调用
五、OesRender
前面有说过,EGLTextureView
的setRenderer
只能调用一次,故才会创建BaseRender
,现在来创建Renderer
调度者
public class OesRender implements Renderer {
private Context context;
/**
- 输入(FBO保存数据)
*/
private BaseOesRender inputRender;
/**
- 输出(屏幕显示)
*/
private BaseRender outputRender;
private int width;
private int height;
public OesRender(Context context) {
this.context = context;
inputRender = new BaseOesRender(context);
outputRender = new BaseRender(context);
}
@Override
public void onCreate() {
inputRender.onCreate();
outputRender.onCreate();
}
@Override
public void onChange(int width, int height) {
this.width = width;
this.height = height;
inputRender.onChange(width, height);
outputRender.onChange(width, height);
}
@Override
public void onDraw() {
inputRender.onDrawSelf();
outputRender.onDraw(inputRender.getFboTextureId());
}
public void setOesSize(int width, int height) {
inputRender.setOesSize(width, height);
}
public void setOnSurfaceTextureListener(OnSurfaceTextureListener onSurfaceTextureListener) {
inputRender.setOnSurfaceTextureListener(onSurfaceTextureListener);
}
public void setSurfaceTexture(SurfaceTexture surfaceTexture) {
inputRender.setSurfaceTexture(surfaceTexture);
}
public int getFboTextureId() {
return inputRender.getFboTextureId();
}
}
注意到,OesRender
里面,有两个Render
BaseOesRender
渲染Oes纹理
,并绑定了Fbo
BaseRender
做为输出显示
六、CameraView
完成了上面的工作,接下来就可以进入正题,如何在Android
中,使用OpenGLES + Camera1
实现相机预览?
既然我们已经完成了“容器”和Renderer
的创建,那么下面的工作就比较轻松
还记得上一章的Android Camera1相机预览,不记得的同学可以点击链接进入查看,因为接下来需要用到上一章介绍的CameraManager
public class CameraView extends EGLTextureView implements OnCameraListener {
private final CameraManager cameraManager = new CameraManager();
private Activity activity;
public OesRender render;
public CameraView(Context context) {
this(context, null);
}
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
private void init(Context context) {
setEGLContextClientVersion(2);
render = new OesRender(context);
setRenderer(render);
setRenderMode(EGLTextureView.RENDERMODE_WHEN_DIRTY);
activity = (Activity) context;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
【Android高级架构视频学习资源】文章来源:https://www.toymoban.com/news/detail-848831.html
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!文章来源地址https://www.toymoban.com/news/detail-848831.html
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
.(img-8pVlIYcc-1711756366555)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
上面分享的百度、腾讯、网易、字节跳动、阿里等公司2021年的高频面试题,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,上面只是以图片的形式给大家展示一部分。
【Android思维脑图(技能树)】
知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。
[外链图片转存中…(img-afVEsosx-1711756366556)]
【Android高级架构视频学习资源】
**Android部分精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门即可获取!
到了这里,关于Android OpenGLES + Camera1 相机预览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!