Home [Android] 동영상에 그림 그리기 (10) - 개선 리스트
Post
Cancel

[Android] 동영상에 그림 그리기 (10) - 개선 리스트

이번 포스트에서는 boomerang 앱을 완성한 이후 수정, 개선한 부분들에 대해 작성하겠습니다. 이 포스트의 내용은 코드의 수정사항이 생기면 계속 추가하겠습니다.

Vertex Buffer Object

지난 포스트에서 Vertex Buffer에 대해 작성하면서, java.nio 패키지의 ByteBuffer를 사용하는 방법(Client-side buffer)과, OpenGL ES에서 버퍼를 생성하는 두 가지 방법이 있다고 하였습니다. OpenGL ES의 버퍼를 사용하는 방식으로 수정해보았습니다.

1
2
val vertexBuffer = IntArray(1)
GLES20.glGenBuffers(1, vertexBuffer, 0)

우선 버퍼를 생성해야 합니다. IntArray를 생성하는 것은 빈 배열을 생성하고 그 곳에 생성한 OpenGL ES의 버퍼에 접근할 수 있는 값을 넣어주는 방식입니다. 이러한 방식은 EGL 초기화텍스처 생성 등에서 쓰였던 방식이니 이제 익숙하실겁니다.

1
2
3
4
5
6
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBuffer[0])
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, 32, vertexBufferOld, GLES20.GL_STATIC_DRAW)
GLES20.glEnableVertexAttribArray(positionLoc)
GLES20.glVertexAttribPointer(
    positionLoc, 2, GLES20.GL_FLOAT, false, 8, 0
)

다음으로 glBindBuffer(...)를 호출하여 해당 버퍼를 사용하겠다고 지정합니다.

glBufferData(...)는 버퍼에 들어갈 데이터를 초기화하는 함수입니다. 뒤의 GLES20.GL_STATIC_DRAW 인자는 이 데이터가 얼마나 자주 수정되고 사용되는지에 대한 값으로, 이 값은 자주 사용되지만 수정하지 않는 데이터에 적용합니다. 이 값에 따라 데이터의 메모리 위치가 결정됩니다.

이후 glEnableVertexAttribArray(...)를 호출해 버퍼의 값을 Vertex Shader의 attribute가 읽을 수 있도록 설정하고, glVertexAttribPointer(...)로 값을 어떻게 읽을지 설정합니다. 파라미터는 각각

  • attribute의 location 값
  • 각 값의 개수 (몇 개씩 들어갈 것인지)
  • 자료형
  • 정규화 여부(0~1 사이의 값으로)
  • 하나의 값이 차지하는 크기(2개씩 각 4바이트(float)이므로 8바이트)
  • 기본값

을 의미합니다.

in, out 키워드

결론부터 적자면, OpenGL ES 2.0에서는 해당 키워드를 GLSL ES(ESSL)에 사용할 수 없습니다.

1
2
3
4
5
6
7
8
9
uniform mat4 uMVPMatrix;
uniform mat4 uTexMatrix;
attribute vec4 aPosition;
attribute vec4 aTextureCoord;
varying vec2 vTextureCoord;
void main() {
    gl_Position = uMVPMatrix * aPosition;
    vTextureCoord = (uTexMatrix * aTextureCoord).xy;
}

위의 vertex shader 코드에서 attribute 대신에, layout(location = 0) in을 사용할 수 있습니다. 이 경우 얻을 수 있는 장점은:

1
2
3
private val positionLoc: Int

positionLoc = GLES20.glGetAttribLocation(programHandle, "aPosition")

Vertex Shader의 attribute 변수의 location 값을 가져오기 위해 위와 같은 코드를 작성할 필요가 없어집니다. 대신 location = 으로 지정한 값을 그대로 사용할 수 있습니다. location 값을 가져오는 것이 왜 필요한지 궁금하시다면 바로 위의 Vertex Buffer Object에 대해 작성한 코드에서 positionLoc 값이 어떻게 쓰였는지 확인하시면 도움이 될 것입니다. 만약 in 키워드를 사용한다면 positionLoc 대신에 location으로 지정한 값, 예를 들어 0을 대신 사용할 수 있을 것입니다.

또한 out 키워드는 varying 대신에 사용할 수 있습니다. varying은 Vertex Shader의 결과물, 출력값이며 Fragment Shader의 입력값으로 쓰입니다. 이를 Vertex Shader 에서는 out으로 쓰고, Fragment Shader에서 in으로 사용할 수 있습니다.

in, out 키워드가 도입된 이유는 더 이상 Shader가 두 종류만 존재하지 않기 때문입니다. OpenGL 3.2에서 Vertex Shader와 Fragment Shader 사이에 Geometry Shader라고 불리는 추가적인 Shader가 추가되었고, 그 이후 버전에서 Tesellation Evaluation Shader, Compute Shader 등이 추가되었습니다. 또한 이러한 Shader들은 Vertex Shader나 Fragment Shader처럼 필수적인 과정이 아니기 때문에 특정 값이 어느 Shader와 연결되는지에 대해서는 알 수 없습니다. 따라서 in, out 키워드로 데이터 입출력을 지정하도록 하여 각 Shader 간의 데이터 연결을 하는 방식으로 변경되었습니다.

attributevarying 타입 한정자(type qualifier)는 GLSL 1.30에서 deprecated 되었고 GLSL 1.40부터 제거되었습니다. 이는 GLSL ES에서는 3.00부터 이미 제거된 상태에 해당됩니다. 즉 OpenGL ES 3.0 이상을 사용한다면 이 두 타입 한정자를 사용할 수 없습니다.

하지만 이 프로젝트에서는 OpenGL ES 2.0도 지원하도록 하고 있고, 버전을 확인해보면 OpenGL ES 2.0은 GLSL ES 1.00 버전을 사용합니다. layout 키워드는 GLSL 3.3 (OpenGL 3.3에서 사용)을 기반으로 한 GLSL ES 3.00에서 추가되었으므로 OpenGL ES 2.0을 사용한다면 해당 키워드를 사용할 수 없습니다.

따라서 런타임에 사용 버전을 결정하는 이 앱에서는 해당 키워드를 사용하지 않거나, 버전에 따라서 사용하는 GLSL ES 코드를 분리할 수 있을 것입니다. 현재는 사용에 지장이 없기 때문에 기존의 코드를 그대로 사용하기로 하였습니다만, 분리한 코드로 테스트하였을 때 성능의 향상이 있다면 수정할 예정입니다.

This post is licensed under CC BY 4.0 by the author.

[Android] 동영상에 그림 그리기 (9) - SurfaceView로 화면 표시

[CS] 비동기 프로그래밍 (1) - 캐시 메모리