Desarrollo de Android OpenGL: vista previa detallada de la cámara (con demostración)

En el pasado, la compañía necesitaba personalizar la cámara. Antes usaba un tercero. Más tarde, la demanda cambió. El tercero no admite agregar pegatinas dinámicas, por lo que solo puede expandirse por sí mismo. Por supuesto, hay muchos ejemplos en Internet, pero casi no hay ejemplos de agregar pegatinas dinámicas. De todos modos, no lo encontré (quiero llorar sin lágrimas). Por supuesto, todas las funciones ahora están implementadas. Creo que openGl es bastante interesante, así que lo resolveré nuevamente desde el principio y planeo organizar una serie de aprendizaje de OpenGL. Para referencia de aprendizaje. Si no sabe nada sobre OpenGl, se recomienda leer el desarrollo de #Android OpenGL: explicación detallada del dibujo de la imagen Enlace completo del proyecto: AndroidCamera

Bueno, sin más preámbulos, ingresemos el texto:

Puede aprender los siguientes conocimientos de este artículo:

  1. Las ventajas, desventajas y diferencias de SurfaceView, GlSurfaceView, SurfaceTexture, TextureView.
  2. Cómo mostrar la vista previa de la cámara a través de SurfaceView.
  3. Cómo mostrar la vista previa de la cámara a través de TextureView.
  4. Cómo manejar la vista previa de la cámara a través de GlSurfaceView.
  5. Resumen del artículo
  6. Enlace fuente

1. Ventajas, desventajas y diferencias de SurfaceView, GlSurfaceView, SurfaceTexture, TextureView

Vista de superficie

Heredado de View, tiene la mayoría de las propiedades de View, pero no puede establecer la transparencia debido a la existencia de un titular. Ventajas: se puede dibujar en un hilo separado, sin afectar el hilo principal, utilizando un mecanismo de doble búfer, la pantalla es más fluida cuando se reproduce un video. No se puede anidar.

GlSurfaceView

GlSurfaceView hereda de la clase SurfaceView, que se usa especialmente para mostrar el renderizado OpenGL.Es fácil de entender que puede mostrar videos, imágenes y escenas 3D.

Textura de superficie

和SurfaceView功能类似,区别是,SurfaceTexure可以不显示在界面中。使用OpenGl对图片流进行美化,添加水印,滤镜这些操作的时候我们都是通过SurfaceTexre去处理,处理完之后再通过GlSurfaceView显示。缺点,可能会导致个别帧的延迟。本身管理着BufferQueue,所以内存消耗会多一点。

TextureView

同样继承自View,必须在开启硬件加速的设备中使用(保守估计目前百分之九十的Android设备都开启了),TextureView通过setSurfaceTextureListener的回调在子线程中进行更新UI. 优点:支持动画效果。 缺点:在5.0之前在主线程渲染,在5.0之后在单独线程渲染。

TextureView SurfaceView
绘制 稍微延时 及时
内存
动画 支持 不支持
耗电
适用场景(推荐) 视频播放,相机应用 大量画布更新(游戏绘制)

2. 如何通过SurfaceView显示Camera预览。

基本步骤

  1. 在xml文件中设置SurfaceView 。
  2. 实现SurfaceHolder.Callback的回调。
  3. 打开摄像头Camera.open(0);
  4. 设置摄像头相关参数;
  5. 将摄像头数据设置到SurfaceView中,并开启预览。

代码部分

<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"  tools:context="com.aserbao.aserbaosandroid.opengl.openGlCamera.simpleCameraOpengl.simpleCamera.CameraSurfaceViewShowActivity">
    <FrameLayout
        android:id="@+id/frame_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <SurfaceView
                    android:layout_width="match_parent"
                     android:layout_height="match_parent"
                     android:id="@+id/mSurface" />
        <Button
            android:id="@+id/btn_change"
            android:text="给动画,SurfaceView不支持设置透明度"
            android:textAllCaps="false"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </FrameLayout>
</android.support.constraint.ConstraintLayout>
/**
 * 使用SurfaceView预览Camera数据
 */
public class CameraSurfaceViewShowActivity extends AppCompatActivity implements SurfaceHolder.Callback {
    @BindView(R.id.mSurface)
    SurfaceView mSurfaceView;

    public SurfaceHolder mHolder;
    private Camera mCamera;
    private Camera.Parameters mParameters;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_base_camera);
        ButterKnife.bind(this);
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        try {
            // Open the Camera in preview mode
            mCamera = Camera.open(0);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
        } catch (IOException e) {
        }
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    mParameters = mCamera.getParameters();
                    mParameters.setPictureFormat(PixelFormat.JPEG); //图片输出格式
