수색…
텍스처링의 기초
텍스처는 특정 데이터 항목뿐 아니라 여러 항목을 혼합 (보간)하는 샘플 지점에 대한 편리한 액세스를 허용하는 데이터 저장소의 한 형태입니다.
OpenGL에서 텍스쳐는 많은 것들에 사용될 수 있지만 가장 일반적으로 이미지를 폴리곤 (예 : 삼각형)에 매핑합니다. 텍스처를 삼각형 (또는 다른 다각형)에 매핑하려면 텍스처의 어느 부분에 해당하는지 각 꼭지점에 알려야합니다. 우리는 다각형의 각 꼭지점에 텍스처 좌표를 할당하고 그 다각형의 모든 단편 사이에 보간됩니다. 텍스처 좌표는 일반적으로 아래 이미지와 같이 x 및 y 축에서 0에서 1까지입니다.
이 삼각형의 텍스처 좌표는 다음과 같습니다.
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
이러한 좌표를 VBO (정점 버퍼 객체)에 넣고 셰이더에 대한 새 특성을 만듭니다. 버텍스 위치에 적어도 하나의 속성을 이미 가져야하므로 텍스쳐 좌표에 대해 다른 속성을 생성하십시오.
텍스처 생성하기
우선 할 일은 unsigned int 텍스처에 저장 될 ID에 의해 참조 될 텍스처 객체를 생성하는 것입니다 :
GLuint texture;
glGenTextures(1, &texture);
그 후에는 이후의 모든 텍스처 명령이이 텍스처를 구성하도록 바인드해야합니다.
glBindTexture(GL_TEXTURE_2D, texture);
이미지로드 중
이미지를로드하려면 자신 만의 이미지 로더를 만들거나 Java 의 C ++ 또는 TWL의 PNGDecoder 에서 SOIL (Simple OpenGL Image Library)과 같은 이미지로드 라이브러리를 사용할 수 있습니다.
SOIL로 이미지를로드하는 예는 다음과 같습니다.
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
이제이 이미지를 텍스처 오브젝트에 할당 할 수 있습니다 :
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
그런 다음 텍스처 객체를 바인딩 해제해야합니다.
glBindTexture(GL_TEXTURE_2D, 0);
텍스처 좌표의 랩 파라미터
위에서 볼 수 있듯이 텍스처의 왼쪽 아래 모서리에는 UV (st) 좌표 (0, 0)가 있고 텍스처의 오른쪽 위 모서리에는 좌표 (1, 1)이 있지만 메쉬의 텍스처 좌표는 모든 범위. 이를 처리하기 위해 텍스처 좌표가 텍스처에 래핑되는 방식을 정의해야합니다.
텍스처 좌표의 랩 파라미터는 GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
및 GL_TEXTURE_WRAP_R
사용하여 glTextureParameter 로 설정할 수 있습니다.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
가능한 매개 변수는 다음과 같습니다.
GL_CLAMP_TO_EDGE
는 텍스처 좌표가 [1 / 2N, 1 - 1 / 2N] 범위에 고정되도록합니다. N 은 방향의 텍스처 크기입니다.GL_CLAMP_TO_BORDER
동일하지GL_CLAMP_TO_EDGE
하지만 클램핑의 경우, 페치 된 텍셀 데이터에 의해 지정된 색으로 치환GL_TEXTURE_BORDER_COLOR
.GL_REPEAT
는 텍스처 좌표의 정수 부분을 무시하도록합니다. 텍스처가 바둑판 식으로 배열되어 있습니다.
-
GL_MIRRORED_REPEAT
: 텍스처 좌표의 정수 부분이 짝수이면 무시됩니다. 반면, 텍스처 좌표의 정수 부분이 홀수 인 경우 텍스처 좌표는 1 - frac (s)로 설정 됩니다. fract (s) 는 텍스처 좌표의 소수 부분입니다. 이로 인해 텍스처가 두 번째로 미러링 됩니다.
-
GL_MIRROR_CLAMP_TO_EDGE
는GL_CLAMP_TO_EDGE
에서와 같이 클램핑 될 좌표를 텍스쳐의 하나의 reptition에 대해GL_MIRRORED_REPEAT
대해 텍스트 좌표가 반복되도록합니다.
GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
및 GL_TEXTURE_WRAP_R
의 기본값은 GL_REPEAT
입니다.
텍스처 적용
마지막으로 수행 할 작업은 그리기 호출 전에 텍스처를 바인딩하는 것입니다.
glBindTexture(GL_TEXTURE_2D, texture);
텍스처 및 프레임 버퍼
텍스처의 이미지를 프레임 버퍼에 첨부 할 수 있으므로 해당 텍스처에 직접 렌더링 할 수 있습니다.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
참고 : 정의되지 않은 동작을 호출하기 때문에 동일한 렌더링 작업에서 동일한 텍스처를 읽고 쓸 수 없습니다. 그러나 당신은 이것을위한 render 호출 사이에 glTextureBarrier()
사용할 수 있습니다.
텍스처 데이터 읽기
glGetTexImage
함수를 사용하여 텍스처 데이터를 읽을 수 있습니다.
char *outBuffer = malloc(buf_size);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
outBuffer);
참고 : 텍스처 유형 및 형식은 예를 들어, 다를 수 있습니다.
PBO 사용
버퍼를 GL_PIXEL_UNPACK_BUFFER
바인드하면 glTexImage2D
의 data
매개 변수가 해당 버퍼로 오프셋됩니다.
즉, glTexImage2D는 반환 할 수 있기 전에 모든 데이터가 응용 프로그램의 메모리에서 복사 될 때까지 기다릴 필요가 없으므로 주 스레드의 오버 헤드가 줄어 듭니다.
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width*height*3, NULL, GL_STREAM_DRAW);
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
//write data into the mapped buffer, possibly in another thread.
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
memcpy(mappedBuffer, image, width*height*3);
SOIL_free_image(image);
// after reading is complete back on the main thread
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
그러나 PBO가 정말 빛나는 곳은 렌더링 결과를 응용 프로그램 메모리로 다시 읽어야 할 때입니다. 이 바인딩 버퍼에 픽셀 데이터를 읽어 GL_PIXEL_PACK_BUFFER
다음의 데이터 파라미터 glGetTexImage
그 버퍼에 오프셋 될 것이다 :
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, NULL, GL_STREAM_COPY);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
null);
//ensure we don't try and read data before the transfer is complete
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// then regularly check for completion
GLint result;
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(result), NULL, &result);
if(result == GL_SIGNALED){
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* mappedBuffer = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
//now mapped buffer contains the pixel data
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
GLSL 쉐이더에서 텍스처 사용하기
버텍스 쉐이더는 텍스처 좌표를 버텍스 애트리뷰트로 받아서 프래그먼트 쉐이더에 전달합니다. 기본적으로 조각은 삼각형의 위치를 기반으로 올바르게 보간 된 좌표를 받음을 보장합니다.
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordOut;
void main()
{
gl_Position = vec4(position, 1.0f);
texCoordOut = texCoordIn;
}
프래그먼트 셰이더는 texCoord
출력 변수를 입력 변수로 사용합니다. 그런 다음 uniform sampler2D
를 선언하여 프래그먼트 쉐이더에 텍스처를 추가 할 수 있습니다. 텍스처의 조각을 샘플링하기 위해 우리는 두 개의 매개 변수를 가진 내장 함수 texture
를 사용합니다. 첫 번째는 샘플링하고자하는 텍스처이고 두 번째는이 텍스처의 좌표입니다.
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
image
는 여기에 직접 텍스처 ID가 아닙니다. 샘플링 될 텍스처 유닛 의 ID입니다. 차례 차례로, 짜임새는 직접 프로그램에 묶이지 않는다; 그것들은 텍스처 유닛에 묶여있다. 이것은 glActiveTexture
텍스처 유닛을 활성화 한 다음 glBindTexture
를 호출하면이 텍스처 유닛에 영향을 미침으로써 가능합니다. 그러나 기본 텍스처 단위가 텍스처 단위 0
이기 때문에 하나의 텍스처를 사용하는 프로그램을이 호출을 생략하면 더 간단하게 만들 수 있습니다.