Android OpenGL 相机预览问题

标签 android opengl-es

我正在开发一个 Android 相机应用程序,它可以修改相机源并将其实时显示在屏幕上。我在运行 4.3 的 DROID RAZR MAXX 上运行并完美地完成了我想做的事情,它在其他手机上也完美运行,但不幸的是我在几部手机上遇到了问题并且无法找到问题。

我附上了显示问题所在的屏幕截图。

很难说出绿色“伪影”是什么,但它几乎看起来像是从第一次打开时开始的摄像头馈送。颜色闪烁,但 block 内的形状并没有真正改变。

我已经删除了所有不需要的东西,并尽我所能清理了代码,但老实说,我对为什么会发生这种情况一无所知,尤其是因为它似乎在某些手机上运行良好,而在其他手机上手机没有。

如果我需要提供更多信息,请发表评论,我会添加!

相机 Activity .java

public class CameraActivity extends Activity
{
    private MyGLSurfaceView glSurfaceView;
    private MyCamera mCamera;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        mCamera = new MyCamera();

        glSurfaceView = new MyGLSurfaceView(this, mCamera);

        setContentView(glSurfaceView);
    }

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

我的相机.java

public class MyCamera
{
    private final static String LOG_TAG = "MyCamera";

    private Camera mCamera;
    private Parameters mCameraParams;
    private Boolean running = false;

    void start(SurfaceTexture surface)
    {
        Log.v(LOG_TAG, "Starting Camera");

        mCamera = Camera.open(0);
        mCameraParams = mCamera.getParameters();
        Log.v(LOG_TAG, mCameraParams.getPreviewSize().width + " x " + mCameraParams.getPreviewSize().height);

        try {
            mCamera.setPreviewTexture(surface);
            mCamera.startPreview();
            running = true;
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    void stop()
    {
        if (running) {
            Log.v(LOG_TAG, "Stopping Camera");
            mCamera.stopPreview();
            mCamera.release();
            running = false;
        }
    }
}

MyGLSurfaceView.java

class MyGLSurfaceView extends GLSurfaceView implements Renderer
{
    private final static String LOG_TAG = "MyGLSurfaceView";
    private MyCamera mCamera;
    private SurfaceTexture mSurface;
    private DirectVideo mDirectVideo;

    public MyGLSurfaceView(Context context, MyCamera camera)
    {
        super(context);

        mCamera = camera;
        setEGLContextClientVersion(2);

        setRenderer(this);
    }

    @Override
    public void onDrawFrame(GL10 gl)
    {
        float[] mtx = new float[16];
        mSurface.updateTexImage();
        mSurface.getTransformMatrix(mtx);

        mDirectVideo.draw();
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height)
    {
        Log.v(LOG_TAG, "Surface Changed");
        GLES20.glViewport(0, 0, width, height);
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config)
    {
        Log.v(LOG_TAG, "Surface Created");
        int texture = createTexture();
        mDirectVideo = new DirectVideo(texture);
        mSurface = new SurfaceTexture(texture);
        mCamera.start(mSurface);
    }

    private int createTexture()
    {
        int[] textures = new int[1];

        // generate one texture pointer and bind it as an external texture.
        GLES20.glGenTextures(1, textures, 0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);

        // No mip-mapping with camera source.
        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);

        // Clamp to edge is only option.
        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 textures[0];
    }

    public static int loadShader(int type, String shaderCode){

        // create a vertex shader type (GLES20.GL_VERTEX_SHADER)
        // or a fragment shader type (GLES20.GL_FRAGMENT_SHADER)
        int shader = GLES20.glCreateShader(type);

        // add the source code to the shader and compile it
        GLES20.glShaderSource(shader, shaderCode);
        GLES20.glCompileShader(shader);

        return shader;
    }
}

DirectVideo.java

public class DirectVideo
{
    private final String vertexShaderCode =
            "attribute vec4 vPosition;" +
            "attribute vec2 inputTextureCoordinate;" +
            "varying vec2 textureCoordinate;" +
            "void main()" +
            "{"+
                "gl_Position = vPosition;"+
                "textureCoordinate = inputTextureCoordinate;" +
            "}";

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

    private FloatBuffer vertexBuffer, textureVerticesBuffer;
    private ShortBuffer drawListBuffer;
    private final int mProgram;
    private int mPositionHandle;
    private int mTextureCoordHandle;

    private short drawOrder[] = { 0, 1, 2, 0, 2, 3 }; // order to draw vertices

    // number of coordinates per vertex in this array
    private static final int COORDS_PER_VERTEX = 2;

    private final int vertexStride = COORDS_PER_VERTEX * 4; // 4 bytes per vertex

    static float squareCoords[] = {
       -1.0f,  1.0f,
       -1.0f, -1.0f,
        1.0f, -1.0f,
        1.0f,  1.0f,
    };

    static float textureVertices[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        1.0f, 0.0f,
        0.0f, 0.0f,
    };

