1.12 깊이 테스트

이번 섹션에서는 뒤집힌 내부 표면과 같은 렌더링 문제를 해결하는 데 중요한 기법인 깊이 테스트를 살펴보겠습니다. 깊이 테스트를 통해 프래그먼트의 시각적 순서를 앞에서 뒤로 결정하여 카메라에 가장 가까운 프래그먼트만 그려지고 그 뒤의 프래그먼트는 폐기되도록 합니다.

플레이그라운드 실행 - 1_12_depth

뷰포트 좌표에서 z축은 프래그먼트 깊이를 나타냅니다. 정확한 렌더링을 위해서는 z-값에 따라 프래그먼트를 정렬하는 것이 필수적입니다.

이를 위해 깊이 버퍼 또는 z-버퍼라고 알려진 추가 버퍼를 도입합니다. 프래그먼트 셰이더에서 값을 출력할 때, 프래그먼트에 색상을 할당할 뿐만 아니라 현재 깊이 값을 깊이 버퍼에 기록합니다. 깊이 버퍼는 각 프래그먼트 위치에 대해 가장 작은 z-값(가장 앞쪽)만 저장합니다.

예를 들어, 현재 프레임 버퍼의 위치 (300, 400)에 깊이가 0.5인 프래그먼트가 있다고 가정해 봅시다. 동일한 위치에 깊이가 0.6인 새로운 프래그먼트가 있다면, 더 멀리 있기 때문에 폐기됩니다. 반대로, 깊이가 0.4인 새로운 프래그먼트는 더 가깝다고 간주되어 이전 프래그먼트를 대체합니다. 이는 가장 앞쪽에 있는 프래그먼트만 표시되도록 합니다.

깊이 테스트 동작은 구성 가능하며, 더 큰 깊이 값을 가진 프래그먼트를 유지하고 더 작은 값을 폐기하도록 선택할 수 있습니다. 그러나 일반적으로 가장 앞쪽의 프래그먼트를 보존하는 것이 선호되는 동작입니다.

깊이 테스트를 활성화하기 위해 코드를 업데이트해 보겠습니다. 다행히 파이프라인이 올바르게 구성되면 깊이 버퍼에 대한 쓰기는 자동으로 처리되므로 셰이더 코드는 변경되지 않습니다.

const pipelineDesc = {
    layout,
    vertex: {
        module: shaderModule,
        entryPoint: 'vs_main',
        buffers: [positionBufferLayoutDesc]
    },
    fragment: {
        module: shaderModule,
        entryPoint: 'fs_main',
        targets: [colorState]
    },
    primitive: {
        topology: 'triangle-strip',
        frontFace: 'ccw',
        cullMode: 'none'
    },
    depthStencil: {
        depthWriteEnabled: true,
        depthCompare: 'less',
        format: 'depth24plus-stencil8'
    }
};

파이프라인 디스크립터에서 새로운 depthStencil 섹션을 도입합니다. 깊이 값을 깊이 버퍼에 기록하도록 depthWriteEnabledtrue로 설정합니다. depthCompareless로 설정하여 더 작은 깊이 값을 가진 프래그먼트를 유지하고 더 큰 깊이 값을 가진 프래그먼트를 폐기합니다. 더 큰 깊이를 가진 프래그먼트를 보존하는 것과 같이 다른 동작을 실험해 볼 수도 있습니다.

형식은 depth24plus-stencil8로 지정하여 깊이 값에 24비트를 할당하고 스텐실 값에 8비트를 할당합니다. 스텐실에 대해서는 향후 챕터에서 논의할 예정이며, 지금은 깊이에 초점을 맞춥니다.

설정을 완료하려면 깊이 버퍼를 생성해야 합니다:

const depthTextureDesc = {
    size: [canvas.width, canvas.height, 1],
    dimension: '2d',
    format: 'depth24plus-stencil8',
    usage: GPUTextureUsage.RENDER_ATTACHMENT 
};

let depthTexture = device.createTexture(depthTextureDesc);
let depthTextureView = depthTexture.createView();

깊이 버퍼를 생성하는 것은 텍스처 맵을 생성하는 것과 유사합니다. 깊이 버퍼의 크기를 캔버스와 일치시키고, 차원을 2d로 설정하며, 동일한 depth24plus-stencil8 형식을 사용합니다. 깊이 버퍼는 일반적으로 보이지 않지만 렌더링 대상이므로 GPUTextureUsage.RENDER_ATTACHMENT를 사용으로 지정합니다.

const depthAttachment = {
    view: depthTextureView,
    depthClearValue: 1,
    depthLoadOp: 'clear',
    depthStoreOp: 'store',
    stencilClearValue: 0,
    stencilLoadOp: 'clear',
    stencilStoreOp: 'store'
};

const renderPassDesc = {
    colorAttachments: [colorAttachment],
    depthStencilAttachment: depthAttachment
};

마지막으로, depthAttachment를 정의하고 이를 렌더 패스에 추가합니다. colorAttachment와 마찬가지로, depthAttachment는 깊이 버퍼 값의 로드 및 저장 동작을 지정합니다. 로드 시 깊이 버퍼를 1로 초기화합니다(depthClearValue). 이는 뷰포트 좌표에서 가능한 최대 깊이 값인 1로 배경 깊이를 효과적으로 설정합니다. 저장 시(depthStoreOp), 'less' 동작에 따라 들어오는 값을 단순히 저장합니다. 스텐실 설정은 지금은 무시할 수 있습니다.

렌더 패스에는 이제 depthStencilAttachment가 포함됩니다. 이러한 업데이트를 통해 코드는 올바르게 가려짐을 처리하여 더 정확한 렌더링을 위해 전면 표면이 후면 표면을 가리도록 합니다.

깊이 테스트가 이전 예제의 아티팩트를 수정합니다
깊이 테스트가 이전 예제의 아티팩트를 수정합니다

GitHub에 의견 남기기