//                    mParameters.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);//预览持续发光
                    mParameters.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);//持续对焦模式
                    mCamera.setParameters(mParameters);
                    mCamera.startPreview();
                    mCamera.cancelAutoFocus();
                }
            }
        });
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
    }

    @OnClick(R.id.btn_change)
    public void onViewClicked() {
//        PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.5f,1.0f);
        PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.5f,1.0f);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mSurfaceView,  valuesHolder,valuesHolder1,valuesHolder3);
        objectAnimator.setDuration(5000).start();
    }
}

效果展示

特别提醒

SurfaceView预览相机视图不支持透明度,可以设置缩放旋转属性。如果需要做动画特效的话不推荐使用SurfaceView显示视图。可以使用下面的TextureView或者GlSurfaceView来显示。

3. 如何通过TextureView显示Camera预览

基本步骤

TextureView和SurfaceView显示Camera数据其实差不多,差别就两点:

  1. SurfaceView显示需要实现SurfaceHolder.Callback的回调而TextureView通过实现 TextureView.SurfaceTextureListener接口。
  2. 当Camera使用SurfaceView预览时通过setPreviewDisplay(holder)方法来设置预览视图,而使用TextureView预览时使用setPreviewTexture(mCameraTextureView.getSurfaceTexture())方法来设置。 其他步骤同上。

代码部分

public class CameraTextureViewShowActivity extends AppCompatActivity implements TextureView.SurfaceTextureListener {

    @BindView(R.id.camera_texture_view)
    TextureView mCameraTextureView;
    public Camera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_surface_texture);
        ButterKnife.bind(this);
        mCameraTextureView.setSurfaceTextureListener(this);
    }

    @Override
    public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
        try {
            mCamera = Camera.open(0);
            mCamera.setDisplayOrientation(90);
            mCamera.setPreviewTexture(mCameraTextureView.getSurfaceTexture());
            mCamera.startPreview();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {

    }

    @Override
    public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
        if (mCamera != null) {
            mCamera.stopPreview();
            mCamera.release();
            mCamera = null;
        }
        return false;
    }

    @Override
    public void onSurfaceTextureUpdated(SurfaceTexture surface) {

    }

    @OnClick(R.id.btn_texture_anim)
    public void onViewClicked() {
        PropertyValuesHolder valuesHolder = PropertyValuesHolder.ofFloat("translationX", 0.0f, 0.0f);
        PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.3f,1.0f);
        PropertyValuesHolder valuesHolder4 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.3f,1.0f);
        PropertyValuesHolder valuesHolder2 = PropertyValuesHolder.ofFloat("rotationX", 0.0f, 2 * 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder5 = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 2 * 360.0f, 0.0F);
        PropertyValuesHolder valuesHolder3 = PropertyValuesHolder.ofFloat("alpha", 1.0f, 0.7f, 1.0F);
        ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mCameraTextureView, valuesHolder, valuesHolder1, valuesHolder2, valuesHolder3,valuesHolder4,valuesHolder5);
        objectAnimator.setDuration(5000).start();
    }
}

效果展示

4. 如何通过GlSurfaceView处理Camera预览。

如果你在学习自定义相机,而且你的相机想要实现美颜,滤镜,人脸识别AR场景 and so on。这时候你就必须要学习如何使用GlsurfaView罗。如果你没有openGl的基本配置的知识或者你之前完全没有学习过openGl的开发,再次强烈建议你看一下这篇文章# Android OpenGL开发——图像绘制详解,否则,下面内容可能会引起你的严重不适。当然,如果你还是觉得哪部分写得不好或者有疑问的话,欢迎给我留言,或者关注公众号aserbao私聊我。

基本步骤

  1. 在xml中添加GlSurfaceView
  2. 创建渲染器类实现GlSurfaceView.Renderer
  3. 清除画布,并创建一个纹理并绑定到。
  4. 创建一个用来最后显示的SurfaceTexture来显示处理后的数据。
  5. 创建Opengl ES程序并添加着色器到该程序中,创建openGl程序的可执行文件,并释放shader资源。
  6. 打开摄像头,并配置相关属性。设置预览视图,并开启预览。
  7. 添加程序到ES环境中,并设置及启用各类句柄。
  8. 在onDrawFrame中进行画布的清理及绘制最新的数据到纹理图形中。
  9. 设置一个SurfaceTexture.OnFrameAvailableListener的回调来通知GlSurfaceview渲染新的帧数据。

