Advanced OpenGL(10) - Anti-Aliasing

흑빡·2026년 7월 8일

그래픽스

목록 보기
45/50

안티 앨리어싱

학교 그래픽스 시험에서 안티 앨리어싱 문제가 나왔는데 개어려웟음
근데 공부 오지게 한 보람있게 안티 앨리어싱 문제 맞추고 총합 A맞으면서 그래픽스 과락안함 개쩐당

기본적으로 필터가 있음
박스 필터, 가우시안 필터 등...

이 필터들은 해당 픽셀의 색상을 결정지을때, 주변의 픽셀에서 어느정도의 가중치를 가지고 픽셀을 결정지을거냐를 말함

왼쪽이 가우시안 필터, 오른쪽이 박스 필터임
가우시안 필터는 가우시안 분포를 통해 근사 가중치를 이용함
박스 필터는 주변 모든 픽셀의 균등 가중치를 이용함

결국 kernel을 이용해서 픽셀의 색상을 결정짓는다고 생각하면 됨

주로 거론되는 안티 앨리어싱은
TAA, MSAA, SSAA, DLSS등이 있음
TAA는 가우시안 필터를 이용해 이전 프레임과 현재 프레임의 픽셀 색상을 계산하는 안티 앨리어싱
MSAA는 박스 필터를 이용해 polygon의 가장자리(테두리) 픽셀을 2X, 4X, 8X등으로 쪼개어서 픽셀의 평균 색상을 계산하는 안티 앨리어싱
SSAA는 전체 화면에 대해 x배수로 크기를 키운다음 색상을 계산하고 다시 다운그레이드 시키는 방식으로 색상을 적용하는 안티 앨리어싱
DLSS는 딥러닝을 이용한 안티 앨리어싱임

가장 구현하기 쉽고 성능도 나쁘지 않은 안티 앨리어싱은 MSAA임
따라서 그걸 해보겠음

CPP로 RayTracing 구현하기 - 4. 안티앨리어싱
여기서 박스 필터링이 어떻게 구현되는지 살펴봤음
궁금하면 체크 ㄱㄱ

MSAA

이렇게 rasterizated 되었다고 해보자

이 픽셀들에 fragment shader가 빨간색으로 씌워지면

이렇게 보일거임

여기서 MSAA를 적용하려면 어떻게 해야하냐...
바로 픽셀을 n등분하는거임

bresenham 알고리즘

픽셀사이의 선을 이을때, 해당 선이 픽셀위에서 지나가는데,
진행방향이 가로냐 세로냐에 따라 픽셀의 중앙을 설정하고,
해당 중앙을 넘어가면 해당 픽셀에 점을 찍고,
중앙을 넘어가지 못하면 가로, 세로에 따라 알맞은 방향 - 1위치의 픽셀에 점을 찍어서 이어버리는 rasterize알고리즘임

만약 특정 픽셀의 일부분이 덮여있다고 플래그가 켜진다면...
MSAA를 하게됨

근데 문제가 있음

fragment shader를 서브샘플 수만큼, 예를들어 8x MSAA라면 8번 수행해야한다?
말도 안되는거임
그럼 SSAA랑 다를게 없는거임

fragment shader는 단 1번만 실행될거임

그럼 어떻게 색상 결정을 하냐?

  1. 깊이/스텐실 버퍼를 이용해 서브샘플 범위를 설정함 (4X MSAA면 4X범위)
  2. 4개로 쪼개진 서브 샘플에서, 픽셀의 중앙이나 특정 위치에서 계산된 단일 색상이 통째로 덮어씌워 색상을 결정함

그냥 kernel처럼 작동하는거임

그냥 이거임ㅇㅇ

그럼 gl에서는 어떻게 사용하냐?
그걸 이제부터 알아보자 ㅇㅇ

MSAA in OpenGL

위에 과정을 보면 좀 무서운데
실제로는 그냥 옵션 몇개만 추가해주면됨

먼저 sample수를 지정해주고, msaa를 활성화시켜주면 끝임
백그라운드에서 래스터라이저가 알아서 aa해줌

glfwWindowHint(GLFW_SAMPLES, 4);
glEnable(GL_MULTISAMPLE);

렌더 루프 들어가기전에 저 2개만 작성해주면 됨
숫자는 원하는 서브샘플 수를 적으면 됨

차이가 보이는가 ㅇㅇ

커스텀 프레임버퍼 AA적용

만약 우리가 후처리효과를 넣고싶은데,
만약 AA가 적용된 화면에 후처리효과를 넣고싶으면?
커스텀 프레임버퍼를 통해서 AA를 적용해야지 ㅇㅇ

위의 경우는 제공되는 프레임버퍼에서 사용했을 경우라 저렇게 설정이 많이 필요없는거임

커스텀 프레임버퍼를 사용할때는 직접 멀티샘플 버퍼를 만들어줘야함

이걸 하는 방법은 2가지임

texture attachment, render buffer object임
겁먹을 필요 없음
공부했던 내용 반복이라...

Advanced OpenGL(4) - frame buffers

이부분에서 텍스쳐/렌더 버퍼로 붙이는 쪽임

