본문 바로가기
Opengles 3.0 with Android

Chapter 7.4 FBO

by SimonLee 2024. 11. 17.

FBO를 잘 이해하기 위해서는 아래 챕터 내용을 먼저 보고 오자.

 

이전 챕터에서 원이 반복적으로 있는 패턴의 모양 ( 폴카닷 )을 렌더링 했었다.

https://graphicsimon.tistory.com/114

폴카닷을 렌더링 할때 FrameBuffer Object를 사용하여 텍스처로 렌더링 하는 코드 설명한다.

objModel->Render() 함수를 수행하면 폴카닷을 1 프레임 렌더링 한다고 이해하자.

 

텍스처 렌더링도 아래 챕터를 참고한다.

https://graphicsimon.tistory.com/115

까만 영역에 폴카닷 렌더링 결과를 넣을 것임.

결과

 

FBO (Framebuffer Object)란?

**FBO (Framebuffer Object)**는 OpenGL에서 **오프스크린 렌더링(Offscreen Rendering)**을 위한 기능입니다. 즉, 화면에 직접 출력하지 않고, 텍스처나 렌더버퍼에 렌더링할 수 있게 해줍니다.

FBO의 주요 특징

  1. 오프스크린 렌더링:
    • 렌더링 결과를 화면 대신 텍스처렌더버퍼에 저장할 수 있습니다.
    • 이를 통해 그림자 매핑, 포스트 프로세싱, 반사 효과와 같은 다양한 그래픽 효과를 구현할 수 있습니다.
  2. 사용자 정의 렌더 타겟:
    • 기본 프레임버퍼 대신, 사용자가 정의한 FBO로 렌더링할 수 있습니다.
    • FBO에 연결된 텍스처렌더버퍼는 이후에 텍스처 맵핑이나 후처리에 활용할 수 있습니다.
  3. 성능 향상:
    • 화면에 직접 렌더링하지 않고, 텍스처에 렌더링하면 필요한 작업만 수행할 수 있어 성능을 최적화할 수 있습니다.

FBO의 구조

  • FBO는 다음과 같은 **어태치먼트(Attachment)**로 구성됩니다:
    • 컬러 어태치먼트: 렌더링된 결과의 색상 데이터를 저장하는 컬러 텍스처.
    • 깊이 어태치먼트: 깊이 정보를 저장하는 깊이 텍스처 또는 렌더버퍼.
    • 스텐실 어태치먼트(옵션): 스텐실 테스트에 사용되는 데이터를 저장.

FBO의 일반적인 사용 흐름

  1. FBO 생성 및 바인드:
    • glGenFramebuffers()와 glBindFramebuffer()로 FBO를 생성하고 바인드합니다.
  2. 어태치먼트 설정:
    • glFramebufferTexture2D()로 컬러 텍스처를 연결하고, glFramebufferRenderbuffer()로 깊이 버퍼를 연결합니다.
  3. 렌더링 수행:
    • FBO가 바인드된 상태에서 렌더링 작업을 수행합니다.
  4. 기본 프레임버퍼로 전환:
    • 작업이 끝나면 기본 프레임버퍼로 전환(glBindFramebuffer(GL_FRAMEBUFFER, 0))합니다.
  5. 렌더링 결과 활용:
    • FBO에 저장된 텍스처를 사용해 포스트 프로세싱이나 텍스처 맵핑 작업을 수행합니다.

 

-----------------------------------------------------------------------------------------------

Initialize

 

//! DemoFBO 클래스 생성자
DemoFBO::DemoFBO(Renderer* parent)
{
    // 부모 객체가 없을 경우, 함수 종료
    if (!parent)
        return;

    // 1. 렌더러 핸들러 설정
    RendererHandler = parent;
    ProgramManagerObj = parent->RendererProgramManager(); // 프로그램 매니저 객체 설정
    TransformObj = parent->RendererTransform();           // 변환 객체 설정

    // 2. 3D 모델과 텍스처 쿼드 객체 생성
    objModel = new ObjLoader(RendererHandler);     // 3D 객체 로더 초기화
    textureQuad = new SimpleTexture(RendererHandler); // 텍스처가 맵핑된 쿼드 초기화
}