建议: GlSurfaceView作用简单的理解OpenGl对相机数据进行处理完之后的显示。我们需要明白的是渲染器的渲染周期及渲染方法的调用时机。

  1. onSurfaceCreated()当surface创建(第一次进入当前页面)或者重新创建(切换后台再进入)的时候调用。
  2. onSurfaceChanged()当surface大小发生改变的时候会被调用。
  3. onDrawFrame()绘制当前帧数据的时候被调用。

代码部分

public class CameraGlSurfaceShowActivity extends AppCompatActivity implements SurfaceTexture.OnFrameAvailableListener {
    public SurfaceTexture mSurfaceTexture;

    public static Camera camera;
    private int camera_status = 1;
    @BindView(R.id.camera_glsurface_view)
    GLSurfaceView mCameraGlsurfaceView;
    public MyRender mRenderer;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_camera_gl_surface_show);
        ButterKnife.bind(this);
        mCameraGlsurfaceView.setEGLContextClientVersion(2);//在setRenderer()方法前调用此方法
        mRenderer = new MyRender();
        mCameraGlsurfaceView.setRenderer(mRenderer);
        mCameraGlsurfaceView.setRenderMode(RENDERMODE_WHEN_DIRTY);
    }


    @Override
    public void onFrameAvailable(SurfaceTexture surfaceTexture) {
        mCameraGlsurfaceView.requestRender();
    }

    @OnClick({R.id.btn_gl_surface_view_animator, R.id.btn_gl_surface_view_switch})
    public void onViewClicked(View view) {
        switch (view.getId()) {
            case R.id.btn_gl_surface_view_animator:
                PropertyValuesHolder valuesHolder1 = PropertyValuesHolder.ofFloat("scaleX", 1.0f, 0.5f,1.0f);
                PropertyValuesHolder valuesHolder4 = PropertyValuesHolder.ofFloat("scaleY", 1.0f, 0.5f,1.0f);
                PropertyValuesHolder valuesHolder5 = PropertyValuesHolder.ofFloat("rotationY", 0.0f, 360.0f, 0.0F);
                ObjectAnimator objectAnimator = ObjectAnimator.ofPropertyValuesHolder(mCameraGlsurfaceView, valuesHolder1, valuesHolder4,valuesHolder5);
                objectAnimator.setDuration(3000).start();
                break;
            case R.id.btn_gl_surface_view_switch:
                camera_status ^= 1;
                if (camera != null) {
                    camera.stopPreview();
                    camera.release();
                }
                mRenderer.mBoolean = true;
                camera = Camera.open(camera_status);
                try {
                    camera.setPreviewTexture(mSurfaceTexture);
                } catch (IOException e) {
                    e.printStackTrace();
                }
                camera.startPreview();
                break;
        }
    }

    public class MyRender implements GLSurfaceView.Renderer {
        private final String vertexShaderCode = "uniform mat4 textureTransform;\n" +
                "attribute vec2 inputTextureCoordinate;\n" +
                "attribute vec4 position;            \n" +//NDK坐标点
                "varying   vec2 textureCoordinate; \n" +//纹理坐标点变换后输出
                "\n" +
                " void main() {\n" +
                "     gl_Position = position;\n" +
                "     textureCoordinate = inputTextureCoordinate;\n" +
                " }";

        private final String fragmentShaderCode = "#extension GL_OES_EGL_image_external : require\n" +
                "precision mediump float;\n" +
                "uniform samplerExternalOES videoTex;\n" +
                "varying vec2 textureCoordinate;\n" +
                "\n" +
                "void main() {\n" +
                 "    vec4 tc = texture2D(videoTex, textureCoordinate);\n" +
                "    float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;\n" +  //所有视图修改成黑白
                "    gl_FragColor = vec4(color,color,color,1.0);\n" +
//                "    gl_FragColor = vec4(tc.r,tc.g,tc.b,1.0);\n" +
                "}\n";
        private FloatBuffer mPosBuffer;
        private FloatBuffer mTexBuffer;
        private float[] mPosCoordinate = {-1, -1, -1, 1, 1, -1, 1, 1};
        private float[] mTexCoordinateBackRight = {1, 1, 0, 1, 1, 0, 0, 0};//顺时针转90并沿Y轴翻转  后摄像头正确,前摄像头上下颠倒
        private float[] mTexCoordinateForntRight = {0, 1, 1, 1, 0, 0, 1, 0};//顺时针旋转90  后摄像头上下颠倒了,前摄像头正确

        public int mProgram;
        public boolean mBoolean = false;

        public MyRender() {
            Matrix.setIdentityM(mProjectMatrix, 0);
            Matrix.setIdentityM(mCameraMatrix, 0);
            Matrix.setIdentityM(mMVPMatrix, 0);
            Matrix.setIdentityM(mTempMatrix, 0);
        }

        private int loadShader(int type, String shaderCode) {
            int shader = GLES20.glCreateShader(type);
            // 添加上面编写的着色器代码并编译它
            GLES20.glShaderSource(shader, shaderCode);
            GLES20.glCompileShader(shader);
            return shader;
        }

        private void creatProgram() {
            //通常做法
//            String vertexSource = AssetsUtils.read(CameraGlSurfaceShowActivity.this, "vertex_texture.glsl");
//            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexSource);
//            String fragmentSource = AssetsUtils.read(CameraGlSurfaceShowActivity.this, "fragment_texture.glsl");
//            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentSource);
            int vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
            int fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);
            // 创建空的OpenGL ES程序
            mProgram = GLES20.glCreateProgram();

            // 添加顶点着色器到程序中
            GLES20.glAttachShader(mProgram, vertexShader);

            // 添加片段着色器到程序中
            GLES20.glAttachShader(mProgram, fragmentShader);

            // 创建OpenGL ES程序可执行文件
            GLES20.glLinkProgram(mProgram);

            // 释放shader资源
            GLES20.glDeleteShader(vertexShader);
            GLES20.glDeleteShader(fragmentShader);
        }

        private FloatBuffer convertToFloatBuffer(float[] buffer) {
            FloatBuffer fb = ByteBuffer.allocateDirect(buffer.length * 4)
                    .order(ByteOrder.nativeOrder())
                    .asFloatBuffer();
            fb.put(buffer);
            fb.position(0);
            return fb;
        }

        private int uPosHandle;
        private int aTexHandle;
        private int mMVPMatrixHandle;
        private float[] mProjectMatrix = new float[16];
        private float[] mCameraMatrix = new float[16];
        private float[] mMVPMatrix = new float[16];
        private float[] mTempMatrix = new float[16];

        //添加程序到ES环境中
        private void activeProgram() {
            // 将程序添加到OpenGL ES环境
            GLES20.glUseProgram(mProgram);

            mSurfaceTexture.setOnFrameAvailableListener(CameraGlSurfaceShowActivity.this);
            // 获取顶点着色器的位置的句柄
            uPosHandle = GLES20.glGetAttribLocation(mProgram, "position");
            aTexHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
            mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "textureTransform");

            mPosBuffer = convertToFloatBuffer(mPosCoordinate);
            if(camera_status == 0){
                mTexBuffer = convertToFloatBuffer(mTexCoordinateBackRight);
            }else{
                mTexBuffer = convertToFloatBuffer(mTexCoordinateForntRight);
            }

            GLES20.glVertexAttribPointer(uPosHandle, 2, GLES20.GL_FLOAT, false, 0, mPosBuffer);
            GLES20.glVertexAttribPointer(aTexHandle, 2, GLES20.GL_FLOAT, false, 0, mTexBuffer);

            // 启用顶点位置的句柄
            GLES20.glEnableVertexAttribArray(uPosHandle);
            GLES20.glEnableVertexAttribArray(aTexHandle);
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            mSurfaceTexture = new SurfaceTexture(createOESTextureObject());
            creatProgram();