    private int texture;

    public DirectVideo(int texture)
    {
        this.texture = texture;

        // initialize vertex byte buffer for shape coordinates
        ByteBuffer bb = ByteBuffer.allocateDirect(squareCoords.length * 4);
        bb.order(ByteOrder.nativeOrder());
        vertexBuffer = bb.asFloatBuffer();
        vertexBuffer.put(squareCoords);
        vertexBuffer.position(0);

        // initialize byte buffer for the draw list
        ByteBuffer dlb = ByteBuffer.allocateDirect(drawOrder.length * 2);
        dlb.order(ByteOrder.nativeOrder());
        drawListBuffer = dlb.asShortBuffer();
        drawListBuffer.put(drawOrder);
        drawListBuffer.position(0);

        ByteBuffer bb2 = ByteBuffer.allocateDirect(textureVertices.length * 4);
        bb2.order(ByteOrder.nativeOrder());
        textureVerticesBuffer = bb2.asFloatBuffer();
        textureVerticesBuffer.put(textureVertices);
        textureVerticesBuffer.position(0);

        int vertexShader    = MyGLSurfaceView.loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode);
        int fragmentShader  = MyGLSurfaceView.loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode);

        mProgram = GLES20.glCreateProgram();             // create empty OpenGL ES Program
        GLES20.glAttachShader(mProgram, vertexShader);   // add the vertex shader to program
        GLES20.glAttachShader(mProgram, fragmentShader); // add the fragment shader to program
        GLES20.glLinkProgram(mProgram);                  // creates OpenGL ES program executables
    }

    public void draw()
    {
        GLES20.glUseProgram(mProgram);

        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture);

        // get handle to vertex shader's vPosition member
        mPositionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition");

        // Enable a handle to the triangle vertices
        GLES20.glEnableVertexAttribArray(mPositionHandle);

        // Prepare the <insert shape here> coordinate data
        GLES20.glVertexAttribPointer(mPositionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, vertexBuffer);

        mTextureCoordHandle = GLES20.glGetAttribLocation(mProgram, "inputTextureCoordinate");
        GLES20.glEnableVertexAttribArray(mTextureCoordHandle);
        GLES20.glVertexAttribPointer(mTextureCoordHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, vertexStride, textureVerticesBuffer);

        GLES20.glDrawElements(GLES20.GL_TRIANGLES, drawOrder.length, GLES20.GL_UNSIGNED_SHORT, drawListBuffer);

        // Disable vertex array
        GLES20.glDisableVertexAttribArray(mPositionHandle);
        GLES20.glDisableVertexAttribArray(mTextureCoordHandle);
    }
}

最佳答案

onDrawFrame 方法中,您获得了一个转换矩阵,但您没有使用它。 该矩阵应用于转换纹理坐标。 有关详细信息,请参阅 SurfaceTexture 的文档类。

这里是修复:

  1. 将矩阵传递给 draw 方法:

    @Override
    public void onDrawFrame(GL10 gl)
    {
        float[] mtx = new float[16];
        mSurface.updateTexImage();
        mSurface.getTransformMatrix(mtx);    
    
        mDirectVideo.draw(mtx);
    }
    
  2. 将以下方法添加到 DirectVideo 类:

     private float[] transformTextureCoordinates( float[] coords, float[] matrix)
     {          
        float[] result = new float[ coords.length ];        
        float[] vt = new float[4];      
    
        for ( int i = 0 ; i < coords.length ; i += 2 ) {
            float[] v = { coords[i], coords[i+1], 0 , 1  };
            Matrix.multiplyMV(vt, 0, matrix, 0, v, 0);
            result[i] = vt[0];
            result[i+1] = vt[1];
        }
        return result;
     }
    
  3. 在 draw 方法中,在添加到缓冲区之前转换 textureVertices 列表(您应该在每次绘制时都进行此转换,因为矩阵可以更改):

    textureVerticesBuffer.clear();
    textureVerticesBuffer.put( transformTextureCoordinates( textureVertices, mtx ));
    textureVerticesBuffer.position(0);
    

另一种解决方案是将矩阵传递给着色器。

关于Android OpenGL 相机预览问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19852680/

相关文章:

java - 如何从相机 Intent 获取 URI?

javascript - 从 get 方法中删除 %20 值

android - eglSwapBuffers 失败 : EGL_BAD_ALLOC AndEngine Android

c++ - OpenGL : GLSL float has low precision

android - 在 JNI/OpenGL ES 加载代码期间,非常规和狡猾的 Android 崩溃

android - 单击列表项后显示对话框

android - 如何将字符串数据从 Activity 传递到 fragment ?

java - 2 个随机数似乎给出了几乎相同的结果

java - 使用 Java 或 C# 在 Y 轴上进行 Open GL 翻译

ios - 用于多重采样的 OpenGL ES 快照在 iOS 中提供奇怪的颜色