//! DemoFBO 모델 초기화 함수
void DemoFBO::InitModel()
{
    // 1. 3D 객체 모델 초기화
    objModel->InitModel();

    // 2. 텍스처 쿼드 모델 초기화
    textureQuad->InitModel();

    // 3. FBO 생성
    GenerateFBO();
}

 

 

//! 텍스처 생성 함수: 컬러 텍스처 또는 깊이 텍스처를 생성합니다.
unsigned int DemoFBO::generateTexture(int width, int height, bool isDepth, bool isStencil)
{
    unsigned int texId;

    // 1. 텍스처 객체 생성 및 바인드
    glGenTextures(1, &texId);
    glBindTexture(GL_TEXTURE_2D, texId);

    // 2. 텍스처 필터링 설정
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);             // 텍스처 확대 필터: GL_LINEAR
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); // 텍스처 축소 필터: Mipmap 사용
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);           // S축 래핑 모드: GL_CLAMP_TO_EDGE
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);           // T축 래핑 모드: GL_CLAMP_TO_EDGE

    // 3. 텍스처 데이터 할당 (깊이 텍스처 또는 컬러 텍스처)
    if (isDepth) {
        // 깊이 텍스처 생성
        glTexImage2D(GL_TEXTURE_2D,               // 텍스처 타겟: GL_TEXTURE_2D
                     0,                           // Mipmap 레벨: 0 (기본 레벨)
                     GL_DEPTH_COMPONENT32F,       // 내부 포맷: 32비트 깊이 컴포넌트
                     width, height,               // 텍스처의 너비와 높이
                     0,                           // 경계선: 0
                     GL_DEPTH_COMPONENT,          // 포맷: 깊이 컴포넌트
                     GL_FLOAT,                    // 데이터 타입: GL_FLOAT
                     0);                          // 데이터 포인터: NULL (비어 있는 텍스처)
    } 
    else if (isStencil) {
        glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, width, height, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
    } 
    else {
        // 컬러 텍스처 생성
        glTexImage2D(GL_TEXTURE_2D,               // 텍스처 타겟: GL_TEXTURE_2D
                     0,                           // Mipmap 레벨: 0 (기본 레벨)
                     GL_RGBA,                     // 내부 포맷: RGBA
                     width, height,               // 텍스처의 너비와 높이
                     0,                           // 경계선: 0
                     GL_RGBA,                     // 포맷: RGBA
                     GL_UNSIGNED_BYTE,            // 데이터 타입: GL_UNSIGNED_BYTE
                     0);                          // 데이터 포인터: NULL (비어 있는 텍스처)
    }

    // 4. 오류 체크
    int error = glGetError();
    if (error != 0) {
        std::cout << "Error: Fail to generate texture. Error code: " << error << std::endl;
    }

    // 5. 텍스처 바인딩 해제
    glBindTexture(GL_TEXTURE_2D, 0);

    // 6. 생성된 텍스처 ID 반환
    return texId;
}

 

GenerateFBO()와 GenerateFBOWithRenderBuffer()의 차이점

두 함수 모두 OpenGL에서 **FBO(Frame Buffer Object)**를 생성하지만, 깊이(Depth) 어태치먼트를 어떻게 설정하느냐에 차이가 있습니다.

  1. GenerateFBO()
    • 깊이 어태치먼트로 깊이 텍스처를 사용합니다.
    • 깊이 정보는 텍스처에 저장되며, 이후 다른 렌더링 단계에서 텍스처 맵핑으로 사용할 수 있습니다.
    • 장점:
      • 깊이 텍스처를 사용하면 그림자 매핑이나 포스트 프로세싱에서 깊이 정보를 직접 활용할 수 있습니다.
    • 단점:
      • 깊이 텍스처는 메모리 사용이 높을 수 있으며, 성능이 렌더버퍼보다 떨어질 수 있습니다.
  2. GenerateFBOWithRenderBuffer()
    • 깊이 어태치먼트로 렌더버퍼를 사용합니다.
    • 깊이 정보는 렌더버퍼에 저장되며, 이후 텍스처로 사용되지 않고 깊이 테스트에만 사용됩니다.
    • 장점:
      • 렌더버퍼는 성능이 더 좋고 메모리 사용이 적습니다.
      • 렌더링 속도가 더 빠르며, 깊이 정보가 필요한 경우에만 사용됩니다.
    • 단점:
      • 렌더버퍼에 저장된 깊이 정보를 텍스처로 직접 접근할 수 없습니다.
      • 텍스처 맵핑에 사용할 수 없기 때문에, 그림자 매핑 등에서는 적합하지 않습니다.
