개발 / / 2024. 5. 2. 07:13

WebGPU depth texture 깊이 뎁스 텍스처

반응형

 

 

 

 

depth texture 뎁스 텍스처 오류

depth texture 오류가 발생한 모습

왼쪽과 오른쪽은 각각 토끼와 큐브를 렌더링한 것이라고 한다.

큐브는 말할 것도 없고, 토끼는 귀쪽이 뭔가 띄엄띄엄 구멍난 것처럼 되어있는데. 이 문제는 depth texture와 관련된 문제라고 한다.

 

어떤 삼각형이 다른 삼각형보다 앞에 있는 걸로 보여야되는데, 그렇지 않고 순서가 뒤바뀌는 경우를 그래픽 파이프라인에서는 depth buffer에서 처리한다. 

혹여나 삼각형이 보여지는 순서가 뒤바뀌어서 위 그림과 같은 문제가 생기면, sort로 triangle들이 depth를 앞에서부터 정렬하거나, depth buffer방법이 있다.

 

depth testing 뎁스 테스팅이란?

depth buffer는 이미지의  z 좌표로써 저장된다. 이미지에는 z좌표가 없는데? 그래서 depth buffer인 것이다. 

 

이렇게 여러 오브젝트들이 결국 하나의 이미지로 나오게 되는데, 뭐가 앞에 있고 뭐가 뒤에있는지를 픽셀에 depth 값을 줌으로써 구분한다. 

그림에서 위의 컬러 사진은 color buffer를 보여주고, 밑에는 depth buffer를 보여준다. depth값이 작을 수록 카메라와 가까이 있기 때문에 먼저 렌더링 해야한다.

 

depth test 뎁스 테스트는 모든 primitive 정보들을 가지고 마지막 단계에서 이미지를 생성할 때 진행한다. primitive가 어떤 것은 다른 프리미티브 뒤에 숨어있어서 당장 시야에 보이면 안되는 경우도 있기 때문에 그렇다.

그렇게 각각의 프리미티브 끼리 어떤 픽셀은 앞에 있어야하고 어떤 픽셀은 그 뒤에있고 하는 것을 depth value (z-value)로써 관리한다.

 

 

사진처럼 삼각형이 꽂혀있는 상황에서는 특정 부분은 앞에있고, 뒤에있고 다르다. 

이를 보이는 영역으로 clipping하고 나면, 그 안에 보이는 픽셀들의 depth value(z-value)를 구하고 서로 비교하여 더 작은 값을 가진 픽셀로 덮어씌운다.

 

그림의 경우 회색 삼각형은 화면이랑 평행해서 깊이 값이 5로 똑같다.

노란 삼각형은 비스듬히 있는데, 여기에 꽂힌 회색 삼각형보다 시야에서 먼 노란 삼각형의 부분들은 각각의 픽셀들이 회색 삼각형의 픽셀들로 덮어 씌워진다.

 

이렇게 실제로 렌더링 되기 전에 depth value들을 가지고 buffer에 넣을 때 depth아 작은 가까운 쪽을 선택하는 과정을 depth testing이라고 한다.

 

WebGPU에서 depth buffer 적용하기

webgpu에서 depth buffer 적용을 하려면 renderDescriptor의 depthStencilAttachment라는 field를 수정해야한다.

const renderPassDescriptor: GPURenderPassDescriptor = {
    colorAttachments: [
      ... 
    ],
    depthStencilAttachment: {
        view: depthTexture.createView(),
        depthClearValue: 1.0,
        depthLoadOp: "clear",
        depthStoreOp: "store",
    },
};

여기서 depthTexture 변수는 다음과 같이 정의 된다.

 

const depthTexture = this.device.createTexture({
  size: this.presentationSize,
  format: "depth24plus",
  usage: GPUTextureUsage.RENDER_ATTACHMENT,
});

각각의 값들은 무슨 의미일까

 

depth 값들 렌더링한 그림을 보면 가까이 있는 오브젝트 일수록 검은색이다.

depthStencilAttachment의 프로퍼티 중 depthClearValu을 1.0으로 설정하면, 가장 멀리이 있는 값이 1.0 흰색으로 설정된다. 가장 가까이 있는 값은 0.0 검은색이 된다.

 

depthTexture 변수의 프로퍼티들을 보면

  • size는 이미지의 크기이므로 color buffer와 같아야한다.
  • format의 depth24plus는 정밀도가 24bit의 크기임을 말한다.

 

this.device.createRenderPipeline({
    primitive: { ...  },
    depthStencil: {
        depthWriteEnabled: true,
        depthCompare: "less",
        format: "depth24plus",
    },
    ...shader,
});

webgpu에서 렌더링 파이프라인의 마지막 단계로 GPUDevice.createRenderPipeline함수로 RenderPipeline을 생성할 때도, depthStencil로 depth에 대한 정의를 해준다.

 

코드에서 depthCompare 값을 less로 정의한 것은 더 작은 값을 채택하여 픽셀에 반영한다는 의미이다.

 

depthBuffer에 대해서 올바르게 정의하면 이렇게 토끼의 귀 부분이 정상적으로 렌더링된다.

 

tip. texture 생성으로 메모리 누수 

그래픽 엔진 코드를 작성하다보면 메모리 누수가 생기는 일이 허다하다. 

많은 상황에서 리소스를 frame마다 생성하는 것이 원인이 되는 것을 종종봤다.

draw(drawCb: (drawHelper: GPURenderPassEncoder) => void) {
// ~~ Define render loop ~~
const depthTexture = this.device.createTexture({
  size: this.presentationSize,
  format: "depth24plus",
  usage: GPUTextureUsage.RENDER_ATTACHMENT,
});
​
const frame = () => {
    // WRONG
    // const depthTexture = this.device.createTexture({
    //   size: this.presentationSize,
    //   format: "depth24plus",
    //   usage: GPUTextureUsage.RENDER_ATTACHMENT,
    // });
    const commandEncoder = this.device.createCommandEncoder();

위 코드처럼 만약 frame 함수에 create texture가 매 프레임마다 실행되었다면, 당연히 메모리가 순식간에 부족해지고 결국 프로그램이 터지는 상황이 발생할 것이다.

 

이런 상황이 생기지 않도록 어디서 텍스터를 생성하고 삭제하고 하는지 잘 검토해볼 필요가 있겠다.

 

참고한 사이트

 

 

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유