SOTAESAN ATLAS

Markdown 원본 다운로드

보행 물리 처리 비용 비교

현재 결론

2026-10-02 사용자 확인에서는 이동감의 큰 차이가 없었다. 이제 보행 계산 시간, 초기화, 다운로드, 메모리와 유지보수 부담을 중심으로 비교한다.

Playwright MCP의 별도 headed 브라우저에서 Three.js BVH·Rapier와 PlayCanvas Ammo를 직접 측정했다. 시작점 정지와 외벽 밀기를 각각 30초씩 3회, 총 18회 완료했다. 각 조건의 반복 중앙값 기준으로 기록된 물리 계산 p95는 0.1–0.6ms였다. 이는 모든 호출이 1ms 미만이라는 뜻은 아니며, 최대값은 원본에 별도로 남겼다.

현재 데스크톱·장면에서 평균 보행 계산 비용은 작다. 다만 PlayCanvas와 Three.js의 통계 범위와 실제 벽 접촉 위치가 달라 물리 엔진 자체의 성능 순위는 확정하지 않는다. 전체 프레임 성능에는 차이가 있었다. 이후 초기 로딩 18회·시대 전환 120회와 메모리 관찰을 완료했으며 로딩·메모리 비교에 기록했다. iPhone 비교에서는 정지 상태를 측정했지만 이동 경로가 충돌 바닥 밖으로 나가 정상 보행 순위를 정하지 않았다. 모바일의 남은 검증과 보행 정책의 유지보수 부담까지 확인한 뒤 최종 엔진을 선택한다.

Node 자동 측정 조건

원본에는 실행 환경, 설정, 구현 파일·월드·충돌 메시의 SHA-256, 실행 전후 좌표와 전체 80회 결과를 보관했다: physics-node.json.

Node 보행 계산 시간

각 행은 5회 실행의 평균값 중앙값 / p95 중앙값, 단위는 호출당 ms이다. p95는 해당 실행에서 호출의 95%가 이 시간 이내였다는 뜻이다.

60Hz 조건 기존 BVH 평균 / p95 Rapier 평균 / p95
시작점 정지 0.0068 / 0.0072 0.0328 / 0.0393
벤치 부근 정지 0.0122 / 0.0125 0.0332 / 0.0357
외벽 밀기 0.1687 / 0.2963 0.1125 / 0.1876
계단 앞뒤 이동 0.0785 / 0.3140 0.0529 / 0.1121

60Hz 외벽 조건에서 초당 누적 처리 시간은 BVH 약 10.12ms, Rapier 약 6.75ms였다. 이는 측정 구간의 경과 시간 합계이며 프로세스 전체 CPU 사용률이 아니다. 이 데스크톱·장면에서는 작은 비용이지만 모바일에서도 같다고 가정하지 않는다.

20Hz에서는 Rapier가 프레임당 최대 3회의 60Hz 내부 처리로 이동을 나눈다. 기존 BVH와 처리 횟수가 달라 호출당 수치만으로 비교하기 어렵다.

20Hz 조건 기존 BVH ms/시뮬레이션 초 Rapier ms/시뮬레이션 초
시작점 정지 0.14 1.85
벤치 부근 정지 0.24 1.94
외벽 밀기 2.42 6.95
계단 앞뒤 이동 3.23 3.10

측정 전체에서 자동 복귀는 0회였다. 이는 네 측정 구간의 결과이며, 장면 전체의 이동 문제 해결을 뜻하지 않는다.

초기화와 다운로드

Node에서 충돌 GLB를 읽고 파싱한 후 컨트롤러를 만드는 시간이다. 렌더러와 자산 로딩은 제외한다. 최초 생성은 1회 관찰, 초기화 이후 재생성은 5회 중앙값이다.

준비 비용 기존 BVH Rapier
최초 생성 111.87ms 362.01ms
초기화 이후 재생성 중앙값 88.72ms 265.85ms

Rapier의 최초 생성에는 WASM 초기화가 포함된다. 별도 모듈 import 관찰값 22.79ms는 위 생성 시간에 포함하지 않았다. BVH import는 측정 전에 끝났으므로 import 시간의 직접 비교는 하지 않는다. 재생성에도 변동이 있으며 원본에 각 값을 남겼다.

현재 프로덕션 산출물의 크기는 다음과 같다. MB는 1,000,000바이트, gzip은 로컬 Python gzip.compress(..., compresslevel=9, mtime=0)로 계산한 값이다. 실제 전송량은 서버 압축·캐시 정책에 따라 달라진다.

로딩 구성 원본 MB gzip MB
Three.js 기본 앱 JS 3.201 1.061
Rapier 선택 시 추가 JS/WASM 청크 4.336 1.653
PlayCanvas 기본 앱 JS 1.982 0.502
Ammo WASM 경로의 JS glue + WASM 1.190 0.341

Rapier 청크는 선택 시에만 로드한다. Ammo의 JS fallback은 WASM 경로와 함께 합산하지 않는다. 기본 앱 JS에는 렌더링 등 다른 코드도 들어 있으므로 이 표가 물리 라이브러리만의 크기 비교는 아니다.