//! Render Buffer의 깊이 버퍼를 사용하는 FBO 생성 함수
void DemoFBO::GenerateFBOWithRenderBuffer()
{
    // 1. 프레임버퍼 객체 생성 및 바인드
    glGenFramebuffers(1, &fboId);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);

    // 2. 텍스처 생성 및 FBO의 컬러 어태치먼트에 연결
    textureId = generateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);
    glFramebufferTexture2D(GL_FRAMEBUFFER,         // FBO 타겟: GL_FRAMEBUFFER
                           GL_COLOR_ATTACHMENT0,   // 컬러 어태치먼트 포인트
                           GL_TEXTURE_2D,          // 텍스처 타겟: GL_TEXTURE_2D
                           textureId,              // 텍스처 ID
                           0);                     // Mipmap 레벨: 0 (기본 레벨)

    // 3. 깊이 정보를 저장할 렌더버퍼 객체 생성 및 설정
    glGenRenderbuffers(1, &rboId);
    glBindRenderbuffer(GL_RENDERBUFFER, rboId);
    glRenderbufferStorage(GL_RENDERBUFFER,         // 렌더버퍼 타겟: GL_RENDERBUFFER
                          GL_DEPTH_COMPONENT16,    // 깊이 포맷: GL_DEPTH_COMPONENT16
                          TEXTURE_WIDTH,           // 텍스처의 너비
                          TEXTURE_HEIGHT);         // 텍스처의 높이

    // 4. 렌더버퍼를 FBO의 깊이 어태치먼트에 연결
    glFramebufferRenderbuffer(GL_FRAMEBUFFER,      // FBO 타겟: GL_FRAMEBUFFER
                              GL_DEPTH_ATTACHMENT, // 깊이 어태치먼트 포인트
                              GL_RENDERBUFFER,     // 렌더버퍼 타겟: GL_RENDERBUFFER
                              rboId);              // 렌더버퍼 ID

    // 5. FBO 상태 체크
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Framebuffer creation failed: %d\n", status);
    }

    // 6. FBO 바인딩 해제
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

 

Depth Buffer를 텍스처로 구현 예정.

//! FBO 생성 함수: 프레임버퍼의 깊이 텍스처를 사용하여 깊이 테스트를 수행
void DemoFBO::GenerateFBO()
{
    // 1. 프레임버퍼 객체(FBO) 생성 및 바인드
    glGenFramebuffers(1, &fboId);
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);

    // 2. 컬러 텍스처와 깊이 텍스처 생성
    textureId = generateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT);         // 컬러 텍스처 생성
    depthTextureId = generateTexture(TEXTURE_WIDTH, TEXTURE_HEIGHT, true); // 깊이 텍스처 생성 (depth = true)

    // 3. 컬러 텍스처를 FBO의 컬러 어태치먼트에 연결
    glFramebufferTexture2D(GL_FRAMEBUFFER,         // FBO 타겟: GL_FRAMEBUFFER
                           GL_COLOR_ATTACHMENT0,   // 컬러 어태치먼트 포인트
                           GL_TEXTURE_2D,          // 텍스처 타겟: GL_TEXTURE_2D
                           textureId,              // 컬러 텍스처 ID
                           0);                     // Mipmap 레벨: 0 (기본 레벨)

    // 4. 깊이 텍스처를 FBO의 깊이 어태치먼트에 연결
    glFramebufferTexture2D(GL_FRAMEBUFFER,         // FBO 타겟: GL_FRAMEBUFFER
                           GL_DEPTH_ATTACHMENT,    // 깊이 어태치먼트 포인트
                           GL_TEXTURE_2D,          // 텍스처 타겟: GL_TEXTURE_2D
                           depthTextureId,         // 깊이 텍스처 ID
                           0);                     // Mipmap 레벨: 0 (기본 레벨)
  
