美文网首页影视处理
Camera2 教程 1:OpenglEs 显示一张四方形图像

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

作者: 古风子 | 来源:发表于2020-03-22 18:14 被阅读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

    参考文章

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

    相关文章

      网友评论

        本文标题:Camera2 教程 1:OpenglEs 显示一张四方形图像

        本文链接:https://www.haomeiwen.com/subject/lzspyhtx.html