두 앱이 사용하는 Sunnyvale SOG 58.07MB와 충돌 GLB 6.18MB는 위 코드 크기에 포함하지 않았다. 실제 초기 로딩에서는 공통 자산 비용도 크다. 현재 Rapier 구현은 핫스팟 가림 검사와 충돌 면 표시용 BVH도 유지하므로 두 충돌 구조를 갖는다. 이후 브라우저 관찰에서 Rapier 선택 시 BVH만 사용하는 경우보다 메인 JS heap 약 4.91MB, backing storage 약 10.52MB가 더 컸다. 이는 현재 구현의 GC 후 관찰값이며 GPU나 총 물리 메모리는 아니다. 조건과 한계는 로딩·메모리 비교에 있다.

브라우저에 추가한 기록

두 앱의 위치 JSON 저장에 초기화와 walkingTimings를 추가했다. 전체 호출 수·평균·최대 외에 최근 600호출의 평균·p95·최대, 입력 없음/이동 요청의 분리 통계, 시뮬레이션 초당 처리 시간을 저장한다. 벽에 막힌 채 W를 누르는 경우도 이동 요청으로 분류한다. 숨겨진 탭과 카메라 경로 재생 구간은 기록에서 제외한다.

앱 기록 범위
Three.js walker.step: 충돌, 보행 정책, Rapier 선택 시 물리 월드 처리
PlayCanvas 엔진의 app.stats.frame.physicsTime: 물리 월드, rigidbody 동기화와 접촉 처리

PlayCanvas의 입력·속도 설정과 앱의 턱 보정 등은 엔진 물리 시간 바깥에 있을 수 있다. 범위가 다르므로 물리 라이브러리 전체 작업량의 동일한 비교로 표현하지 않는다. PlayCanvas는 ammoInitializationMs와 controllerBuildMs를 나누어 저장하고, Three.js의 준비 시간에는 선택 청크·WASM·충돌 구조 준비가 포함되므로 준비 시간 역시 범위를 확인한다.

기존 비교 경로 재생은 카메라를 직접 이동한다. Three.js에서는 보행 호출을 건너뛰고, PlayCanvas에서는 플레이어 body를 비활성화해도 물리 월드 업데이트가 남는다. 기존 재생 결과만으로 보행 비용을 판단하지 않는다.

Playwright MCP 브라우저 측정 완료

조건과 원본

원본·환경·제외한 실행: physics-browser.json. 측정 본문: playwright-physics.mjs (원본 저장소 파일).

앱이 기록한 물리 계산 시간

각 값은 3회 실행의 중앙값이다. 평균·p95는 호출당 ms, 마지막 열은 기록된 처리 시간 합계를 시뮬레이션 시간으로 나눈 값이다. 프로세스 전체 CPU 사용률이 아니다. 페이지 시계 해상도가 약 0.1ms이므로 작은 차이의 정밀한 순위는 해석하지 않는다.

컨트롤러 조건 평균 ms p95 ms ms/시뮬레이션 초
Three.js BVH 시작점 정지 0.031 0.1 0.89
Three.js BVH 외벽 밀기 0.225 0.3 11.54
Three.js Rapier 시작점 정지 0.143 0.2 4.12
Three.js Rapier 외벽 밀기 0.384 0.6 12.84
PlayCanvas Ammo 시작점 정지 0.079 0.2 3.84
PlayCanvas Ammo 외벽 밀기 0.088 0.2 4.27

Three.js는 보행 정책까지 포함한 walker.step, PlayCanvas는 엔진의 physicsTime이다. PlayCanvas의 앱 입력·턱 보정 등은 표의 시간에 포함되지 않을 수 있다. 또한 보행 정책도 완전히 같지 않다. 이번 PlayCanvas 기록의 stepClimbing은 false이고, Rapier에는 0.2m 턱 오르기 설정이 있다. 이 표로 Ammo가 Rapier보다 같은 작업을 몇 배 빠르게 처리한다고 결론 내릴 수 없다.

전체 프레임 결과

30초 구간 전체의 FPS와 프레임 간격 p95의 3회 중앙값이다. 물리 계산, 렌더링과 앱 실행을 함께 반영하며 물리 라이브러리 단독 지표가 아니다.

컨트롤러 조건 평균 FPS 프레임 간격 p95 ms
Three.js BVH 시작점 정지 28.5 50.5
Three.js BVH 외벽 밀기 50.3 49.3
Three.js Rapier 시작점 정지 28.4 50.3
Three.js Rapier 외벽 밀기 26.5 116.7
PlayCanvas Ammo 시작점 정지 48.0 33.8
PlayCanvas Ammo 외벽 밀기 47.3 33.7