/*
    // 스텐실 enable 하니깐 오류나네..
    // heckFramebufferCompleteness: stencil attachment not complete: 0x8cd6 ERROR 발생.
    glFramebufferTexture2D(GL_FRAMEBUFFER,        // 1. fbo target: GL_FRAMEBUFFER
                           GL_DEPTH_STENCIL_ATTACHMENT,   // 2. Depth attachment point
                           GL_TEXTURE_2D,         // 3. tex target: GL_TEXTURE_2D
                           stencilTextureId,        // 4. stencil texture ID
                           0);                    // 5. mipmap level: 0(base)
*/
    // 5. FBO 상태 체크
    GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
    if (status != GL_FRAMEBUFFER_COMPLETE) {
        printf("Framebuffer creation failed: %d\n", status);
    }

    // 6. FBO 바인딩 해제 (기본 프레임버퍼로 전환)
    glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

 

 

void glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level);

매개변수 설명

  1. target (GLenum):
    • FBO의 타겟을 지정합니다.
    • 일반적으로 **GL_FRAMEBUFFER**를 사용합니다.
    • GL_READ_FRAMEBUFFER 또는 GL_DRAW_FRAMEBUFFER로도 설정할 수 있지만, 대부분의 경우 GL_FRAMEBUFFER가 사용됩니다.
  2. attachment (GLenum):
    • 텍스처를 연결할 어태치먼트 포인트를 지정합니다.
    • 일반적으로 다음과 같은 값이 사용됩니다:
      • GL_COLOR_ATTACHMENT0: 컬러 어태치먼트.
      • GL_DEPTH_ATTACHMENT: 깊이(Depth) 어태치먼트.
      • GL_STENCIL_ATTACHMENT: 스텐실(Stencil) 어태치먼트.
      • GL_DEPTH_STENCIL_ATTACHMENT: 깊이와 스텐실이 결합된 어태치먼트.
  3. textarget (GLenum):
    • 텍스처의 타겟을 지정합니다.
    • 일반적으로 **GL_TEXTURE_2D**가 사용됩니다.
    • 큐브 맵 텍스처의 경우, GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X 등으로 설정할 수 있습니다.
  4. texture (GLuint):
    • 연결할 텍스처의 ID입니다.
    • **glGenTextures()**로 생성된 텍스처 ID를 전달합니다.
    • 텍스처를 제거하려면 **0**을 전달할 수 있습니다.
  5. level (GLint):
    • 연결할 텍스처의 Mipmap 레벨을 지정합니다.
    • 일반적으로 기본 레벨인 **0**이 사용됩니다.

 

 

Rendering

void DemoFBO::Render()
{
    // 1. 현재 바인딩된 프레임버퍼를 저장
    int CurrentFbo;
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &CurrentFbo);

    // 2. FBO로 렌더링 시작
    glBindFramebuffer(GL_FRAMEBUFFER, fboId);
    glViewport(0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT);              // FBO 크기에 맞게 뷰포트 설정
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);           // 컬러와 깊이 버퍼 클리어
    objModel->Render();                                           // 3D 객체 렌더링 (텍스처에 렌더링)

    // 3. 기본 프레임버퍼로 다시 전환
    glBindFramebuffer(GL_FRAMEBUFFER, CurrentFbo);
    TransformObj->TransformError();                               // 변환 오류 체크 (디버깅 용도)

    // 4. 텍스처가 맵핑된 쿼드 렌더링
    glViewport(0, 0, RendererHandler->screenWidthPixel() * 2, RendererHandler->screenHeightPixel() * 2); // 전체 화면 뷰포트 설정
    glClearColor(0.710, 0.610, 0.30, 1.0);                        // 화면 배경색 설정
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);           // 컬러와 깊이 버퍼 클리어
    glActiveTexture(GL_TEXTURE0);                                 // 텍스처 유닛 0 활성화
    glBindTexture(GL_TEXTURE_2D, textureId);                      // 렌더된 텍스처 바인드
    textureQuad->Render();                                        // 텍스처가 맵핑된 쿼드 렌더링
    TransformObj->TransformError();                               // 변환 오류 체크 (디버깅 용도)
}

 

 

Texture Rendering

// 텍스처 좌표 (2D 텍스처 맵핑)
float texCoords[8] = {
    0.0f, 0.0f,   // 왼쪽 아래
    1.0f, 0.0f,   // 오른쪽 아래
    0.0f, 1.0f,   // 왼쪽 위
    1.0f, 1.0f    // 오른쪽 위
};

