픽셀 처리 단계는 최종적으로 화면에 출력될 이미지를 생성하는 과정이다. 이 과정에서는 각 픽셀의 색상과 그 외의 부가적인 정보가 결정된다. 픽셀 처리에는 다음과 같은 주요 단계들이 포함된다:

픽셀 셰이더

픽셀 셰이더(또는 프래그먼트 셰이더)는 각 픽셀의 색상을 결정하는 프로그램이다. 이 셰이더는 다양한 입력값을 바탕으로 복잡한 계산을 수행하여 최종 색상 값을 생성한다. 입력값으로는 보통 텍스처 좌표, 노멀, 라이팅 정보, 보간된 값을 포함한다.

픽셀 셰이더의 일반적인 구조는 다음과 같다:

#version 330 core

in vec2 TexCoords;
in vec3 Normal;
out vec4 FragColor;

uniform sampler2D texture1;
uniform vec3 lightDir;

void main()
{
    vec4 texColor = texture(texture1, TexCoords);
    float lighting = max(dot(normalize(Normal), lightDir), 0.0);
    FragColor = vec4(texColor.rgb * lighting, texColor.a);
}

텍스처 매핑

텍스처 매핑은 2D 이미지를 3D 모델의 표면에 매핑하는 과정이다. 이는 각 픽셀의 텍스처 좌표를 사용하여 텍스처 이미지를 샘플링하여 해당 좌표의 색상을 가져오는 방식으로 이루어진다.

텍스처 좌표는 보통 정점 셰이더에서 계산되어 픽셀 셰이더로 전달된다. 셰이더 코드 내에서는 일반적으로 texture 함수를 통해 텍스처 샘플링을 수행한다.

블렌딩

블렌딩은 여러 소스를 결합하여 최종 픽셀 색상을 결정하는 과정이다. 블렌딩은 주로 투명 효과를 구현할 때 사용된다. 이는 기존 프레임 버퍼의 색상 값과 새로 계산된 색상 값을 결합하여 최종 색상을 계산한다.

일반적인 블렌딩 방정식은 다음과 같다:

C_{\text{final}} = \alpha \cdot C_{\text{new}} + (1 - \alpha) \cdot C_{\text{old}}

여기서 \alpha는 새로 계산된 색상의 알파 값이고, C_{\text{new}}C_{\text{old}}는 각각 새로 계산된 색상과 기존 색상이다.

딥스 테스트

딥스 테스트는 각 픽셀이 화면의 어느 위치에 배치될지를 결정하는 과정이다. 깊이 버퍼(Z-buffer)를 사용하여 각 픽셀의 깊이를 비교한다. 깊이 비교가 통과된 픽셀만이 화면에 그려지게 된다.

딥스 테스트 공식은 다음과 같다:

\text{if } z_{\text{new}} < z_{\text{old}} \text{ then update}

여기서 z_{\text{new}}는 새로 계산된 픽셀의 깊이 값이고, z_{\text{old}}는 기존 깊이 버퍼에 저장된 값이다.

안티 앨리어싱

안티 앨리어싱은 계단 현상(aliasing)을 줄이기 위한 기술이다. 이는 픽셀 간의 경계를 부드럽게 하여 더 자연스러운 이미지를 생성한다. 현재 많이 사용하는 방법 중 하나는 멀티 샘플링(Multi-Sample Anti-Aliasing, MSAA)이다.

감마 보정

화면에 출력되는 색상을 정확하게 표현하기 위해 감마 보정을 수행한다. 이는 모니터의 특성에 맞춰 색상 값을 조정하는 과정이다. 감마 보정은 다음과 같은 수식을 따른다:

C_{\text{corrected}} = C_{\text{original}}^{(1/\gamma)}

여기서 \gamma는 감마 값이며, 일반적으로 2.2로 설정된다.

그림자 매핑

그림자 매핑은 3D 환경에서 그림자를 생성하는 주요 기술 중 하나이다. 이 기법은 먼저 그림자를 만들기 위한 뷰포인트로부터 장면을 렌더링하여 깊이 맵(그림자 맵)을 생성한 후, 이 맵을 이용해 실제 렌더링 시 그림자의 존재 여부를 결정한다.

기본적인 그림자 매핑 과정은 다음과 같다:

  1. 그림자 맵 생성:
  2. 장면을 빛의 시점에서 렌더링하여 각 픽셀의 깊이 정보를 저장한다.
  3. 이것이 그림자 맵이다.

  4. 그림자 테스트:

  5. 실제 장면을 렌더링할 때, 각 픽셀이 그림자 맵에 저장된 깊이 값과 비교한다.
  6. 비교 결과에 따라 픽셀이 그림자 안에 있는지 판단한다.

셰이더 코드 예제:

// 그림자 맵 샘플링
float shadowCalculation(vec4 fragPosLightSpace)
{
    // 원근 분할 변환
    vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
    projCoords = projCoords * 0.5 + 0.5;

    // 그림자 맵 비교
    float closestDepth = texture(shadowMap, projCoords.xy).r;
    float currentDepth = projCoords.z;

    // 그림자 여부 판단
    float shadow = currentDepth > closestDepth ? 1.0 : 0.0;

    return shadow;
}

포스트 프로세싱

포스트 프로세싱은 최종 렌더링된 이미지를 추가로 처리하여 다양한 시각적 효과를 적용하는 과정이다. 대표적인 포스트 프로세싱 효과에는 블룸, 모션 블러, 컬러 코렉션, 피사체 포커스(DoF, Depth of Field)가 있다.

포스트 프로세싱을 위해 보통 프레임 버퍼 객체(FBO)를 사용하여 중간 결과를 저장하고, 이 결과를 다시 렌더링 하여 최종 이미지를 생성한다.

멀티샘플 안티앨리어싱 (MSAA)

MSAA는 이미지를 다중 샘플링하여 앨리어싱을 줄이는 기법이다. 각 픽셀당 여러 샘플을 생성하고 이를 평균내어 최종 픽셀 값을 계산한다. 이렇게 하면 경계가 부드럽게 나타난다.

고정 컴퓨팅

고정 컴퓨팅은 CPU가 아닌 GPU를 이용해 일반적인 계산을 수행하는 기법이다. 이는 비디오 게임에서 물리 시뮬레이션, AI 계산 등 다양한 용도로 활용될 수 있다. 고정 셰이더는 일반적인 그래픽 파이프라인이 아닌 커스텀 컴퓨팅에 특화되어 있으며, GPU의 병렬 처리 능력을 최대한 활용한다.

최적화

최적화는 렌더링 성능을 향상시키기 위한 다양한 기법을 적용하는 것이다. 루프 언롤링, 텍스처 압축, 프러닝(prunning), 중복된 렌더링 제어 등이 이에 포함된다. 효율적인 메모리 관리와 GPU 연산 병렬화도 중요한 역할을 한다.

이와 같은 픽셀 처리 단계를 통해 복잡하고 현실감 있는 디지털 이미지를 생성할 수 있다.