Camera2 教程 1:OpenglEs 显示一张四方形图像

青橙相机

程序创建步骤如下:

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

参考文章

https://github.com/afei-cn/GPUImageDemo

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值