// 쿼드 정점 좌표 (2D 평면)
float quad[12] = {
    -1.0f, -1.0f,  0.0f,   // 왼쪽 아래
     1.0f, -1.0f,  0.0f,   // 오른쪽 아래
    -1.0f,  1.0f,  0.0f,   // 왼쪽 위
     1.0f,  1.0f,  0.0f    // 오른쪽 위
};

void SimpleTexture::InitModel()
{
    ProgramManager* ProgramManagerObj = RendererHandler->RendererProgramManager();
    Transform* TransformObj = RendererHandler->RendererTransform();

    // 1. 셰이더 프로그램 초기화
    if (!(program = ProgramManagerObj->Program((char*)"square"))) {
        program = ProgramManagerObj->ProgramInit((char*)"square");
        ProgramManagerObj->AddProgram(program);
    }

    // 2. 셰이더 컴파일
    program->VertexShader = ShaderManager::ShaderInit(VERTEX_SHADER_PRG, GL_VERTEX_SHADER);
    program->FragmentShader = ShaderManager::ShaderInit(FRAGMENT_SHADER_PRG, GL_FRAGMENT_SHADER);

    // 3. 정점 셰이더 컴파일
    CACHE* m = reserveCache(VERTEX_SHADER_PRG, true);
    if (m) {
        if (!ShaderManager::ShaderCompile(program->VertexShader, (char*)m->buffer, 1)) exit(1);
        freeCache(m);
    }

    // 4. 프래그먼트 셰이더 컴파일
    m = reserveCache(FRAGMENT_SHADER_PRG, true);
    if (m) {
        if (!ShaderManager::ShaderCompile(program->FragmentShader, (char*)m->buffer, 1)) exit(2);
        freeCache(m);
    }

    // 5. 셰이더 프로그램 링크
    if (!ProgramManagerObj->ProgramLink(program, 1)) exit(3);

    glUseProgram(program->ProgramID);

    // 6. 유니폼 위치 가져오기
    MVP = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"MODELVIEWPROJECTIONMATRIX");
    TEX = ProgramManagerObj->ProgramGetUniformLocation(program, (char*)"Tex1");
}