이번 조건에서는 PlayCanvas의 프레임 간격이 더 일정했다. Three.js 정지 조건에서는 BVH와 Rapier가 비슷했으며, 벽 조건에서는 Rapier가 더 긴 프레임 간격을 보였다. 물리 시간만으로 이 차이를 설명할 수 없다. 렌더러 CPU/GPU 프로파일과 동일한 카메라·접촉 위치 비교가 없으므로 원인은 미확정이다.

같은 시작 좌표와 W 입력을 사용해도 최종 접촉 위치는 달랐다. 특히 Rapier 외벽 첫 회차는 10초 준비 이후에도 약 0.83m 이동했고 이후 반복에서는 정착했다. BVH·Ammo는 서로 다른 접촉 지점에 정착했다. 따라서 같은 입력이 동일한 충돌 작업량이나 같은 화면을 보장하지 않는다. Three.js의 기록 dt는 최대 0.05초로 제한되므로 시뮬레이션 초와 실제 경과 시간도 다를 수 있다.

시각 근거: BVH 시작점, BVH 벽, Rapier 시작점, Rapier 벽, Ammo 시작점, Ammo 벽.

도구 오류와 제외 기준

PlayCanvas 위치 JSON의 download.saveAs가 두 번 페이지/컨텍스트 종료 오류를 반환했다. 이후 시험 탭이 두 개 남아 있었던 예비 정지 3회·벽 1회는 제외하고 단일 탭에서 모두 다시 측정했다. BVH의 첫 벽 시험도 시작점 복귀 전에 페이지가 닫혀 제외하고 재실행했다. 페이지 종료의 원인은 확정하지 않았다.

유효 PlayCanvas·BVH 기록은 기존 ‘위치 JSON 저장’ 핸들러의 JSON Blob을 페이지 안에서 읽어 저장했다. 다운로드용 클릭만 잠시 억제하고 변경한 브라우저 함수를 finally에서 복구했다. Rapier는 실제 다운로드 JSON을 사용했다. 세 방식의 측정 완료 시 콘솔 조회에는 오류·경고가 없었으며, 초기 페이지의 favicon 404는 별도 로드 문제였다.

네트워크·캐시는 통제하지 않았고 world 응답 가로채기는 HTTP 캐시를 비활성화한다. 이번 실행은 cold/warm 로딩, 메모리, 모바일, 전력 소모나 장시간 안정성 시험이 아니다. 앞선 카메라 경로 재생과 빌드·장면·viewport·동작이 달라 합산하지 않는다.

다음 판단에 필요한 자료

데스크톱에서 정지·외벽 보행과 아래 1·2번의 직접 측정을 완료했다. 같은 조건을 사용자에게 다시 수동 실행하도록 요청할 필요는 없다. 남은 비용과 유지보수 부담은 3·4번과 저작·배포 작업량을 확인한다.

  1. 완료: Sunnyvale에서 GC 후 페이지·worker 메모리를 관찰하고 Knock Hall에서 구현별 왕복 20회 후 자원 추이를 기록했다. GPU 메모리와 장시간 누수 여부는 미확정이다.
  2. 완료: 요청 가로채기 없이 cold/warm 로딩 각 3회와 실제 HTTP 전송량을 기록했다. 상세 결과는 로딩·메모리 비교에 있다.
  3. iPhone Safari의 공통 장면 정지·합성 이동 프레임과 처리 시간은 측정했다. PlayCanvas의 이동 기록은 바닥 밖 무한 낙하로 비교에서 제외했다. 실제 손가락 보행·상호작용·회전, 대형 장면의 동일 경로 보행과 Android 확인은 남아 있다. Mac 결과로 모바일 여력을 예측하지 않는다.
  4. 10분 탐색·반복 시대 전환으로 추락·복귀·멈춤·context loss를 확인하고, 같은 계단·경사 정책을 구현하는 작업량을 비교한다.

이동감이 비슷한 현재 상태에서는 작은 물리 계산 차이와 함께 위 항목을 고려한다. BVH를 제거하려면 핫스팟 가림 검사와 충돌 면 표시를 대체해야 하므로 바로 삭제하지 않는다.

재실행

pnpm benchmark:physics --output artifacts/physics-cost.json

브라우저 기록은 pnpm check 후 ./dev.sh --preview both로 최신 산출물을 사용한다. 서버는 둘 다 켜도 측정 브라우저에는 앱 하나만 연다. Three.js의 Sunnyvale에서 controller=bvh 또는 controller=rapier를 선택하고, PlayCanvas도 같은 Sunnyvale 장면을 사용한다.

Playwright MCP 절차는 AGENTS.md (원본 저장소 파일)에 있다. 브라우저 측정 본문 (원본 저장소 파일)의 measure 함수를 browser_run_code_unsafe에서 호출한다. 먼저 로딩·GPU·단일 탭을 확인하고, 벽 시험에는 위 응답 좌표 변경을 따로 적용한다. 이 파일은 CLI에서 브라우저를 여는 실행기가 아니다.

Node 측정 도구는 benchmark-physics.ts (원본 저장소 파일), 통계 구현은 step-timings.ts (원본 저장소 파일)에 있다.