본문 바로가기
C++ 200제/코딩 IT 정보

[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기

by vicddory 2017. 10. 23.

[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기


Texture

OpenGL ES에서 Texture의 존재감은 매우 크며 처음 언급하고자 하는 건 POT다. OpenGL ES는 POT 텍스쳐만을 받아들이는데, 이 텍스쳐는 모두 가로, 세로의 길이가 2의 배수여야 한다. (2, 4, 8, 16, 32, 64... 1024픽셀)


1024의 사이즈는 적당하면서도 보통은 최대 크기를 의미하기도 한다. 따라서 OpenGL ES에서 사용하는 텍스쳐는 다음과 같은 크기여야 한다.


- 64 * 128

- 256 * 32

- 512 * 512


200 * 200, 256 * 100 같은 길이의 텍스쳐는 사용할 수 없다.


이와 같은 제한 사항은 OpenGL ES를 구동시켜야 하는 GPU를 위한 최적화 규칙이기도 하다. 또 하나 중요한 규칙은 픽셀을 읽어 들이는 순서다. 일반적으로 이미지 파일은 왼쪽 위에서 오른족 아래 방향으로 픽셀 정보를 저장한다.


jpg, png, bmp, gif 등 거의 모든 파일이 해당한다. 하지만, OpenGL ES는 순서가 정반대임을 잊지 말자.


OpenGL ES, pixel_order_example[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


이 작은 문제를 해결하려면 OpenGL ES의 코어에 이미지를 넘기기 전, 개발자 본인이 사용하는 프로그래밍 언어에서 이미지를 위아래로 뒤집는다. (height - 100%).


OpenGL ES, image_flipped_example[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


간략히 OpenGL ES에서 텍스쳐가 동작하는 방식은 이렇다.


1. 이미지 파일에서 16진수로 바이너리 색 정보를 추출 (알파 값도 같이 추출할 수 있으므로, RGB와 RGBA 포맷을 지원)

2. 픽셀의 모든 색 정보를 배열에 저장 (이 과정을 텍셀(texel)이라고 부르며, 이 배열로 texture object를 만들 수 있다)

3. GPU와 Frame Buffer에 최적화시킨 배열을 별도의 공간에 저장 (이렇게 복잡한 부분 때문에 OpenGL이 비난받기도 한다)


OpenGL ES는 32개까지 지원하는 Texture Unit이란 걸 보유하고 있다.


Texture Unit이란 이미 저장된 픽셀 배열과 실제 Rendering 과정에서 발생하는 임시 링크이며, Fragment Shader 안에서도 사용이 가능하다.


기본적으로 각각의 Shader는 8개의 텍스쳐를 사용할 수 있지만, OpenGL ES는 2개의 Shader만 보유하므로, 2개의 Shader에서 사용하는 텍스쳐의 합이 8을 넘을 수 없다.


헷갈리나?


만약 당신이 하나의 Shader에서만 텍스쳐를 쓴다면 그 Shader 안에서 8개의 텍스쳐를 쓸 수 있다. 당신이 2개의 Shader를 쓴다면 2개의 Shader 안의 텍스쳐 합이 8을 넘을 수 없다는 뜻이다.


OpenGL ES, 3D 오브젝트[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


OpenGL ES는 32개의 텍스쳐를 보유할 수 있고 이것들을 모두 Shader에서 사용하도록 지원하지만, Shader는 8개의 텍스쳐만을 사용할 수 있을 뿐이다.


말이 안 된다고 생각하나? 나도 그렇게 생각한다. 중요한 건 33개의 텍스쳐가 필요하다면 첫 번째 텍스쳐를 다시 사용해야 한다는 것이다. 되게 헷갈린다는 걸 나도 안다. 헷갈린다.


다음 그림을 보자. (32개의 텍스처를 순차적으로 순회하며 사용해야 한다는 뜻)


OpenGL ES, texture_units_example[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


이 그림을 보면, 하나의 Texture Unit은 여러 Shader에서 중복 사용이 될 수 있다.


이런 접근 방법은 개발자로 하여금 혼란을 주지만 Khronos 그룹의 입장에서도 이해해보자. 


아래는 크로노스 개발자의 말이다.


"OpenGL ES Shader는 정말 엄청난 녀석이다"


“GPU에 의해서 매우 빠르게 처리된다. 하지만 크고 무거운 텍스쳐 데이터는... 흠... 여전히 CPU가 처리한다. 우리는 shader가 texture로 빠르게 접근하는 방법이 필요했다.


그래서 shader처럼 GPU에서 바로 처리될 수 있는 texture unit을 만들었다. GPU 내부의 캐쉬는 매우 빠르지만, GPU에서 동작할 texture unit의 수를 제한해야 했다. 개발자는 texture 데이터를 texture unit에 연결하고, texture unit을 사용할 shader를 알려주면 된다” 


이런 방법을 써볼 수도 있다.


보통 texture unit은 fragment shader에서 사용하지만, vertex shader 역시 texture 내부를 볼 수 있다. 일반적이진 않지만 어떤 경우엔 유용할 수도 있다.


기억해야 할 2가지 중요한 것은, 


첫째, glActiveTexture()로 texture unit을 활성화해야 하고, glBindTexture()로 바인딩해야 한다.

둘째, opengl ES는 32개의 texture unit을 지원하지만, vender에서 지원하는 개수를 넘을 수 없다.


OpenGL ES 텍스쳐, 3D 오브젝트[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


vender가 16개를 지원하면 0 ~15번까지 쓸 수 있다. 코드로 돌아가서 texture object를 만들고, bind하고 속성을 설정하자. (vender는 애플, 구글 등 OpenGL ES 명세를 구현한 회사들)


Texture Creation


GLvoid glGenTextures(GLsizei n, GLuint* textures)

n : 생성할 텍스쳐의 개수

textures : 텍스쳐 번호를 저장할 번수


GLvoid glBindTexture(GLenum target, GLuint texture)

target : 2D 텍스쳐, 또는 3D 텍스쳐로 설정

GL_ARRAY_BUFFER: This will set a VBO (or ABO, whatever)

GL_ELEMENT_ARRAY_BUFFER: This will set an IBO.

buffer : 연결할 텍스쳐


3D 텍스쳐라니, 이상하지 않은가?


OpenGL ES, 텍스쳐[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


난 처음 이 용어를 듣고 나서 이런 생각이 들었다. "WTF!(What the FUCK!!!)". 이런 이상한 부분 때문에 OpenGL ES는 Cube Map의 3D texture를 호출한다. 어쨌든, 핵심은 각 면을 나타내는 2D 텍스쳐와 큐브맵의 2D 텍스쳐를 갖고 어떻게 Texel을 하는가... 이것이다.


cube의 중심에서 3D 벡터는 어디에 존재하는 것인가? 이 부분에 대해선 많은 주의(설명과 이해)가 필요하다. 그래서 나는 3D 텍스쳐는 생략할 거다. 그냥 2D texture를 사용해라. 


아무튼, 생성된 Texture의 속성을 설정하자. 크로노스는 OpenGL ES의 내부를 서버(server)라 부르기에 texture data를 정의하는 과정을 업로드(upload)라 부른다.


텍스쳐 데이터를 업로드하고 속성을 설정하려면 아래의 함수를 사용한다.


Texture Properties

GLvoid glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)


와우! 파라미터가 많다!


그렇다 해도 이해하기 어렵진 않다. 우선, 다른 "Hook"처럼 glTexImage2D를 호출한 뒤 바인드(맨 나중에)된 텍스쳐의 속성을 설정한다.


Mip Map은 렌더링 타임을 최적화하는 기능 중 하나다. 간단히 말해서 원본 텍스쳐를 순차적으로 작게 만들고 그 과정의 복사본을 생성/저장하는 것이다. (1x1 픽셀의 크기가 될 때까지)


[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기[[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기]


이후 Rastrize 단계에서 물체가 보이는 크기에 따라 3D object의 원본이나 위에서 저장한 복사본을 선택해 보여주는 것이다.


mip map 이후 color 포맷, 이미지 크기, 데이터 포맷을 통해 최종 픽셀 데이터를 설정한다. 이 때문에 사용자는 가장 좋은 최적화 형태인 2byte per pixel의 데이터를 항상 보게 된다.


OpenGL ES는 어플리케이션이 구동될 기기와 EGL Context의 색상 범위와 포맷 안에서만 사용할 수 있다는 것을 기억해라. (벤더에 종속적이란 것을 잊지 마라) 이제 기본적인 텍스쳐 생성 방법과 OpenGL ES 내부에서의 구동 방법을 알았으니 Rasterize로 넘어가자.


출처 : All About OpenGL ES 2.X 번역

[OpenGL ES] 5. 3D 텍스쳐(Texture), 3D 오브젝트 이해하기

댓글