Lobsters

Teaching GPU programming in p5.js: now with compute shaders

p5.js로 GPU 프로그래밍 가르치기 — 컴퓨트 셰이더까지

p5.js의 셰이더 시스템 p5.strands가 컴퓨트 셰이더까지 확장됩니다. 익숙한 JavaScript 문법으로 픽셀 필터와 정점 변형부터 GPU 병렬 연산까지 단계적으로 익히며, 글에서는 WebGPU로 보이드(boids) 1만 개를 실행한 사례도 소개합니다.

AI 요약

GPU 프로그래밍은 그래픽 API와 셰이더 파이프라인, CPU와 GPU 사이의 데이터 전달을 한꺼번에 익혀야 해 진입 장벽이 높습니다. 글쓴이는 처음부터 모든 배경지식을 요구하기보다, 학습자가 당장 작업을 시작할 수 있는 발판(scaffolding)을 제공하고 필요할 때 세부 개념을 드러내는 방식을 제안합니다. p5.js의 p5.strands는 이 발판을 JavaScript와 익숙한 p5.js 문법으로 마련합니다.

익숙한 코드에서 시작하는 셰이더

p5.strands를 시작할 때 필요한 지식은 반복문과 조건문, 변수, 함수, 기본적인 JavaScript 수학입니다. 처음부터 셰이더 파이프라인이나 uniform, varying을 알 필요는 없습니다. 기존 p5.js 사용자는 buildFilterShader()로 필터를 만들고, filterColor.set()에 색을 지정하는 식으로 픽셀 셰이더를 작성합니다. filterColor.texCoord는 현재 픽셀의 텍스처 좌표를 0부터 1 사이 값으로 제공합니다. 이를 색상으로 출력하면 좌표에 따른 그라데이션을 볼 수 있고, noise()를 적용하면 생성형 텍스처를 만들 수 있습니다.

millis()를 사용해 색상을 시간에 따라 바꾸거나, 슬라이더 값을 uniformFloat()로 셰이더에 전달할 수도 있습니다. 글쓴이는 이 단계에서 학습자가 집중할 개념을 코드가 픽셀마다 실행된다는 점으로 좁힙니다. 셰이더에서 uniform이 쓰이더라도 처음에는 그 내부 구현을 알 필요가 없고, 슬라이더 값을 연결하는 과정에서 uniform을 배울 수 있습니다.

픽셀에서 도형과 인스턴스로

셰이더는 캔버스 전체에 적용하는 필터에 그치지 않습니다. buildMaterialShader()로 만든 셰이더에서는 worldInputs 블록 안에서 정점 위치를 바꿉니다. 예를 들어 millis()와 정점의 x 좌표를 이용해 구체의 각 정점을 서로 다르게 움직일 수 있습니다. buildGeometry()로 도형을 재사용 가능한 모델로 만들고 model(geometry, count)를 호출하면 여러 인스턴스를 그립니다. 셰이더 안에서는 instanceID()로 각 인스턴스를 구분해 위치나 색을 조정합니다.

이 방식으로 수십 개의 복사본을 그리거나, 여러 선을 서로 다른 위치와 색으로 배치해 빛이 분산되는 듯한 효과를 만들 수 있습니다. 글에서는 선 400개로 구성한 도형을 50개 인스턴스로 그리는 예도 보여줍니다. 다만 정해진 수식만으로 움직임을 표현하기 어려운 입자나 자율 에이전트에는 별도의 상태 데이터가 필요합니다.

WebGPU 저장 버퍼와 컴퓨트 셰이더

WebGPU에서는 createStorage()로 저장 버퍼를 만들고, 셰이더 안에서 uniformStorage()로 읽습니다. 글의 예제는 SVG 경로에서 점 100개의 좌표를 추출해 저장한 뒤, instanceID()를 이용해 각 점 위치에 구체를 배치합니다. 이 데이터를 매 프레임 갱신하면 입자 움직임을 표현할 수 있습니다.

일반 JavaScript에서는 반복문이 입자마다 위치와 속도를 갱신합니다. 컴퓨트 셰이더에서는 buildComputeShader()로 갱신 함수를 만들고, index.x를 현재 작업의 인덱스로 사용합니다. compute(updateShader, count)를 호출하면 각 작업을 GPU에서 병렬 처리합니다. 글은 공을 벽에 튕기게 하는 예제로 일반 반복문과 컴퓨트 셰이더 구현을 비교합니다. 셰이더 버전에서는 입자 상태를 저장 버퍼에 두고, 그리기 역시 인스턴싱과 셰이더로 처리합니다.

컴퓨트 셰이더를 활용한 다음 예제는 서로 밀어내고 정렬하며 무리 짓는 보이드(boids) 알고리즘입니다. 글쓴이는 100개 에이전트 예제를 제시하고, M1 MacBook Pro에서 전체 화면으로 1만 개를 실행하는 스케치도 원활하게 동작했다고 설명합니다. 이 사례에서는 공간 분할 알고리즘을 별도로 고려하지 않았다고 덧붙입니다. 다만 WebGPU 예제를 실행하려면 WebGPU를 지원하는 브라우저가 필요하며, 글에서는 최신 Chrome과 Firefox의 Windows·Mac 버전을 안내합니다.

더 낮은 수준으로 확장하기

p5.strands는 필요에 따라 vertex shader, fragment shader, compute shader, uniform, varying, texture를 차례로 접하게 설계됐습니다. 각 블록은 JavaScript 대신 GLSL이나 WGSL로 작성할 수도 있습니다. .inspectHooks()를 호출하면 JavaScript 셰이더가 생성한 네이티브 구현을 확인할 수 있고, p5의 기본 셰이더 시스템을 유지하면서 일부를 네이티브 코드로 바꾸는 방법도 있습니다. 완전히 처음부터 시작하려면 loadShader()와 createShader()를 사용할 수 있습니다.

p5.strands의 WebGL 기능은 이미 제공되며, 컴퓨트 셰이더는 p5.js 2.3에 들어갈 예정입니다. 그 전에도 컴퓨트 셰이더 브랜치 빌드에서 시험할 수 있다고 안내합니다.

원문: Dave Pagurek / 번역·요약: Trawling