재사용 가능한 WebGPU & Rust 엔진: 파트 3 — 3D 카메라 시스템 및 핫 리로딩 구현
요약
본 글은 WebGPU와 Rust를 사용하여 3D 카메라 시스템을 구축하는 방법을 다룹니다. 특히, 평면 렌더링에서 완전한 3D 뷰포트로 전환하기 위해 Pinhole 카메라 모델 구현에 초점을 맞춥니다. Projection Matrix와 View Matrix 두 가지 핵심 행렬 변환을 사용하며, wgpu 바인드 그룹 전략을 통해 엔진의 확장성을 확보하는 방법을 제시합니다.
핵심 포인트
- WebGPU에서 3D 깊이를 얻으려면 Perspective Matrix가 필수적입니다.
- Pinhole 카메라 구현은 Projection Matrix와 View Matrix 두 가지로 이루어집니다.
- wgpu 바인드 그룹(bind groups)을 사용하여 카메라 데이터를 구조화하고 확장성을 높일 수 있습니다.
마지막 업데이트 이후 시간이 좀 지났습니다! 저는 자산 로딩과 파이프라인 최적화와 관련하여 특히 좋은 피드백과 아키텍처 제안을 받았습니다. 이 아이디어들을 단계적으로 통합할 계획입니다.
셰이더 핫 리로딩이나 멀티스레드 자산 스트리밍 같은 고급 기능으로 뛰어들기 전에, 근본적인 문제를 해결해야 합니다: 평면 2D 렌더링에서 완전한 3D 뷰포트로 이동하는 것입니다. 오늘 우리는 Pinhole 3D 카메라 시스템을 설정하는 데 중점을 둘 것입니다.
1. 왜 카메라가 필요한가요? (정규화 장치 좌표계)
만약 wgpu에서 Camera View나 Projection matrix uniform 없이 메쉬를 렌더링한다면, 여러분의 정점 위치는 WebGPU의 Normalized Device Coordinates (NDC) 클립 공간으로 직접 전달됩니다.
wgpu에서 NDC는 다음 범위의 경계가 있는 클립 공간입니다:
X와 Y는 [-1.0, 1.0] 범위이고 Z는 [0.0, 1.0] 범위이다.
원근 분할(perspective division)을 수행하는 투영 행렬(perspective matrix)이 없으면 (Z축을 따라 거리에 기반하여 기하학적 구조를 조정), 여러분의 장면은 평평하고 직교적인 상자처럼 렌더링됩니다. 이 [-1.0, 1.0] 범위를 벗어난 정점들은 즉시 클립되고, 물체들이 멀리 떨어질수록 작아지지 않습니다.
2. 표준 Pinhole 카메라 구축하기
현실적인 3D 깊이를 달성하기 위해, 우리는 두 가지 핵심 행렬 변환을 사용하여 고전적인 pinhole camera 모델을 구현합니다:
-
Projection Matrix (cgmath::perspective): 거리에 기반하여 좌표를 조정하고 창 크기를 조정할 때 물체가 늘어나지 않도록 종횡비 보정을 적용하는 Field-of-View (FOV) 프러스텀을 구성합니다.
-
View Matrix (cgmath::Matrix4::look_at_rh): 우측 손 좌표계(_rh)를 사용하여 카메라의 눈 위치, 목표 방향 및
카메라 행렬을 셰이더로 전달하기 위해 wgpu 바인드 그룹(bind groups)을 사용하여 명시적인 레이아웃 계약(layout contract)을 설정합니다:
-
camera_controller.bind_group_layout: WebGPU에게 유니폼 데이터가 셰이더에 진입하기 전의 정확한 형태와 메모리 가시성(memory visibility)을 알려줍니다 (@group(0) @binding(0) var<uniform> camera: CameraUniform;). -
render_pipeline_layout: 카메라 레이아웃을 슬롯 0에 등록하여 파이프라인이 드로우 콜(draw calls) 중에 **@group(0)**에서 카메라 데이터를 기대하도록 합니다.
모듈식 바인딩 전략 (Modular Binding Strategy)
바인드 그룹을 명시적으로 구조화하면 엔진의 확장성을 유지할 수 있습니다. 나중에 재질(materials), 텍스처, 또는 라이트를 추가하더라도, 카메라 설정이 깨지지 않도록 단순히 더 높은 그룹 슬롯에 바인딩하면 됩니다:
// Group 0: Camera Uniforms (프레임당 또는 카메라 이동 시 한 번 업데이트)
@group(0) @binding(0) var<uniform> camera: CameraUniform;
...
다음 단계는? (What’s Next?)
카메라 행렬이 매 프레임 유니폼 버퍼를 업데이트함에 따라, 이제 로드된 모든 3D 메시 주위를 자유롭게 궤도 이동(orbit), 패닝(pan), 및 확대/축소(zoom)할 수 있습니다. 이 기능들의 세부 사항은 나중에 다루도록 하겠습니다.

여기서는 원근감 변화는 있지만 깊이(depth)가 없는, 약간 흥미로운 이미지를 얻습니다.
보너스: 셰이더 핫 리로딩 (Shader Hot-Reloading)
WGSL에서 색상이나 조명 방정식을 수정할 때마다 애플리케이션을 재시작하는 것은 반복 속도를 빠르게 저하시킵니다.
셰이더를 미리 컴파일하는 대신, notify를 사용하여 -/any_name/ 폴더를 감시하고 .wgsl 파일이 저장될 때마다 런타임에 RenderPipeline을 재구축할 수 있습니다.
작동 원리:
-
OS 레벨 비동기 추적:
notify::RecommendedWatcher를 사용하여 운영체제의 네이티브 파일 알림 API에 연결합니다. 이를 통해 렌더링 스레드를 차단하지 않고 백그라운드에서 파일 변경 사항을 모니터링할 수 있습니다. -
메인 스레드 채널 리시버 (
Receiver<Result<Event>>): 백그라운드 워처가 파일 시스템 업데이트를 스레드 안전한 mpsc 채널을 통해 전송합니다. 리시버 측은 메인 스레드에서 사용할 수 있도록 유지됩니다. -
리드로우 시 재로드 트리거: 메인 이벤트 루프의
RedrawRequested단계 동안, 우리는watcher.rx.try_recv()를 사용하여 채널을 비차단 방식으로 확인합니다. 파일 수정 이벤트가 감지되면,state.reload_shaders()는 디스크에서 업데이트된.wgsl파일을 읽어 활성RenderPipeline을 실시간으로 교체합니다.

애플리케이션을 닫지 않고도 셰이더를 변경할 수 있습니다!
읽어주셔서 감사합니다! 유사한 시스템을 구축하고 있다면, 제 개인 프로젝트들을 확인해 보세요:
산업용 3D 비전 또는 AI 파이프라인을 구축하십니까? |
확인하기
— 까다로운 광학 기하학에 대해 감지 네트워크를 벤치마크, 테스트 및 검증하도록 설계되었습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기