Camera2 教程 1:OpenglEs 显示一张四方形图像
2020-03-22 本文已影响0人
古风子
青橙相机
程序创建步骤如下:
1 在xml中添加GlSurfaceView
2 创建渲染器类实现GlSurfaceView.Renderer
2.1 创建顶点坐标和纹理坐标
2.2 创建坐标数据缓冲对象
2.3 实现onSurfaceCreated方法
2.4 实现onSurfaceChanged
2.5 实现onDrawFrame
2.6 获取图片纹理ID
2.7 执行渲染图片
3 调用render执行图片渲染
4 显示结果
5 流程总结
1 在xml中添加GlSurfaceView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#999999">
<android.opengl.GLSurfaceView
android:id="@+id/surfaceView"
android:layout_width="@dimen/image_common_size"
android:layout_height="@dimen/image_common_size"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="24dp" />
</RelativeLayout>
在Activity中获取GlSurfaceView
package com.jdf.camera.activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.LinearLayout;
import com.jdf.base.filterlayout.FilterItem;
import com.jdf.camera.R;
import java.util.List;
public class PicFilterBaseActivity extends BaseActivity{
private GLSurfaceView mGLSurfaceView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_texture);
mGLSurfaceView = findViewById(R.id.surfaceView);
}
}
2 创建渲染器类实现GlSurfaceView.Renderer
2.1 创建顶点坐标和纹理坐标
//GImageRenderer.java
//顶点坐标
public static final float CUBE[] = {
-1.0f, -1.0f,//v0
1.0f, -1.0f,//v1
-1.0f, 1.0f,//v2
1.0f, 1.0f,//v3
};
//纹理坐标
public static final float TEXTURE_NO_ROTATION[] = {
0.0f, 1.0f,//t0
1.0f, 1.0f,//t1
0.0f, 0.0f,//t2
1.0f, 0.0f,//t3
};
两者的映射关系为:
纹理映射如上图,在Android中,纹理的坐标原点在左上角,而opengl的屏幕显示坐标系的中心点在屏幕中心位置;纹理和顶点的坐标位置,要一一对应,这样,渲染出来的图片才是正常显示
2.2 创建坐标数据缓冲对象
在GImageRenderer对应创建的时候,为顶点坐标和纹理坐标分配FloatBuffer
public JGPUImageRenderer(final JGPUImageFilter filter) {
this.filter = filter;
//为顶点坐标添加分配buffer对象
glCubeBuffer = ByteBuffer.allocateDirect(CUBE.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
glCubeBuffer.put(CUBE).position(0);
//为纹理坐标添加buffer对象
glTextureBuffer = ByteBuffer.allocateDirect(TEXTURE_NO_ROTATION.length * 4)
.order(ByteOrder.nativeOrder())
.asFloatBuffer();
}
JGPUImageFilter 里面实现对默认顶点作色器和片元着色器的处理
至此,前期的准备工作做好了,我们为要显示的GLSurfaceView设置渲染对象
2.3 实现onSurfaceCreated方法
@Override
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
//清楚屏幕颜色缓存
GLES20.glClearColor(0, 0, 0, 1);
//禁用深度监测
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
// //使用面剔除,不可见的区域,不进行绘制操作
// GLES20.glEnable(GL10.GL_CULL_FACE);
// //启用了之后,OpenGL在绘制的时候就会检查,当前像素前面是否有别的像素,如果别的像素挡道了它,那它就不会绘制,也就是说,OpenGL就只绘制最前面的一层
// GLES20.glEnable(GL10.GL_DEPTH_TEST);
filter.ifNeedInit();
}
filter.ifNeedInit();完成对着色器的程序的创建,
//GPUImageFilter.java
//默认的顶点着色器
public static final String NO_FILTER_VERTEX_SHADER = "" +
"attribute vec4 position;\n" +
"attribute vec4 inputTextureCoordinate;\n" +
" \n" +
"varying vec2 textureCoordinate;\n" +
" \n" +
"void main()\n" +
"{\n" +
" gl_Position = position;\n" +
" textureCoordinate = inputTextureCoordinate.xy;\n" +
"}";
//默认的片元着色器
public static final String NO_FILTER_FRAGMENT_SHADER = "" +
"varying highp vec2 textureCoordinate;\n" +
" \n" +
"uniform sampler2D inputImageTexture;\n" +
" \n" +
"void main()\n" +
"{\n" +
" gl_FragColor = texture2D(inputImageTexture, textureCoordinate);\n" +
"}";
public void ifNeedInit() {
if (!isInitialized) init();
}
private final void init() {
onInit();
onInitialized();
}
public void onInit() {
//根据传入的作色器字符串,创建,链接,编译对象,返回程序对象id
glProgId = OpenGlUtils.loadProgram(vertexShader, fragmentShader);
//获取程序中顶点位置属性引用
glAttribPosition = GLES20.glGetAttribLocation(glProgId, "position");
//获取程序中纹理内容属性引用
glUniformTexture = GLES20.glGetUniformLocation(glProgId, "inputImageTexture");
//获取程序中顶点纹理坐标属性引用
glAttribTextureCoordinate = GLES20.glGetAttribLocation(glProgId, "inputTextureCoordinate");
//标明指初始化一次
isInitialized = true;
}
public void onInitialized() {
}
2.4 实现onSurfaceChanged
@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
//surface显示大小
outputWidth = width;
outputHeight = height;
///设置视窗大小及位置
GLES20.glViewport(0, 0, width, height);
//使用已创建的程序
GLES20.glUseProgram(filter.getProgram());
//向gpu处理对象中传入surface大小
filter.onOutputSizeChanged(width, height);
//根据surface大小,缩放图片,使得图片和GlSurfaceView显示大小一致
adjustImageScaling();
}
2.5 实现onDrawFrame
@Override
public void onDrawFrame(GL10 gl) {
//清楚上一帧颜色缓存和深度缓存
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//调用默认着色器处理图片
filter.onDraw(glTextureId, glCubeBuffer, glTextureBuffer);
}
//JGPUImageFilter.java
public void onDraw(final int textureId, final FloatBuffer cubeBuffer,
final FloatBuffer textureBuffer) {
GLES20.glUseProgram(glProgId);
runPendingOnDrawTasks();
if (!isInitialized) {
return;
}
cubeBuffer.position(0);
GLES20.glVertexAttribPointer(glAttribPosition, 2, GLES20.GL_FLOAT, false, 0, cubeBuffer);
GLES20.glEnableVertexAttribArray(glAttribPosition);
textureBuffer.position(0);
GLES20.glVertexAttribPointer(glAttribTextureCoordinate, 2, GLES20.GL_FLOAT, false, 0,
textureBuffer);
GLES20.glEnableVertexAttribArray(glAttribTextureCoordinate);
if (textureId != OpenGlUtils.NO_TEXTURE) {
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
GLES20.glUniform1i(glUniformTexture, 0);
}
onDrawArraysPre();
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
GLES20.glDisableVertexAttribArray(glAttribPosition);
GLES20.glDisableVertexAttribArray(glAttribTextureCoordinate);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
}
glTextureId表示传入的图片生成的id,我们来看下,怎么获取传入的图片的纹理ID
2.6 获取图片纹理ID
public void setImageBitmap(final Bitmap bitmap, final boolean recycle) {
if (bitmap == null) {
return;
}
runOnDraw(new Runnable() {
@Override
public void run() {
Bitmap resizedBitmap = null;
if (bitmap.getWidth() % 2 == 1) {
resizedBitmap = Bitmap.createBitmap(bitmap.getWidth() + 1, bitmap.getHeight(),
Bitmap.Config.ARGB_8888);
Canvas can = new Canvas(resizedBitmap);
can.drawARGB(0x00, 0x00, 0x00, 0x00);
can.drawBitmap(bitmap, 0, 0, null);
addedPadding = 1;
} else {
addedPadding = 0;
}
glTextureId = OpenGlUtils.loadTexture(
resizedBitmap != null ? resizedBitmap : bitmap, glTextureId, recycle);
if (resizedBitmap != null) {
resizedBitmap.recycle();
}
imageWidth = bitmap.getWidth();
imageHeight = bitmap.getHeight();
adjustImageScaling();
}
});
}
protected void runOnDraw(final Runnable runnable) {
synchronized (runOnDraw) {
runOnDraw.add(runnable);
}
}
主要是创建一个任务队列,在onDrawFrame执行的时候,从该队列中取出任务,执行,生成纹理id
2.7 执行渲染图片
@Override
public void onDrawFrame(final GL10 gl) {
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
//调用队列任务
runAll(runOnDraw);
filter.onDraw(glTextureId, glCubeBuffer, glTextureBuffer);
}
private void runAll(Queue<Runnable> queue) {
synchronized (queue) {
while (!queue.isEmpty()) {
queue.poll().run();
}
}
}
3 调用render执行图片渲染
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_texture);
mGLSurfaceView = findViewById(R.id.surfaceView);
JGPUImageFilter normalFilter = new JGPUImageFilter();
JGPUImageRenderer renderer = new JGPUImageRenderer(normalFilter);
setGLSurfaceViewRender(renderer);
renderer.setImageBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.texture));
}
public void setGLSurfaceViewRender(JGPUImageRenderer renderer) {
//从布局文件中,获取GLSurfaceView对象
mGLSurfaceView.setEGLContextClientVersion(2);
//设置颜色缓存为RGBA,位数为8888
mGLSurfaceView.setEGLConfigChooser(8, 8, 8, 8, 16, 0);
mGLSurfaceView.getHolder().setFormat(PixelFormat.RGBA_8888);
//设置GLSurfaceView的render
mGLSurfaceView.setRenderer(renderer);
mGLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
//请求刷新GLSurfaceView
mGLSurfaceView.requestRender();
}
4 显示结果
opengl渲染图片后续,我们通过改变JGPUImageFilter的顶点着色器和片元作色器,实现不同的滤镜效果
5 流程总结
图片渲染流程6 代码地址
后续关于相机滤镜相关代码,都将在此分支更新
https://github.com/jdf-eng/QCCamera/tree/GpuImage