마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

카메라 미리보기 도중 OpenGL ES 2.0로 Bitmap 이미지 Rendering 질문

0 추천

OpenGLES 2.0으로 실시간 필터가 적용된 카메라 미리보기를 구현하였습니다.

그리고 찍힌 사진에도 동일하게 필터가 적용될 수 있도록

찍힌 Bitmap을 받아 OpenGLES로 rendering 해주는 renderBitmap() 메서드를 만들었는데요.

코드는 다음과 같습니다.

public Bitmap renderBitmap(Bitmap bitmap) {
        synchronized (this) {
            int width = bitmap.getWidth();
            int height = bitmap.getHeight();

            int[] textureName = new int[1];
            GLES20.glGenTextures(textureName.length, textureName, 0);
            GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureName[0]);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
            GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
            GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

            int[] renderBuffer = new int[3];
            GLES20.glGenRenderbuffers(renderBuffer.length, renderBuffer, 0);
            GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER, renderBuffer[0]);
            GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_RGBA, width, height);     // specified the render buffer to be color render buffer.

            int[] frameBuffer = new int[1];
            GLES20.glGenFramebuffers(frameBuffer.length, frameBuffer, 0);
            GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer[0]);
            GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, textureName[0], 0);
            GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_RENDERBUFFER, renderBuffer[0]);
            GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_DEPTH_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderBuffer[1]);
            GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER, GLES20.GL_STENCIL_ATTACHMENT, GLES20.GL_RENDERBUFFER, renderBuffer[2]);

            GLES20.glUseProgram(mProgramImage);

            int positionHandle = GLES20.glGetAttribLocation(mProgramImage, "vPosition");
            GLES20.glEnableVertexAttribArray(positionHandle);
            GLES20.glVertexAttribPointer(positionHandle, 3, GLES20.GL_FLOAT, false, 0, mVertexBuffer);

            int texCoordHandle = GLES20.glGetAttribLocation(mProgramImage, "vTexCoord");
            GLES20.glEnableVertexAttribArray(texCoordHandle);
            GLES20.glVertexAttribPointer(texCoordHandle, 2, GLES20.GL_FLOAT, false, 0, mTexCoordBuffer);

            int bright = GLES20.glGetUniformLocation(mProgramImage, "brightness");
            int contr = GLES20.glGetUniformLocation(mProgramImage, "contrast");
            int satur = GLES20.glGetUniformLocation(mProgramImage, "saturation");
            int tintr = GLES20.glGetUniformLocation(mProgramImage, "tintR");
            int tintg = GLES20.glGetUniformLocation(mProgramImage, "tintG");
            int tintb = GLES20.glGetUniformLocation(mProgramImage, "tintB");
            GLES20.glUniform1f(bright, mCameraParams.mBrightness / 200.0f);
            GLES20.glUniform1f(contr, mCameraParams.mContrast / 200.0f);
            GLES20.glUniform1f(satur, (mCameraParams.mSaturation + 100) / 100.0f);
            GLES20.glUniform1f(tintr, mCameraParams.mTintR / 200.0f);   // * (1 - 0.213f)
            GLES20.glUniform1f(tintg, mCameraParams.mTintG / 200.0f);   // * (1 - 0.715f)
            GLES20.glUniform1f(tintb, mCameraParams.mTintB / 200.0f);   // * (1 - 0.072f)

            GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureName[0]);
            GLES20.glDrawElements(GLES20.GL_TRIANGLES, mDrawOrder.length, GLES20.GL_UNSIGNED_SHORT, mDrawListBuffer);

            GLES20.glDisableVertexAttribArray(positionHandle);
            GLES20.glDisableVertexAttribArray(texCoordHandle);

            Log.i("CheckLog", "Status of Framebuffer : " + GLES20.glCheckFramebufferStatus(GLES20.GL_FRAMEBUFFER));     // 36053 is complete state number.

            IntBuffer intBuffer = IntBuffer.allocate(width * height);
            GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, intBuffer);

            int[] intArrayO = intBuffer.array();
            int[] intArrayR = new int[width * height];
            for (int i = 0; i < height; i++) {
                for (int j = 0; j < width; j++) {
                    intArrayR[(height - i - 1) * width + j] = intArrayO[i * width + j];
                }
            }

            Bitmap pBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
            pBitmap.copyPixelsFromBuffer(intBuffer.wrap(intArrayR));

            return pBitmap;
        }
}

FrameBuffer가 제대로 형성이 안되었는지

Log 체크하는 부분에서는 계속 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 값만 반환하고

return 되는 bitmap도 사이즈는 정상이나 검은색으로 비어있습니다.

위 renderBitmap() 메서드가 호출될 때는 카메라 미리보기의 OpenGLES 처리와 꼬이지 않도록

onDrawFrame() 메서드 내부의 OpenGLES 코드들에 대해 동기화 처리는 했구요.

잘 아시는 분들께 도대체 뭐가 잘못된 것인지 도움좀 구합니다.ㅠ

아무리 OpenGL 경험이 거의 없는 상태로 만들었다지만...

몇일째 구글링을 해봐도 답을 얻을 수 없는게

뭔가 크게 잘못생각하고 만든게 아닐까 하는 걱정이 드네요...

THK (2,360 포인트) 님이 2016년 10월 4일 질문

1개의 답변

0 추천
 
채택된 답변
GLenum error = glGetError(); 로 GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT 로그 찍히는 부분에서 발생하는 오류가 뭔지 확인해 보세요..

  GLES20.glReadPixels(0, 0, width, height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, intBuffer); 자체를 지원 못해서 0으로 된 값을 반환한게 아닐까 합니다.

https://android.googlesource.com/platform/frameworks/base/+/47fb191/core/java/com/android/internal/util/HexDump.java 를 사용하여, 각 반환값을 덤프해서 확인 해 보시는 것도 방법일 듯 합니다.
사악미소 (65,330 포인트) 님이 2016년 10월 4일 답변
THK님이 2016년 10월 4일 채택됨
감사합니다!^^ 덕분에 glGetError()를 사용해서 원인이 GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER, GLES20.GL_RGBA, width, height); 호출 시 GL_RGBA에 있음을 알아냈습니다. 확인해보니 GLES20.GL_RGBA 밸류는 다른 메서드의 인수로 사용할 수 있는 값이고, glRenderbufferStorage 메서드 에서는 RGBA4 나 RGB565 같은 것들만 쓸 수 있더군요.
...