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로 전송하지 않고도 색상 데이터를 업데이트할 수 있습니다.