Android OpenGL ES从入门到进阶(四)—— OpenGL ES 2.0+GLSurfaceView预览相机

源码链接:https://github.com/smzhldr/AGLFramework

一、综述

安卓中的相机预览方式可以有好几种,比如SurfaceView,TextureView,GLSurfaceView等都可以预览相机。如果在预览中要改变预览效果,比如说美颜,磨皮的话使用GLSurfaceView+OpenGL ES预览就非常方便,由于5.0以上不支持Camera2,所以大多数相机类App都还使用的Camera的API,本篇文章就以Camera为例学习。

二、预览步骤

1.打开相机,创建SurfaceTexture接收摄像头拍摄的数据(打开相机记得申请摄像头权限 )

public void openCamera(){
	Camera camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
	//SurfaceTexture用于接收相机返回的预览数据,需要一个Texture来填充
	int textureId = createCameraTexture();
	SurfaceTexture surfaceTexture = new SurfaceTexture(textureId);     	
	camera.setPreviewTexture(surfaceTexture);
	camera.startPreview();
}

2.创建OpenGL ES扩展纹理
安卓相机的预览需要使用OpenGL ES扩展纹理,为了方便理解,纹理可以理解为类似于图片或者画布一类的东西,填充在SurfaceTexture中用于接收相机回调数据,创建纹理的方法暂且可以认为是固定如下的。

//注意相机预览需要 GL_TEXTURE_EXTERNAL_OES 类型
private int createCameraTexture() {
    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];
}

三、将相机预览数据显示到屏幕上

前面我们说了,SurfaceTexture用于接收摄像头预览数据,也就是说相机预览的数据会时时传到SurfaceTexture中,在这里,我们需要手动调用surfaceTexture.updateTexImage()去主动刷新SurfaceTexture上的数据。

surfaceTexture.updateTexImage();

那怎么获取SurfaceTexture上的数据的,我们前面创建SurfaceTexture时传入的Texture就具有预览的画面信息,这样问题就变成了一个拥有数据的texture,我们要将其绘制到屏幕上,这就简单了,基本上与Android OpenGL ES从入门到精通(二)—— OpenGL ES 2.0渲染纹理到屏幕中讲述的内容一样,不熟悉的可以回顾一下,当然下面我们会给出完整demo,为了能比较直观的表达出整体的逻辑,整个功能都写在一个activity中,想用的粘过去(申请拍照权限)直接就能用,若是没有OpengGLUUtils这样的工具类,可以参考上一篇Android OpenGL ES从入门到精通(三)—— OpenGL ES 2.0基础知识入门篇(Hello Triangle)中对shader进行编译链接。如有疑问欢迎留言讨论。

以下是demo源码,拿去随便用:

public class CameraActivity extends Activity {

