Computer Graphics/OpenGL

OpenGL 정리 - 7. Texture 입히기

surkim 2024. 9. 12. 14:57

OpenGL에서 텍스처를 사용하는 방법에 대해 다뤄보겠다.

텍스처는 3D 그래픽에서 물체의 표면에 이미지를 입혀 더 현실감 있는 렌더링을 가능하게 해준다. 


1. 텍스처란?

텍스처는 3D 물체에 이미지를 입히는 방식이다. 단순히 색상만 지정하는 것보다 더 사실적이고 복잡한 물체를 표현할 수 있다. 이를 통해 적은 수의 정점으로도 고품질의 렌더링을 할 수 있다.

2. 텍스처 좌표 (Texture Coordinates)

텍스처를 물체 표면에 입히기 위해서는 각 정점에 텍스처 좌표를 지정해야 한다. 텍스처 좌표는 [0, 1] 범위로 정규화된 좌표계이며, 텍스처 이미지의 좌하단을 (0, 0), 우상단을 (1, 1)으로 한다. 텍스처 좌표는 vertex attribute로 정점 셰이더에 전달되어 각 픽셀의 텍스처 좌표가 계산된다. 그 좌표를 기반으로 프래그먼트 셰이더에서 해당 텍스처의 색상을 가져와 물체에 입힌다.


3. 텍스처 래핑 (Texture Wrapping)

텍스처 좌표가 [0, 1] 범위를 넘어설 때 어떻게 처리할지 설정하는 옵션이다. OpenGL에서 제공하는 주요 텍스처 래핑 방식은 다음과 같다:

  • GL_REPEAT: 텍스처가 반복된다.
  • GL_CLAMP_TO_EDGE: 범위를 벗어나면 가장자리 픽셀이 계속 반복된다.
  • GL_MIRRORED_REPEAT: 텍스처가 거울처럼 반전되어 반복된다.

4. 텍스처 필터링 (Texture Filtering)

텍스처가 너무 크거나 작은 경우, 픽셀을 어떻게 처리할지 결정하는 옵션이다. 주요 필터링 방식은 다음과 같다:

  • GL_NEAREST: 가장 가까운 픽셀을 그대로 사용한다. 속도가 빠르지만 부드럽지 않다.
  • GL_LINEAR: 주변 픽셀 값을 평균해 부드러운 결과를 제공한다.

5. OpenGL에서 텍스처 사용하는 과정

  1. 텍스처 객체 생성 및 바인딩: OpenGL에서 텍스처 객체를 만들고 바인딩한다.
  2. 래핑 및 필터링 설정: 텍스처가 화면에 그려질 때의 래핑과 필터링 방식을 설정한다.
  3. 이미지 데이터 업로드: 이미지 데이터를 GPU에 업로드하여 텍스처로 만든다.
  4. 셰이더에 텍스처 전달: 셰이더에서 텍스처를 사용하기 위해 uniform 변수를 사용해 텍스처를 전달한다.
// 텍스처 객체 생성
glGenTextures(1, &m_texture);
glBindTexture(GL_TEXTURE_2D, m_texture);

// 래핑 및 필터링 설정
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// 이미지 데이터 업로드
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);

6. 텍스처 좌표와 셰이더에서의 사용

텍스처 좌표는 버텍스 데이터에 포함되고, 정점 셰이더에서 이를 처리한 후 프래그먼트 셰이더로 전달된다.

정점 셰이더:

#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;

out vec4 vertexColor;
out vec2 texCoord;

void main() {
    gl_Position = vec4(aPos, 1.0);
    vertexColor = vec4(aColor, 1.0);
    texCoord = aTexCoord;
}

프래그먼트 셰이더:

#version 330 core
in vec4 vertexColor;
in vec2 texCoord;
out vec4 fragColor;

uniform sampler2D tex;

void main() {
    fragColor = texture(tex, texCoord);
}

7. Mipmap과 다중 텍스처 사용

Mipmap은 텍스처의 크기를 줄여 여러 단계로 저장해, 적합한 크기의 텍스처를 골라 사용해 성능을 높이는 기법이다.

다중 텍스처는 여러 텍스처를 하나의 셰이더에서 사용하는 방법이다. 다중 텍스처를 사용할 때는 텍스처 슬롯을 이용해 각 텍스처를 셰이더에 전달한다.

// 첫 번째 텍스처 바인딩
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texture->Get());

// 두 번째 텍스처 바인딩
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_texture2->Get());

// 셰이더에 텍스처 유닛을 전달
m_program->Use();
glUniform1i(glGetUniformLocation(m_program->Get(), "tex"), 0);
glUniform1i(glGetUniformLocation(m_program->Get(), "tex2"), 1);

셰이더에서 다중 텍스처를 사용하는 방법은 다음과 같다:

프래그먼트 셰이더:

#version 330 core
in vec2 texCoord;
out vec4 FragColor;

uniform sampler2D tex;
uniform sampler2D tex2;

void main() {
    FragColor = texture(tex, texCoord) * 0.8 + texture(tex2, texCoord) * 0.2;
}

위 코드에서는 두 개의 텍스처를 블렌딩하여, 첫 번째 텍스처는 80%, 두 번째 텍스처는 20% 비율로 적용하였다.

 

다음 포스트에서 텍스처의 사용을 자세히 다뤄보자