텍스쳐 부착은 쉐이더에서 값을 읽어서 사용해야할때 사용하고
렌더버퍼는 쉐이더에서 사용하지 않고, 단순히 데이터 쓰기만 해야할때 사용

multi-sample texture attachment

개쉬움

glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0);
  1. GL_TEXTURE_2D_MULTISAMPLE에 바인딩
  2. glTexImage2DMultisample에 원하는 텍스쳐, 샘플수등을 넣어줌
    • 마지막 GL_TRUE는 모든 텍셀에 대해 같은 수의 서브샘플을 가진다는걸 의미함
  3. 프레임버퍼에 texture attachment를 함

쉽지?
GL_TRUE이게 좀 중요함

그리고 이렇게 자동적으로 렌더링을 통해 들어온 데이터는
쉐이더로 넘겨서 사용가능하지~

참고로 멀티샘플 텍스쳐를 쉐이더에서 사용하려면

sampler2DMS로 데이터를 받아서 texelFetch을 이용해 샘플당 색상값을 가져올 수 있음

multi-sample render buffer object

멀티샘플 프레임버퍼는 약간의 후조정이 필요함

일단 멀티샘플 프레임버퍼를 직접 만들면, 래스터라이저가 알아서 MSAA를 해줌
다만,

여기서 MSAA가 되었다는건 해당 렌더버퍼가 더 많은 데이터를 가지고 있다는걸 말함
따라서 데이터의 크기를 줄이는 resolve라는 과정을 거쳐야함
그걸 Blitting이라고 함

4X MSAA를 예시로 들면
화면크기가 400*400일때, 하나의 프레임이 400*400*4의 데이터를 가지게 됨
따라서 400*400에 맞게 데이터 크기를 조절해줘야함

그게 Resolve임

아이디어는 간단!

  1. 멀티샘플 렌더버퍼에 MSAA적용
  2. 프레임버퍼에 멀티샘플 프레임버퍼 blitting

끝!

코드로는 아래와 같음

// ===== MSAA FBO 생성 =====
unsigned int multisampledFBO;
glGenFramebuffers(1, &multisampledFBO);
glBindFramebuffer(GL_FRAMEBUFFER, multisampledFBO);

// 멀티샘플 "컬러" 렌더버퍼 생성
unsigned int colorRBO;
glGenRenderbuffers(1, &colorRBO);
glBindRenderbuffer(GL_RENDERBUFFER, colorRBO);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGB8, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRBO);

// 멀티샘플 깊이/스텐실 렌더버퍼 생성
unsigned int rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, SCR_WIDTH, SCR_HEIGHT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    std::cout << "ERROR::FRAMEBUFFER:: Multisampled Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// ===== 일반 FBO(리졸브 대상) 생성 =====
unsigned int normalFBO;
glGenFramebuffers(1, &normalFBO);
glBindFramebuffer(GL_FRAMEBUFFER, normalFBO);

// 리졸브된 결과를 담을 일반 2D 텍스처 생성
unsigned int screenTexture;
glGenTextures(1, &screenTexture);
glBindTexture(GL_TEXTURE_2D, screenTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, SCR_WIDTH, SCR_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);

if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
    std::cout << "ERROR::FRAMEBUFFER:: Normal Framebuffer is not complete!" << std::endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);

//메인 렌더링 루프
while (!glfwWindowShouldClose(window))
{
    // --- 첫 번째 단계: MSAA FBO에 3D 장면 렌더링 ---
    glBindFramebuffer(GL_FRAMEBUFFER, multisampledFBO);
    glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glEnable(GL_DEPTH_TEST);
        
    // (이 부분에 3D 물체를 그리는 코드가 들어갑니다)
    // DrawScene(); 

    // --- 두 번째 단계: MSAA FBO의 내용을 일반 FBO로 리졸브(블리팅) ---
    glBindFramebuffer(GL_READ_FRAMEBUFFER, multisampledFBO);
    glBindFramebuffer(GL_DRAW_FRAMEBUFFER, normalFBO);
    glBlitFramebuffer(0, 0, SCR_WIDTH, SCR_HEIGHT, 0, 0, SCR_WIDTH, SCR_HEIGHT, GL_COLOR_BUFFER_BIT, GL_NEAREST);

    // --- 세 번째 단계: 일반 FBO의 텍스처를 사용하여 화면에 2D 사각형 렌더링 (후처리 적용) ---
    glBindFramebuffer(GL_FRAMEBUFFER, 0); // 화면 출력으로 변경
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f); 
    glClear(GL_COLOR_BUFFER_BIT);
    glDisable(GL_DEPTH_TEST); // 화면 전체를 덮는 사각형이므로 깊이 테스트 비활성화

    // (이 부분에 후처리 셰이더 활성화 코드가 들어갑니다)
    // screenShader.use();
        
    glBindTexture(GL_TEXTURE_2D, screenTexture);
        
    // (이 부분에 화면 크기의 사각형(Quad)을 그리는 코드가 들어갑니다)
    // DrawPostProcessingQuad();

    glfwSwapBuffers(window);
    glfwPollEvents();
}

    glfwTerminate();
    return 0;
}

천천히 코드보면 알수이쓸거임ㅇㅇ

profile
그래픽스 하는 퍼그

0개의 댓글