이번 포스트에서는 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 간의 데이터 연결을 하는 방식으로 변경되었습니다.
attribute
와 varying
타입 한정자(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 코드를 분리할 수 있을 것입니다. 현재는 사용에 지장이 없기 때문에 기존의 코드를 그대로 사용하기로 하였습니다만, 분리한 코드로 테스트하였을 때 성능의 향상이 있다면 수정할 예정입니다.