//            mProgram = ShaderUtils.createProgram(CameraGlSurfaceShowActivity.this, "vertex_texture.glsl", "fragment_texture.glsl");
            camera = Camera.open(camera_status);
            try {
                camera.setPreviewTexture(mSurfaceTexture);
                camera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }
            activeProgram();

        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
            Matrix.scaleM(mMVPMatrix,0,1,-1,1);
            float ratio = (float) width / height;
            Matrix.orthoM(mProjectMatrix, 0, -1, 1, -ratio, ratio, 1, 7);// 3和7代表远近视点与眼睛的距离,非坐标点
            Matrix.setLookAtM(mCameraMatrix, 0, 0, 0, 3, 0f, 0f, 0f, 0f, 1.0f, 0.0f);// 3代表眼睛的坐标点
            Matrix.multiplyMM(mMVPMatrix, 0, mProjectMatrix, 0, mCameraMatrix, 0);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            if(mBoolean){
                activeProgram();
                mBoolean = false;
            }
            if (mSurfaceTexture != null) {
                GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
                mSurfaceTexture.updateTexImage();
                GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mMVPMatrix, 0);
                GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, mPosCoordinate.length / 2);
            }
        }
    }

    public static int createOESTextureObject() {
        int[] tex = new int[1];
        //生成一个纹理
        GLES20.glGenTextures(1, tex, 0);
        //将此纹理绑定到外部纹理上
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0]);
        //设置纹理过滤参数
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
        return tex[0];
    }
}

