opengl

Android基于Shader的图像处理(6)-Camera开发

2018-09-30  本文已影响121人  andev009

完整代码位置:AndroidShaderDemo

Camera的预览方向和拍照方向
YUV 数据格式完全解析
MagicCamera

这里讲下Shader在相机开发里怎么应用。
相机预览的图像通过GLSurfaceView来展示,自定义一个CameraView来显示图像:

public class CameraView extends GLSurfaceView implements GLSurfaceView.Renderer{

Renderer接口的三个方法要在CameraView里实现。因为要使用Shader来处理预览图像,这里就需要先将预览图像保存在一个地方,然后把该图像作为纹理对象来处理。Android提供了SurfaceTexture来保存预览图像,SurfaceTexture和一个纹理对象绑定在一起。因为预览图像的格式是YUV,而不是普通RGB格式,所以生成这个纹理对象方式和一般不一样,生成纹理方法如下:

public static int getExternalOESTextureID() {
        int[] texture = new int[1];
        GLES20.glGenTextures(1, texture, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);//重点看这行
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
        GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
                GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
        return texture[0];
    }

glBindTexture绑定的是GLES11Ext.GL_TEXTURE_EXTERNAL_OES,猜测是硬件来解析图像格式,生成纹理。
有了纹理对象后,将该纹理和SurfaceTexture绑定在一起,代码如下:

private void initSurfaceTexture(){
    if (textureId == TextureHelper.NO_TEXTURE) {
     textureId = TextureHelper.getExternalOESTextureID();//生成纹理
    if (textureId != TextureHelper.NO_TEXTURE) {
        surfaceTexture = new SurfaceTexture(textureId);//两个绑定
   surfaceTexture.setOnFrameAvailableListener(onFrameAvailableListener);
    }
   }
}

接收预览图像的地方有了,现在可以在onSurfaceChanged回调里打开相机了:

@Override
public void onSurfaceChanged(GL10 gl, int width, int height) {
    glViewport(0, 0, width, height);
    surfaceWidth = width;
    surfaceHeight = height;
    openCamera();
}

在openCamera()方法里,首先配置相机参数,然后将之前接收图像的SurfaceTexture传给相机,代码如下:

 camera.setPreviewTexture(surfaceTexture);

现在相关配置都好了,开始接收图像到手机上显示。这里有两种显示方法,一种是直接显示相机预览图像,一种是对预览图像处理后再显示(就是相机滤镜,特效等)。先说下直接显示预览图像。
在onDrawFrame里首先要先调用surfaceTexture.updateTexImage();更新预览的图像到纹理,然后调用surfaceTexture.getTransformMatrix(mtx);获得一个矩阵,用于之后的图像变换(图像翻转之类)。有了纹理,之后的渲染就和之前文章中的各种render方式一样了。所不同的还是因为图像是YUV格式,所以使用该纹理时要用 glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);这里封装成CameraInputFilter和SimpleTextureOESProgram来专门出来预览图像渲染。完整的onDrawFrame代码如下:

@Override
public void onDrawFrame(GL10 gl) {
    GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

    if(surfaceTexture == null)
        return;
    surfaceTexture.updateTexImage();
    float[] mtx = new float[16];
    surfaceTexture.getTransformMatrix(mtx);
    if(cameraInputFilter == null){
        cameraInputFilter = new CameraInputFilter(context, vertexArray);
        }

    cameraInputFilter.setTextureTransformMatrix(mtx);

    cameraInputFilter.onDrawFrame(textureId);
    }
}

程序跑下,发现图像显示不对,图像是反的,还记得前面提到的 surfaceTexture.getTransformMatrix(mtx);吗?将该 mtx传给Vertex Shader,将纹理坐标变换下,就可以得到正确的图像了。

void main()
{
    v_TextureCoordinates = (u_textureTransform * a_TextureCoordinates).xy;
    gl_Position = a_Position;
}

下面讲下将预览图像处理后再显示。
有时候需要将预览图像当做纹理,然后再将普通纹理传给各种滤镜Shader。转化的方式就是使用帧缓存,过程就是不把SurfaceTexture的纹理显示到屏幕上,而是先显示在帧缓存里,然后再将帧缓存绑定的纹理传个各个Shader。
生成帧缓存对象的方法,前面文章有提到,这里不再说。在CameraInputFilter里通过onDrawToTexture方法将预览图像渲染到帧缓存。然后将返回的纹理id交给各种Shader,Demo里用ImageFilter来接收这个纹理id,并使用GrayTextureProgram把预览图像变成灰度图显示出来。

以上就是处理相机预览图像的流程。但是很多情况下,因为相机的翻转,纹理的坐标不匹配,导致显示图像不对。这里参考了MagicCamera项目,在openCamera里,根据相机的一些参数,先生成纹理坐标,代码如下:

protected void adjustSize(int rotation, boolean flipHorizontal, boolean flipVertical){
    float[] textureCords = TextureRotationUtil.getRotation(Rotation.fromInt(rotation),
                flipHorizontal, flipVertical);
    float[] cube = TextureRotationUtil.CUBE;


    float[] newCube = new float[]{
                cube[0], cube[1], textureCords[0],textureCords[1],
                cube[2], cube[3], textureCords[2],textureCords[3],
                cube[4], cube[5], textureCords[4],textureCords[5],
                cube[6], cube[7], textureCords[6],textureCords[7]
        };

    vertexArray = new VertexArray(newCube);
}
上一篇下一篇

猜你喜欢

热点阅读