Computer Graphics/OpenGL

OpenGL 정리 - 15. Texture Map 적용

surkim 2024. 9. 23. 13:23

이번 포스팅에서는 Fragment Shader에서 텍스처를 적용하는 방법에 대해 다루겠다. 텍스처는 물체 표면에 색상을 입히는 중요한 역할을 하며, 이를 텍스처 맵핑(Texture Mapping)이라고 한다. 텍스처를 적용하려면 텍스처 이미지를 로드하고, 셰이더에서 사용할 수 있도록 유니폼으로 넘겨주는 과정이 필요하다.


1. 텍스처 로딩 및 유니폼 설정

먼저, 텍스처 이미지를 로드하고 이를 OpenGL에 바인딩한 후, 셰이더에서 사용할 수 있도록 유니폼으로 설정하는 과정을 살펴보겠다.

텍스처 이미지 로드

// 텍스처 이미지 로드 및 생성
m_material.diffuse = Texture::CreateFromImage(
    Image::Load("./image/container2.png").get()
);
m_material.specular = Texture::CreateFromImage(
    Image::Load("./image/container2_specular.png").get();

위 코드는 두 개의 텍스처 이미지를 로드하는 부분이다. container2.pngdiffuse 텍스처로, container2_specular.pngspecular 텍스처로 사용된다. 각각의 이미지를 Texture 객체로 생성하여 m_material.diffusem_material.specular에 저장했다.

텍스처 유닛 활성화 및 바인딩

glActiveTexture(GL_TEXTURE0);
m_material.diffuse->Bind();
glActiveTexture(GL_TEXTURE1);
m_material.specular->Bind();

텍스처를 로드한 후에는 OpenGL에서 텍스처를 사용할 수 있도록 텍스처 유닛을 활성화하고, 각각의 텍스처를 바인딩한다. GL_TEXTURE0GL_TEXTURE1에 각각 diffusespecular 텍스처가 바인딩된다.

유니폼으로 텍스처 넘기기

m_program->Use();
m_program->SetUniform("material.diffuse", 0);   // 텍스처 유닛 0에 바인딩된 diffuse 텍스처
m_program->SetUniform("material.specular", 1);  // 텍스처 유닛 1에 바인딩된 specular 텍스처
m_program->SetUniform("material.shininess", m_material.shininess);

텍스처 유닛에 텍스처를 바인딩한 후, 셰이더에 넘길 때는 SetUniform() 함수를 사용하여 텍스처 유니폼(material.diffuse, material.specular)을 각각 텍스처 유닛 0텍스처 유닛 1에 매핑한다.


2. Fragment Shader에서 텍스처 적용

이제 로드된 텍스처를 Fragment Shader에서 사용하는 방법을 보자. 텍스처는 sampler2D 타입으로 정의되며, texture() 함수를 통해 텍스처 좌표(texCoord)에 대응하는 픽셀 값을 가져온다.

Fragment Shader 코드

#version 330 core
in vec3 normal;
in vec2 texCoord;  // 텍스처 좌표
in vec3 position;
out vec4 fragColor;

uniform vec3 viewPos;

struct Light {
    vec3 position;
    vec3 ambient;
    vec3 diffuse;
    vec3 specular;
};
uniform Light light;

struct Material {
    sampler2D diffuse;  // Diffuse 텍스처
    sampler2D specular; // Specular 텍스처
    float shininess;
};
uniform Material material;

void main() {
    // 텍스처에서 색상 가져오기
    vec3 texColor = texture(material.diffuse, texCoord).xyz;
    vec3 ambient = texColor * light.ambient;

    // Diffuse 값 계산
    vec3 lightDir = normalize(light.position - position);
    vec3 pixelNorm = normalize(normal);
    float diff = max(dot(pixelNorm, lightDir), 0.0);
    vec3 diffuse = diff * texColor * light.diffuse;

    // Specular 값 계산
    vec3 specColor = texture(material.specular, texCoord).xyz;
    vec3 viewDir = normalize(viewPos - position);
    vec3 reflectDir = reflect(-lightDir, pixelNorm);
    float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess);
    vec3 specular = spec * specColor * light.specular;

    // 최종 색상 계산
    vec3 result = ambient + diffuse + specular;
    fragColor = vec4(result, 1.0);
}

텍스처 적용 과정:

  1. Diffuse 텍스처 적용: sampler2D 타입의 material.diffuse에서 texture() 함수를 사용해 텍스처 좌표(texCoord)에 해당하는 색상을 가져온다. 이 값은 물체의 기본 색상이며, ambient와 diffuse 계산에 사용된다.
  2. Specular 텍스처 적용: material.specular에서 텍스처 좌표를 사용해 specular 텍스처의 색상을 가져온다. 이 텍스처는 물체의 반사광을 결정하는 데 사용된다.
  3. 최종 색상 계산: Ambient, Diffuse, Specular 값을 더해 물체 표면의 최종 색상을 계산한다. 이 값은 fragColor로 저장되어 화면에 출력된다.

이렇게 텍스처 이미지를 로드한 후 셰이더에 유니폼으로 넘겨서 Fragment Shader에서 텍스처 좌표를 기반으로 색상을 계산하는 과정을 살펴봤다. 텍스처는 물체의 기본 색상뿐만 아니라 빛에 의한 반사 효과를 추가하는 데도 중요한 역할을 한다.

3. 결과

0