当然,大多数情况下渲染顶点着色器及片段着色器的代码会编写一个glsl的文件放到assets目录下进行访问。 下面是另外一个操作方式:

vertex_texture.glsl文件

uniform mat4 textureTransform;
attribute vec2 inputTextureCoordinate;
attribute vec4 position;            //NDK坐标点
varying   vec2 textureCoordinate; //纹理坐标点变换后输出

 void main() {
     gl_Position = position;
     textureCoordinate = inputTextureCoordinate;
 }

fragment_texture.glsl文件:

#extension GL_OES_EGL_image_external : require
precision mediump float;
uniform samplerExternalOES videoTex;
varying vec2 textureCoordinate;

void main() {
    vec4 tc = texture2D(videoTex, textureCoordinate);
    float color = tc.r * 0.3 + tc.g * 0.59 + tc.b * 0.11;//这里进行的颜色变换处理,传说中的黑白滤镜。
    gl_FragColor = vec4(color,color,color,1.0);
}

读取文件内容方式:

public static String read(Context context, String fileName) {
        String result = null;
        try {
            InputStream is = context.getResources().getAssets().open("Shader/" + fileName);
            int length = is.available();
            byte[] buffer = new byte[length];
            is.read(buffer);
            result = new String(buffer, "utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

具体实现在上面代码creatProgram()下注释掉通常做法的那部分。

实现效果

遇到的问题

  1. 使用OpenGl处理数据之后再GlsurfaceView上图片镜像了,咋办?这种问题要么出在绘制顶点坐标,要么出在源数据上,但是通过其他的比如SurfaceView直接预览数据没问题,那么只能是第一种可能了。仔细回看下代码,果然修改渲染颜色矩阵显示出来的数据方向都是不同的。

下面是我改动矩阵产生的结果:

问题根本找到了,如何解决呢?起始点就四个点,一个点就最多三个连接点,而且起始点不能和不相邻的点进行连接(一旦连接就会出现上面的第二种情况),所以最后4个点只有8种可能性,将所有点的都试一遍,得出如下结论(经过几轮测试,基本结论如下):

  1. 还有一个关于前后摄像头的切换的问题,我上面的做法是在点击切换摄像头操作的时候只针对摄像头进行了释放重启操作,直接在onDrawFrame方法中对渲染矩阵进行了修改,没有对SurfaceTexture进行数据清除(具体看上面代码)。然而也看了一些主流的第三方Demo,这里不列出名字了。他们的做法是摄像头和surfaceTexture一块释放。当然,两种方式都可以,我上面的那种方式暂时没找到什么问题,而且我通过实测比第二种方式看到相机数据的时间要快一点。

运行效果

5. 总结

弄清楚没个步骤之后你就会发现之前感觉无从下手的东西也就这样。没什么大不了的。

Sugerencia personal: al hacer un proyecto de cámara, es mejor aclarar cada paso, y una lógica clara ahorrará mucho tiempo. Al principio, estaba haciendo esta parte tomando la demostración de otra persona. Cuando cometí un error, no pude comenzar. Fui a Baidu para encontrar una solución. Perdí mucho tiempo. No puedo decir por qué. Después de reorganizar, aprenderá mucho.

Por último, no digamos más, si hay algo que no entiendas, puedes dejarme un mensaje o seguir la cuenta de WeChat del mismo nombre aserbao para chatear conmigo en privado. Dale pulgar arriba si crees que está bien escrito. Olvidé olvidar, hay código fuente y atención, mira abajo.

6. Enlace fuente

Si AndroidCamera te resulta útil, dale una estrella

Estoy participando en el reclutamiento del programa de firma de creadores de la Comunidad Tecnológica de Nuggets , haga clic en el enlace para registrarse y enviar .

Supongo que te gusta

Origin juejin.im/post/7117202297569935397
Recomendado
Clasificación