void SimpleTexture::Render()
{
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glDisable(GL_CULL_FACE);                // 백페이스 컬링 비활성화
    glUseProgram(program->ProgramID);       // 셰이더 프로그램 사용
    glEnable(GL_BLEND);                     // 블렌딩 활성화
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    // 1. 변환 적용 (회전)
    TransformObj->TransformPushMatrix();
    TransformObj->TransformRotate(rotationX, 0.0, 1.0, 0.0);
    TransformObj->TransformRotate(rotationY, 1.0, 0.0, 0.0);

    // 2. 텍스처 필터링 및 래핑 설정
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    // 3. 텍스처 유니폼 설정
    glUniform1i(TEX, 0);

    // 4. 정점 속성 활성화 및 설정
    glEnableVertexAttribArray(VERTEX_POSITION);
    glEnableVertexAttribArray(TEX_COORD);
    glVertexAttribPointer(TEX_COORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
    glVertexAttribPointer(VERTEX_POSITION, 3, GL_FLOAT, GL_FALSE, 0, quad);

    // 5. MVP 행렬 설정
    glUniformMatrix4fv(MVP, 1, GL_FALSE, (float*)TransformObj->TransformGetModelViewProjectionMatrix());
    TransformObj->TransformPopMatrix();

    // 6. 쿼드 렌더링
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    // 7. 정점 속성 비활성화
    glDisableVertexAttribArray(VERTEX_POSITION);
    glDisableVertexAttribArray(TEX_COORD);

    // 8. 회전 애니메이션 업데이트
    if (deltaX != 0.0) {
        if (deltaX > 0.0) deltaX -= DecelerationFactor;
        else deltaX += DecelerationFactor;
        rotationX += deltaX;
    }

    if (deltaY != 0.0) {
        if (deltaY > 0.0) deltaY -= DecelerationFactor;
        else deltaY += DecelerationFactor;
        rotationY += deltaY;
    }
}

 

Vertex Shader

#version 300 es

// 1. 정점 정보 입력 (버텍스 속성)
layout(location = 0) in vec3 VertexPosition;   // 정점의 위치 (layout(location = 0))
layout(location = 1) in vec2 VertexTexCoord;   // 정점의 텍스처 좌표 (layout(location = 1))

// 2. 출력 변수
out vec2 TexCoord;                             // 텍스처 좌표 출력 (프래그먼트 셰이더로 전달)

// 3. 유니폼 변수 (모델-뷰-프로젝션 행렬)
uniform mat4 MODELVIEWPROJECTIONMATRIX;        // MVP 행렬 (모델, 뷰, 프로젝션 합성)

void main(void) {
    // 4. 텍스처 좌표 전달
    TexCoord = VertexTexCoord;

    // 5. 변환된 정점 위치 계산 (클립 공간)
    gl_Position = MODELVIEWPROJECTIONMATRIX * vec4(VertexPosition, 1.0);
}

 

Fragment Shader

#version 300 es

// 1. 정밀도 설정
precision mediump float;                   // 부동 소수점 연산의 정밀도를 중간(mediump)으로 설정

// 2. 입력 변수 (정점 셰이더에서 전달된 텍스처 좌표)
in vec2 TexCoord;                          // 텍스처 좌표 입력

// 3. 유니폼 변수 (샘플러)
uniform sampler2D Tex1;                    // 2D 텍스처 샘플러 (GL_TEXTURE_2D)

// 4. 출력 변수 (프래그먼트 셰이더의 최종 출력 색상)
layout(location = 0) out vec4 soutColor;   // 출력 색상 (layout(location = 0))

void main() {
    // 5. 텍스처 샘플링
    // - `texture()` 함수는 텍스처(Tex1)에서 텍스처 좌표(TexCoord)를 사용해 색상을 샘플링합니다.
    soutColor = texture(Tex1, TexCoord);
}

 

 

참고  스텐실 버퍼

이유: 깊이-스텐실 렌더버퍼를 함께 사용하는 이유

  1. 성능 향상 및 메모리 효율성:
    • 깊이와 스텐실 데이터를 하나의 렌더버퍼에 저장하면, 메모리 접근이 줄어들고 성능이 향상됩니다.
    • 하드웨어에서 깊이와 스텐실 데이터를 동시에 처리할 수 있기 때문에, 메모리 대역폭과 성능 측면에서 유리합니다.
    • 대부분의 GPU는 깊이-스텐실 버퍼를 하나의 버퍼로 처리하도록 최적화되어 있습니다.
  2. 깊이 테스트와 스텐실 테스트는 자주 함께 사용됨:
    • **깊이 테스트(Depth Test)**와 **스텐실 테스트(Stencil Test)**는 보통 함께 사용됩니다.
    • 예를 들어, **다중 패스 렌더링(Multi-pass Rendering)**에서는 깊이와 스텐실 테스트를 동시에 수행하여, 렌더링 결과를 제어합니다.
    • 그림자 매핑(Shadow Mapping), 포스트 프로세싱 효과에서도 깊이와 스텐실 정보를 동시에 사용하는 경우가 많습니다.
  3. 깊이-스텐실 포맷이 표준화되어 있음:
    • OpenGL에서는 깊이-스텐실 포맷(GL_DEPTH24_STENCIL8, GL_DEPTH32F_STENCIL8)이 표준화되어 있으며, 대부분의 하드웨어에서 지원합니다.
    • 이 포맷은 깊이 값과 스텐실 값을 효율적으로 저장할 수 있는 포맷으로, 메모리 사용을 줄이고 성능을 최적화합니다.

스텐실 버퍼의 사용 사례

  • 스텐실 테스트: 객체의 특정 부분에만 렌더링하거나, 다중 패스 렌더링에서 사용.
  • 마스킹 효과: 특정 영역을 마스킹하여 후속 렌더링 작업에서 제외.
  • 윤곽선 렌더링: 스텐실 버퍼를 사용해 객체의 윤곽선만 렌더링.
  • 스텐실 테스트는 보통 비교 연산, 증가/감소, 비트 연산 등의 간단한 연산으로 이루어집니다.
  • 이러한 연산에서는 8비트면 충분합니다.
    • 예를 들어, 객체의 윤곽선 렌더링에서는 **1비트(0 또는 1)**만 사용되기도 합니다.
    • 마스킹 효과에서는 0부터 255 사이의 값을 사용하여, 여러 단계의 마스크를 표현할 수 있습니다