three.js 스킬 이펙트 샌드박스를 게임에 옮길 때 걸리는 것

8/10/2026 ·impact

three.js 스킬 이펙트 샌드박스 Elemental Sandbox 의 요점은 슬라이더 938개가 일시정지 중에도 그대로 먹는다는 데 있습니다. 키를 눌러 장전하면 바닥에 조준 화살표가 뜨고, 마우스로 각도를 잡은 뒤 왼쪽 클릭으로 쏩니다.

코드는 three.js 와 Vite, 손으로 쓴 GLSL 이고 라이선스는 MIT 입니다. 2026년 8월 6일에 만들어진 Elemental Sandbox 저장소이고 릴리스도 배포된 데모도 없어서, 화면을 보려면 클론한 뒤 npm installnpm run dev 를 직접 돌려야 합니다.

슬라이더 938개가 멈춘 화면에서도 먹습니다

슬라이더 938개가 P 키로 시뮬레이션을 멈춘 상태에서도 계속 적용됩니다. 분출 도중이나 타격 순간의 한 프레임을 얼려 두고, 정지 화면에 대고 실루엣과 색과 타이밍을 고쳐 나가는 방식입니다. 저자가 프로젝트 소개 첫머리에 이것을 프로젝트의 요점으로 적어 뒀습니다.

이 방식이 쓸모 있는 건 사람 눈이 그렇게 못 보기 때문입니다. 0.2초짜리 섬광의 색은 재생 중에 비교할 수 없습니다. 되감아 다시 보는 사이 앞 값의 기억이 흐려지니까요. 그럼 녹화해서 프레임끼리 대 보면 되지 않나? 그건 고치고 나서 확인하는 것이고, 편집할 때 필요한 건 고치면서 비교하는 것입니다.

노출된 컨트롤 수는 능력마다 고르지 않습니다. Nova Beam 하나에만 176개가 붙어 있습니다. Frost Lance 는 113개인데 그중 25개가 색이고, Storm Lance 는 123개에 색이 34개입니다. 원형 캐스트가 쓰는 조준 원은 능력과 별개로 40개를 따로 씁니다. 색을 파생 틴트로 묶지 않고 팔레트를 전부 열어 둔 결과입니다.

스킬은 여섯 개인데 조작표에 적힌 것은 다섯 개입니다. src/config/settings.js 1830행 ELEMENTS 배열의 여섯 번째가 glacier 이고, label: 'Glacial Crown', key: 'X' 로 등록돼 InputManager 94~96행KeyXDigit6 를 여섯 번째 슬롯에 물려 뒀습니다.

바닥이 y = 0 이라는 전제가 두 군데 박혀 있습니다

게임에 옮길 때 먼저 걸리는 것은 바닥입니다. 바닥은 y = 0 의 단일 평면이고, 조준 레이캐스트가 노리는 것도 바닥 메시가 아니라 수학적 평면입니다. AimController.js 7행new Plane(new Vector3(0, 1, 0), 0) 을 상수로 박아 두고 광선을 그 평면에 꽂습니다.

평평하게 유지한 것은 의도된 선택입니다. Ground.js 주석에 경로 그리기 레이캐스트와 모든 능력, 땅 분출이 평면을 가정한다고 적혀 있습니다.

원형 캐스트에는 같은 전제가 두 군데 더 들어가 있습니다. 원은 y = 0 의 쿼드 한 장으로 그리고, 스네어의 촉수와 림 아크도 같은 평면에 놓입니다. 둘 다 계단 위로 덮이지 않습니다. 높이 차가 있는 맵이라면 조준 레이캐스트를 지형 콜라이더로 돌리고, 바닥 표시를 평면 쿼드가 아니라 투영 데칼로 다시 짜야 합니다.

혼합 모드도 손봐야 합니다. 조준 원과 스네어 장판이 가산 혼합이어서 바닥을 어둡게 누르지 않고 오히려 밝힙니다. 밝은 색 바닥에서는 경계가 읽히지 않으니 밑에 비가산 패스를 한 장 더 깔아야 합니다.

동시 시전 상한도 이미 정해져 있습니다. 동적 포인트 라이트가 여섯 개뿐이고 빔 하나가 그중 둘을 씁니다. 빔 네 개가 동시에 나가면 풀이 바닥나 LightPool.acquire() 가 null 을 돌려줍니다. 호출부가 가드로 감싸져 있어 터지지는 않고, 넷째 빔의 조명만 빠집니다.

여기까지는 저자가 "Known rough edges" 로 직접 적어 둔 항목입니다. 숨은 결함이 아니라 공개된 전제입니다.

이펙트는 코드로 나오지만 무대는 파일입니다

이펙트만 떼어 갈 사람에게는 유리한 구조입니다. 얼음 결정은 절차적 지오메트리, 번개는 정점 셰이더가 통째로 배치하는 리본 스트립, 운석은 CPU 에서 균열면으로 자른 아이코스피어, 빔은 반지름 셋으로 세 번 그리는 파라메트릭 튜브입니다. 스프라이트 시트가 없습니다.

셰이더로 미는 설계는 드로콜을 줄여 줍니다. 결정 필드 전체가 결정 개수와 무관하게 3회이고 상한은 288개입니다. 번개 하나는 필라멘트 수와 무관하게 2회, 빔 하나는 6회입니다.

파일로 들어 있는 것은 무대와 캐릭터입니다. Idle.fbx 가 리그와 아이들 클립을 함께 싣고, cast1~3.fbx 가 시전 동작을, diffuse.png 가 색을 맡습니다. FBX 넷은 같은 리그의 Mixamo 익스포트이고, 클립이 뼈 이름으로 바인딩되기 때문에 리타게팅 없이 재생됩니다. IBL 용 spruit_sunrise.hdr 한 장이 6MB 가까이 더 붙습니다.

바이너리 개수는 문서와 저장소가 어긋납니다. 문서에 적힌 것은 여섯 개인데 실제로는 열 개이고, 차이는 public/textures/cathedral/ 의 ao·color·normal·roughness 넉 장입니다. 판단이 달라지지는 않습니다 — 이 넷은 ambientCG Rock030 CC0 이고, 파일이 없으면 같은 셰이더가 절차적 돌로 대신 그립니다. 남는 바이너리 의존은 캐릭터 쪽뿐입니다.

저장소 코드는 MIT 이지만 번들된 HDR 프로브와 캐릭터 FBX 는 각자의 원래 라이선스를 유지합니다. Mixamo 익스포트를 게임에 그대로 실어 배포하는 권리는 이 저장소가 주지 않습니다.

이펙트 구현을 참고하려는 경우와 게임에 옮기려는 경우는 답이 다릅니다. 참고가 목적이라면 지금 상태로도 쓸 만합니다. 셰이더 구조와 드로콜 설계를 읽는 데 y = 0 은 방해가 되지 않습니다.

뒤라면 평면 전제부터 다시 짜야 합니다. 화면은 직접 돌려 보지 않았고, 여기 적은 것은 저장소 코드와 문서에서 나온 내용입니다. 첫 줄은 AimController.js 7행의 GROUND_PLANE 을 지형 콜라이더 레이캐스트로 바꾸는 일이고, 둘째 줄은 y = 0 쿼드를 투영 데칼로 갈아 끼우는 일입니다.

Known rough edges 항목 읽기