1.5 단일 버퍼로 색상 삼각형 그리기

이전 예제에서는 정점의 위치와 색상을 제공하기 위해 두 개의 별도 버퍼와 두 개의 위치를 사용했습니다. 하지만 여러 버퍼를 사용하는 것이 항상 필요하거나 최적의 방법은 아닙니다. 이 수정된 예제에서는 모든 정점 속성을 제공하기 위해 단일 버퍼를 사용하는 방법을 시연할 것입니다.

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

이 예제와 이전 예제 간의 주요 차이점에 초점을 맞춰봅시다. 셰이더 코드는 변경되지 않습니다.

const positionAttribDesc = {
    shaderLocation: 0, // @location(0)
    offset: 0,
    format: 'float32x3'
};

const colorAttribDesc = {
    shaderLocation: 1, // @location(1)
    offset: 4 * 3,
    format: 'float32x3'
};

먼저 색상 속성 디스크립터를 수정합니다. 위치와 색상 데이터를 교차시키기 때문에 오프셋을 도입합니다. 이 오프셋은 버퍼 내에서 첫 번째 색상 데이터의 시작을 나타냅니다. 정점 위치 뒤에 색상을 배치했으므로 오프셋은 12바이트(4바이트 * 3)로 설정되며, 이는 정점 위치 벡터의 크기입니다.

const positionColorBufferLayoutDesc = {
    attributes: [positionAttribDesc, colorAttribDesc],
    arrayStride: 4 * 6, // sizeof(float) * 3
    stepMode: 'vertex'
};

둘째, 위치와 색상에 대해 별도의 버퍼를 생성하는 대신, 이제 positionColorBuffer라는 단일 버퍼를 사용합니다. 이 버퍼에 대한 디스크립터를 생성할 때, 속성 목록에 두 가지 속성을 모두 포함합니다. arrayStride는 12바이트 대신 24바이트(4 * 6)로 설정되는데, 이는 각 정점에 이제 6개의 float 숫자(위치에 3개, 색상에 3개)가 연결되기 때문입니다.

const positionColors = new Float32Array([
    1.0, -1.0, 0.0, // position
    1.0, 0.0, 0.0, // 🔴
    -1.0, -1.0, 0.0, 
    0.0, 1.0, 0.0, // 🟢
    0.0, 1.0, 0.0,
    0.0, 0.0, 1.0 // 🔵
]);

let positionColorBuffer = createGPUBuffer(device, positionColors, GPUBufferUsage.VERTEX);

이 버퍼의 데이터를 생성할 때, 위치와 색상을 교차하여 18개의 부동 소수점 숫자(정점 3개 * 정점당 6개의 float)를 제공합니다:

const pipelineDesc = {
    layout,
    vertex: {
        module: shaderModule,
        entryPoint: 'vs_main',
        buffers: [positionColorBufferLayoutDesc]
    },
    fragment: {
        module: shaderModule,
        entryPoint: 'fs_main',
        targets: [colorState]
    },
    primitive: {
        topology: 'triangle-list',
        frontFace: 'cw',
        cullMode: 'back'
    }
};
• • •
passEncoder = commandEncoder.beginRenderPass(renderPassDesc);
passEncoder.setViewport(0, 0, canvas.width, canvas.height, 0, 1);
passEncoder.setPipeline(pipeline);
passEncoder.setVertexBuffer(0, positionColorBuffer);
passEncoder.draw(3, 1);
passEncoder.end();

파이프라인 디스크립터에서 이제 buffers 필드에 하나의 버퍼 디스크립터만 있습니다. 렌더링 명령을 인코딩할 때, 슬롯 0에 하나의 버퍼만 설정합니다:

이 프로그램은 기능적으로 이전 프로그램과 동일하지만, 두 개의 버퍼 대신 단일 버퍼를 사용합니다. 이 경우 단일 버퍼를 사용하는 것이 추가 리소스 생성을 피하고 CPU에서 GPU로 데이터를 두 번 복사할 필요가 없으므로 더 효율적입니다. CPU에서 GPU로 데이터를 전송하는 것은 지연 시간을 발생시키므로, 이러한 전송을 최소화하는 것이 성능에 유리합니다.

여러 버퍼를 사용할지 단일 버퍼를 사용할지 궁금할 수 있습니다. 이는 속성이 얼마나 자주 변경되는지에 따라 달라집니다. 이 예제에서는 위치와 색상 모두 실행 내내 일정하게 유지되므로 단일 버퍼가 적합합니다. 하지만 애니메이션에서와 같이 정점 색상을 자주 업데이트해야 하는 경우, 속성을 다른 버퍼로 분리하는 것이 더 나을 수 있습니다. 이렇게 하면 변경되지 않은 위치 데이터를 GPU로 전송하지 않고도 색상 데이터를 업데이트할 수 있습니다.

GitHub에 의견 남기기