[openGL] Sampler

나우히즈·2024년 10월 9일

Graphics

목록 보기
11/17

서론

텍스처를 다루기 위해서는 텍스처를 어떤 방식으로 처리할지를 설정해주어아야한다.

예를들어, 텍스처가 설정된 0~1 사이의 좌표값을 벗어나는 경우에 대해 어떻게 처리할지를 설정하는 래핑 모드 혹은 샘플들의 중간값을 어떻게 계산할지를 정하는 필터링 모드 가 있다.

이러한 설정들을 관리하는 객체가 Sampler 이다.


내가 들었던 생각

GLSL에서는 텍스처를 유니폼으로 가져와서 사용했었다. 그 때 그 유니폼은,

uniform sampler2D tex;

이런 식으로 처리했었다.

그렇다. 난 샘플러라는 단어를 GLSL에서 처음 보았고, 텍스처와 샘플러는 동의어라고 생각을 하고 있었다.

하지만 둘은 명확히 다른 의미를 지닌다.


샘플러와 텍스처

샘플러(Sampler)텍스처(Texture)는 그래픽스 프로그래밍에서 텍스처 데이터를 처리하고 화면에 표시할 때 중요한 개념이지만, 서로 다른 역할을 합니다. 그 차이를 정리하면 다음과 같습니다.

  1. 텍스처 (Texture)
    • 정의: 텍스처는 실제 이미지 데이터(픽셀 또는 텍셀)를 저장하고 있는 객체.
    이 데이터는 렌더링 시 표면에 입혀지거나 사용.
    • 역할: 2D 이미지, 3D 볼륨 데이터 또는 다른 형태의 텍스처 데이터를 GPU에 저장

  2. 샘플러 (Sampler)

    • 정의: 샘플러는 텍스처 좌표에서 텍스처 데이터를 어떻게 가져올지(샘플링할지) 정의하는 객체. 즉, 텍스처 데이터를 참조하는 방법을 제공하는 일종의 필터나 설정 모음.
    • 역할: 텍스처 좌표(UV)에서 정확히 어떤 텍셀 값을 가져올지(보간, 필터링, 반복 방식 등)를 제어.

    • 주요 기능:
    • 필터링 모드: 텍스처를 확대 또는 축소할 때 사용하는 필터링 방법을 설정합니다. (예: GL_NEAREST, GL_LINEAR)
    • 랩핑 모드: 텍스처 좌표가 0~1 범위를 벗어났을 때 어떻게 처리할지 설정합니다. (예: GL_REPEAT, GL_CLAMP_TO_EDGE)
    • Mipmap: 텍스처의 여러 해상도 레벨을 저장하여 원근감을 표현할 때 사용되며, 샘플러에서 이를 사용할지 설정할 수 있습니다.


이렇듯 샘플러는 텍스처의 설정을 담는 객체 역할을 한다. 따라서 다른 객체와 마찬가지로 아래 함수들을 가지고 있다.

glGenSamplers(GLsizei n, GLuint * samples);
glSamplerParameteri(GLuint sampler, GLenum pname, GLint param);
glSamplerParameterf(GLuint sampler, GLenum pname, GLfloat param);

glBindSampler(GLuint unit, GLuint sampler);

위에 사용된 샘플러 함수들은 OpenGL에서 샘플러 오브젝트를 생성하고 설정하는데 사용된다. 샘플러 오브젝트는 텍스처의 필터링 및 래핑 모드 같은 설정을 텍스처와 독립적으로 관리할 수 있게 도와준다. 각 함수의 역할을 하나씩 살펴보자.

  1. glGenSamplers(GLsizei n, GLuint *samplers)

    • 역할: 새로운 샘플러 오브젝트를 생성하는 함수다.
    • 인자:
    n: 생성할 샘플러 오브젝트의 개수를 지정한다.
    samplers: 생성된 샘플러 오브젝트의 ID를 저장할 변수(혹은 배열)를 가리킨다. 생성된 샘플러 ID가 sampler 변수에 저장된다.

  2. glSamplerParameteri(GLuint sampler, GLenum pname, GLint param)
    • 역할: 생성된 샘플러 오브젝트의 특정 설정(필터링 또는 래핑 모드)을 지정하는 함수.
    • 인자:
    sampler: 설정을 적용할 샘플러 오브젝트의 ID.
    pname: 설정하려는 속성의 종류를 지정한다.
    param: 속성에 적용할 값.

  3. glBindSampler(GLuint unit, GLuint sampler)

    • 역할: 특정 텍스처 유닛에 샘플러 오브젝트를 바인딩한다.
    • 인자:
    unit: 샘플러를 바인딩할 텍스처 유닛의 번호다.
    sampler: 바인딩할 샘플러 오브젝트의 ID다. 이 샘플러가 지정한 유닛에서 텍스처의 필터링 및 래핑 모드를 정의한다.

이처럼 샘플링 객체에 샘플링 세팅을 입력해줄 수 있었다.
하지만 나는 강의에서 이런 샘플링 함수들을 사용하지 않고, glTexParameteri 와 같은 함수를 통해 직접 텍스처에 설정값을 입력했었다.

이런 방식은 텍스처 자체에 내장되어있는 샘플러에 설정값을 입력해주는 방식이다. 텍스처 각각에 샘플링방식을 내장하여 별도의 샘플러 객체 없이 사용할 수 있었다.

다소 유연성은 떨어질 수 있으나, 간편하고 빠르게 작성할 수 있는 방식이었다.

여러 텍스처를 사용해보자

텍스처를 여러개 사용하려면 여러번 만들어서 여러번 바인딩해서 여러번 유니폼으로 보내주면 된(?)다.

너무나 당연하지만 한가지 주의할점은, 현재 컨텍스트에 텍스처를 바인딩 하기 전에 glActiveTexture 함수를 사용하여 현재 선택할 텍스처를 지정해줘야한다는 점이다.

uint32_t textures[3];

glGenTexture(3, &textures);

for (auto i = 0; i < 3; i++)
{
	glActiveTexture(GL_TEXTURE0 + i);
    glBindTexture(GL_TEXTURE_2D, textures[i]);
}

이후 바인딩된 텍스처와 쉐이더 내에 사용되고 있는 유니폼 샘플러와 연결해줘야한다.

	glUniform1i(glGetUniformLocation(m_program->Get(), "tex1"), 0);
	glUniform1i(glGetUniformLocation(m_program->Get(), "tex2"), 1);

현재 프로그램에서 사용되고 있는 쉐이더를 지정해야해서, m_program->Get()을 통해 프로그램의 주소를 첫 번째 인자로 받고, 해당 프로그램 내의 쉐이더에 담긴 유니폼의 이름을 두번째 인자로 넣어 glGetUniformLocation 함수를 동작시키자.

이에 유니폼의 주소를 리턴하게 되면, 각 텍스처가 몇번 텍스처 슬롯에 담겼는지(GL_TEXTURE0, GL_TEXTURE1 , ...) 를 명시해주기 위해 정수를 입력해준다.

예를 들어, 위의 코드는 tex1이라는 유니폼 샘플러는 GL_TEXTURE0 에 담긴 텍스처를 이용하게 하는 것이 된다.

0개의 댓글