利用OpenGL生成纹理并绑定到SurfaceTexture上,然后把Camera的预览数据设置到SurfaceTexture中,OpenGL拿到摄像头数据并显示出来。
1. 顶点与片元着色器
片元着色器:
#extension GL_OES_EGL_image_external:require
precision mediump float;
varying vec2 a_position;
uniform samplerExternalOES vTexture;
void main(){
gl_FragColor = texture2D(vTexture,a_position);
}
- 注意前面要加 #extension GL_OES_EGL_image_external:require声明。
- 之前的sampler2D 变成了 samplerExternalOES
顶点着色器:
attribute vec4 v_position;
attribute vec2 f_position;
varying vec2 a_position;
void main(){
gl_Position = v_position;
a_position = f_position;
}
2. 创建Program
mProgram = ShaderUtil.createProgram(ShaderUtil.getRawResource(mContext, R.raw.demo2_vertex_shader),
ShaderUtil.getRawResource(mContext, R.raw.demo2_fragment_shader));
if (mProgram == 0){
throw new RuntimeException("create program fail");
}
v_position = GLES20.glGetAttribLocation(mProgram, "v_position");
f_positon = GLES20.glGetAttribLocation(mProgram, "f_position");
3. 生成并绑定纹理
int[] textures = new int[1];
GLES20.glGenTextures(1, textures, 0);
textureId = textures[0];
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
//创建Camera预览的SurfaceTexture
mSurfaceTexture = new SurfaceTexture(textureId);
if (mOnSurfaceCreateListener != null){
mOnSurfaceCreateListener.onSurfaceCreate(mSurfaceTexture);
}
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
需要注意使用的是GLES11Ext.GL_TEXTURE_EXTERNAL_OES
:
GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
在创建纹理后我们将纹理id作为SurfaceTexture的构造参数传入,并通过回调通知外层处理Camera:
mSurfaceTexture = new SurfaceTexture(textureId);
if (mOnSurfaceCreateListener != null){
mOnSurfaceCreateListener.onSurfaceCreate(mSurfaceTexture);
}
4. 刷新显示
//将纹理图像更新为图像流中的最新帧。只有在拥有纹理的OpenGL ES上下文在调用线程上是最新的时,才 可以调用此方法。
//它将隐式地将其纹理绑定到GL_TEXTURE_EXTERNAL_OES纹理目标
mSurfaceTexture.updateTexImage();
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glClearColor(1.0f, 0, 0, 1f);
GLES20.glUseProgram(mProgram);
GLES20.glEnableVertexAttribArray(v_position);
GLES20.glVertexAttribPointer(v_position, 2, GLES20.GL_FLOAT, false, 8, mVertexBuffer);
GLES20.glEnableVertexAttribArray(f_positon);
GLES20.glVertexAttribPointer(f_positon, 2, GLES20.GL_FLOAT, false, 8, mFragmentBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glBindTexture(mProgram, 0);
注意要先调用纹理图像更新为图像流中的最新帧。:
mSurfaceTexture.updateTexImage();
外部GLSurfaceView处理逻辑:
public Demo2SurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
setEGLContextClientVersion(2);
Demo2Renderer demo2Renderer = new Demo2Renderer(context);
//Camera 管理类
mDemo2Camera = new Demo2Camera(context);
demo2Renderer.setOnSurfaceCreateListener(new Demo2Renderer.OnSurfaceCreateListener() {
@Override
public void onSurfaceCreate(SurfaceTexture surfaceTexture) {
mDemo2Camera.initCamera(surfaceTexture, cameraId);
//注册当一个新的图像帧可用于SurfaceTexture时要调用的回调。
surfaceTexture.setOnFrameAvailableListener(Demo2SurfaceView.this);
}
});
setRenderer(demo2Renderer);
setRenderMode(RENDERMODE_WHEN_DIRTY);
}
public void onDestory() {
if (mDemo2Camera != null){
mDemo2Camera.stopPreview();
}
}
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
//手动请求刷新
requestRender();
}
再来看下这个Demo2Camera管理类:
public Demo2Camera(Context context){
this.width = DisplayUtil.getScreenWidth(context);
this.height = DisplayUtil.getScreenHeight(context);
}
public void initCamera(SurfaceTexture surfaceTexture,int cameraId){
this.mSurfaceTexture = surfaceTexture;
setCameraParm(cameraId);
}
private void setCameraParm( int cameraId) {
try {
mCamera = Camera.open(cameraId);
//将SurfaceTexture设置为预览surface
mCamera.setPreviewTexture(mSurfaceTexture);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setFlashMode("off");
parameters.setPreviewFormat(ImageFormat.NV21);
Camera.Size size = getFitSize(parameters.getSupportedPictureSizes());
parameters.setPictureSize(size.width, size.height);
size = getFitSize(parameters.getSupportedPreviewSizes());
parameters.setPreviewSize(size.width, size.height);
mCamera.setParameters(parameters);
//开始预览
mCamera.startPreview();
}catch (Exception e){
e.printStackTrace();
}
}
private Camera.Size getFitSize(List<Camera.Size> sizes) {
if(width < height) {
int t = height;
height = width;
width = t;
}
for(Camera.Size size : sizes) {
if(1.0f * size.width / size.height == 1.0f * width / height) {
return size;
}
}
return sizes.get(0);
}
关键处理逻辑在:
mCamera.setPreviewTexture(mSurfaceTexture);
此时将Camera、SurfaceTexture、OpenGL连接在一起了,看下预览的画面。
what? 现在显示怎么是这样的?方向歪到姥姥家了,咳咳,那下面开始调整方向。
5. 调整预览方向
改造顶点着色器:
attribute vec4 v_position;
attribute vec2 f_position;
uniform mat4 uMatrix;
varying vec2 a_position;
void main(){
gl_Position = v_position * uMatrix;
a_position = f_position;
}
增加矩阵:
uniform mat4 uMatrix;
Renderer构造方法中置空矩阵:
Matrix.setIdentityM(matrix, 0);
查找到矩阵对应着色器中的id:
uMatrix = GLES20.glGetUniformLocation(mProgram, "uMatrix");
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
GLES20.glViewport(0, 0, width, height);
setAngle(-90, 0, 0, 1);
setAngle(180, 1, 0, 0);
setAngle(180, 0, 1, 0);
}
public void setAngle(float angle, float x, float y, float z){
Matrix.rotateM(matrix, 0, angle, x, y, z);
}
Matrix.rotateM(matrix, o, a, x, y, z):
a:
正数:逆时针旋转
负数:顺时针旋转
x、y、z:分别表示相应坐标轴
矩阵赋值:
...
GLES20.glUseProgram(mProgram);
GLES20.glUniformMatrix4fv(uMatrix, 1, false, matrix, 0);
...