    GLSurfaceView glSurfaceView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        glSurfaceView = new GLSurfaceView(this);
        glSurfaceView.setEGLContextClientVersion(2);
        glSurfaceView.setRenderer(new CameraRenderer());
        glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
    }

    @Override
    protected void onResume() {
        super.onResume();
        glSurfaceView.onResume();
    }


    @Override
    protected void onPause() {
        super.onPause();
        glSurfaceView.onPause();
    }


    private class CameraRenderer implements GLSurfaceView.Renderer {

        private final String vertexShaderCode =
                "attribute vec4 vPosition;" +
                        "uniform mat4 u_Matrix;" +
                        "attribute vec2 atextureCoordinate;" +
                        "varying vec2 aCoordinate;" +
                        "void main() {" +
                        "  gl_Position = u_Matrix * vPosition;" +
                        "  aCoordinate = atextureCoordinate;" +
                        "}";

        private final String fragmentShaderCode =
                "#extension GL_OES_EGL_image_external : require\n" +
                        "precision mediump float;" +
                        "uniform samplerExternalOES uTexture;" +
                        "varying vec2 aCoordinate;" +
                        "void main() {" +
                        "gl_FragColor = texture2D(uTexture,aCoordinate);" +
                        "}";

        float[] cube = {
                -1.0f, -1.0f,
                1.0f, -1.0f,
                -1.0f, 1.0f,
                1.0f, 1.0f,
        };

        float[] textureCoord = {
                0.0f, 0.0f,
                1.0f, 0.0f,
                0.0f, 1.0f,
                1.0f, 1.0f,
        };

        private FloatBuffer vertexBuffer, textureBuffer;

        private int program;
        private int glPosition;
        private int glTextCoordinate;
        private int glTexture;
        private int glMatrix;

        private SurfaceTexture surfaceTexture;
        private int textureId;

        CameraRenderer() {
            vertexBuffer = ByteBuffer.allocateDirect(cube.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            vertexBuffer.put(cube);
            vertexBuffer.position(0);

            textureBuffer = ByteBuffer.allocateDirect(textureCoord.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            textureBuffer.put(textureCoord);
            textureBuffer.position(0);
        }

        @Override
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            Camera camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_FRONT);
            textureId = createCameraTexture();
            surfaceTexture = new SurfaceTexture(textureId);
            surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
                @Override
                public void onFrameAvailable(SurfaceTexture surfaceTexture) {
                    glSurfaceView.requestRender();
                }
            });
            try {
                camera.setPreviewTexture(surfaceTexture);
                camera.startPreview();
            } catch (IOException e) {
                e.printStackTrace();
            }

            program = OpenGLUtils.createGlProgram(vertexShaderCode, fragmentShaderCode);
            glPosition = GLES20.glGetAttribLocation(program, "vPosition");
            glTextCoordinate = GLES20.glGetAttribLocation(program, "atextureCoordinate");
            glMatrix = glGetUniformLocation(program, "u_Matrix");
            glTexture = GLES20.glGetUniformLocation(program, "uTexture");
        }

        @Override
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            GLES20.glViewport(0, 0, width, height);
        }

        @Override
        public void onDrawFrame(GL10 gl) {
            GLES20.glClearColor(0, 0, 0, 0);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            GLES20.glUseProgram(program);

            float[] matrix = new float[16];
            surfaceTexture.getTransformMatrix(matrix);

            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);
            glUniform1i(glTexture, 0);
            GLES20.glUniformMatrix4fv(glMatrix, 1, false, matrix, 0);

            vertexBuffer.clear();
            vertexBuffer.put(cube).position(0);
            GLES20.glEnableVertexAttribArray(glPosition);
            GLES20.glVertexAttribPointer(glPosition, 2, GLES20.GL_FLOAT, false, 0, vertexBuffer);

            textureBuffer.clear();
            textureBuffer.put(textureCoord).position(0);
            GLES20.glEnableVertexAttribArray(glTextCoordinate);
            GLES20.glVertexAttribPointer(glTextCoordinate, 2, GLES20.GL_FLOAT, false, 0, textureBuffer);

            GLES20.glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

            GLES20.glDisableVertexAttribArray(glPosition);
            GLES20.glDisableVertexAttribArray(glTextCoordinate);
            surfaceTexture.updateTexImage();
        }

        private int createCameraTexture() {
            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];
        }
    }
}

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
好的,我理解了你的问题,以下是 OpenGL ES 纹理从上到下循环流动的 Android demo: 1. 首先,我们需要在 Android Studio 中创建一个新项目。 2. 在项目中创建一个名为“texture.glsl”的文件,并在其中添加以下代码: vertexShaderCode: ```glsl attribute vec4 position; attribute vec2 textureCoordinate; varying vec2 vTextureCoord; void main() { gl_Position = position; vTextureCoord = textureCoordinate; } ``` fragmentShaderCode: ```glsl precision mediump float; uniform sampler2D uTexture; varying vec2 vTextureCoord; uniform float uTime; void main() { vec2 uv = vec2(vTextureCoord.x, vTextureCoord.y + uTime); if (uv.y > 1.0) { uv.y -= 1.0; } vec4 textureColor = texture2D(uTexture, uv); gl_FragColor = textureColor; } ``` 3. 接下来,在项目中创建一个名为“TextureRenderer.java”的类,并在其中添加以下代码: ```java import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLES20; import android.opengl.GLSurfaceView; import android.opengl.GLUtils; import android.os.SystemClock; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class TextureRenderer implements GLSurfaceView.Renderer { private Context context; private int textureId; private int program; private int positionHandle; private int textureCoordinateHandle; private int uTextureHandle; private int uTimeHandle; private float[] vertices = { -1f, -1f, 1f, -1f, -1f, 1f, 1f, 1f }; private float[] textureCoords = { 0f, 1f, 1f, 1f, 0f, 0f, 1f, 0f }; private Bitmap bitmap; public TextureRenderer(Context context) { this.context = context; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { gl.glClearColor(0f, 0f, 0f, 1f); createProgram(); createTexture(); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { GLES20.glViewport(0, 0, width, height); } @Override public void onDrawFrame(GL10 gl) { GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(program); long time = SystemClock.uptimeMillis(); float seconds = time / 1000f; float timeValue = seconds % 1.0f; GLES20.glUniform1f(uTimeHandle, timeValue); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); GLES20.glUniform1i(uTextureHandle, 0); GLES20.glVertexAttribPointer( positionHandle, 2, GLES20.GL_FLOAT, false, 0, VertexArrayHelper.createFloatBuffer(vertices) ); GLES20.glEnableVertexAttribArray(positionHandle); GLES20.glVertexAttribPointer( textureCoordinateHandle, 2, GLES20.GL_FLOAT, false, 0, VertexArrayHelper.createFloatBuffer(textureCoords) ); GLES20.glEnableVertexAttribArray(textureCoordinateHandle); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); GLES20.glDisableVertexAttribArray(positionHandle); GLES20.glDisableVertexAttribArray(textureCoordinateHandle); } private void createProgram() { String vertexShaderCode = AssetsHelper.loadShaderCode(context, "texture.glsl", "vertexShaderCode"); String fragmentShaderCode = AssetsHelper.loadShaderCode(context, "texture.glsl", "fragmentShaderCode"); program = GLES20.glCreateProgram(); int vertexShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER); GLES20.glShaderSource(vertexShader, vertexShaderCode); GLES20.glCompileShader(vertexShader); GLES20.glAttachShader(program, vertexShader); int fragmentShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER); GLES20.glShaderSource(fragmentShader, fragmentShaderCode); GLES20.glCompileShader(fragmentShader); GLES20.glAttachShader(program, fragmentShader); GLES20.glLinkProgram(program); positionHandle = GLES20.glGetAttribLocation(program, "position"); textureCoordinateHandle = GLES20.glGetAttribLocation(program, "textureCoordinate"); uTextureHandle = GLES20.glGetUniformLocation(program, "uTexture"); uTimeHandle = GLES20.glGetUniformLocation(program, "uTime"); } private void createTexture() { bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.texture); int[] textureIds = new int[1]; GLES20.glGenTextures(1, textureIds, 0); textureId = textureIds[0]; GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST); GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_REPEAT ); GLES20.glTexParameteri( GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_REPEAT ); GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0); bitmap.recycle(); } } ``` 4. 最后,在 MainActivity 中,添加以下代码: ```java public class MainActivity extends AppCompatActivity { private GLSurfaceView glSurfaceView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); glSurfaceView = findViewById(R.id.gl_surface_view); glSurfaceView.setEGLContextClientVersion(2); glSurfaceView.setRenderer(new TextureRenderer(this)); glSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_CONTINUOUSLY); } } ``` 5. 运行应用程序,您将看到纹理从上到下循环流动的效果。 希望这个demo能够帮到你。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

[](){}

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值