절차적 게임 개발을 위한 선호 도구와 기법
(cprimozic.net)- 브라우저에서 실행되는 3D 씬·레벨 프로젝트가 커지면서, 전체 월드 생성보다 레벨의 특정 요소에 절차적·생성 기법을 적용하는 재사용 도구가 쌓임
- 텍스처 작업의 중심은 Triplanar Mapping과 Hex Tiling이며, UV 없이 텍스처를 입히거나 반복 패턴을 숨기는 데 활용됨
- 두 기법 모두 보간 가중치에
pow()를 적용해 지배적인 축이나 조회 결과의 비중을 키우고, 일부 텍스처 조회 생략으로 성능 부담을 줄임 - 비싼 프래그먼트 셰이더는 Depth Pre-Pass로 완화할 수 있으며, overdraw가 많은 씬에서는 성능이 30% 이상 개선될 수 있음
- 메시·지오메트리 쪽에서는 LoD terrain, 런타임 메시 처리 파이프라인, 향후 Constructive Solid Geometry를 통해 장식·배경·손상 표현을 확장하려는 흐름이 있음
브라우저 3D 씬에서 쌓인 절차적 도구들
- 몇 년 동안 브라우저에서 실행되는 3D scenes and levels를 만들면서, 커스텀 셰이더 중심의 독립 데모가 서로 연결된 게임 같은 형태로 커짐
- 공통된 접근은 완전한 절차 생성 월드보다 레벨의 특정 부분에 절차적·생성 기법을 적용하는 방식임
- 여러 레벨에서 반복 사용되는 절차적·반절차적 도구와 효과가 자연스럽게 축적됨
셰이더와 텍스처
- 대부분의 텍스처는 양축으로 끊김 없이 타일링되는 seamless texture이며, 넓은 영역에 펼치면 반복 패턴이 눈에 띌 수 있음
- Three.JS의
MeshPhysicalMaterial을 확장한 커스텀 셰이더에 여러 기능을 추가해 seamless texturing 지원을 개선함 -
Triplanar Mapping
- Triplanar Mapping은 거의 모든 레벨에서 쓰이는 텍스처링 핵심 도구임
- 미리 정의된 UV map 없이 seamless texture로 메시를 텍스처링할 수 있어, 절차 생성 terrain처럼 모델러가 UV mapping을 정의할 기회가 없는 경우에 유용함
- 생성 메시와 손으로 모델링한 메시 모두에 잘 동작함
- 구현은 가볍고 단순하며, 참고용 구현은 triplanarMapping.ts에 있음
-
Triplanar Mapping 개선
- 일반적인 Triplanar Mapping은 프래그먼트 normal을 기준으로 세 축의 텍스처 조회를 선형 혼합함
- normal이 단일 축에 가깝지 않은 영역에서는 텍스처가 층처럼 보일 수 있음
- 가중치에 높은 지수의
pow()를 적용한 뒤 다시 정규화하면 지배적인 축의 비중이 커지고 전환 영역이 작아짐 - 이 변환으로 메시 대부분에서 한 축의 가중치가 1에 가까워지고, 다른 두 축의 가중치는 0에 가까워짐
- 임계값보다 작은 가중치의 텍스처 조회를 생략해, Triplanar Mapping의 성능 부담을 일반 UV 기반 텍스처링보다 약간 큰 수준까지 줄일 수 있음
- normal map 처리는 셰이더 코드에서 별도 고려가 필요하며, GPU Gems 방식을 사용함
- 구현 세부 내용은 Normal Mapping for a Triplanar Shader에서 볼 수 있음
-
Hex Tiling
- Hex Tiling은 seamless texture의 눈에 띄는 타일링과 반복을 숨기는 알고리듬임
- 재질에 설정 옵션 하나를 추가하는 것만으로 씬이 낮은 완성도의 mockup처럼 보이는 상태에서 semi-realistic에 가까워질 수 있음
- 초기 구현은 Fabrice Neyret의 Shadertoy를 기반으로 하며, Three.JS material system에 맞게 변환해 프로젝트의 메인 material 셰이더에 통합함
- 이후 허가를 받아 Three.JS 프로젝트의 built-in material에 Hex Tiling을 추가할 수 있는 three-hex-tiling 독립 라이브러리로 포팅함
- Triplanar Mapping과 달리 미리 정의된 UV mapping이 필요함
- 두 기법을 함께 쓰면 각 map마다 프래그먼트당 최대 텍스처 fetch가 27개까지 늘어나 실용적이지 않음
- Hex Tiling도 프래그먼트마다 세 조회 결과를 선형 보간하므로, Triplanar Mapping에서 쓴
pow()가중치 기법으로 성능과 결과 품질을 함께 개선할 수 있음
-
Depth Pre-Pass
- 고급 텍스처링 기법은 큰 씬에서 비싼 프래그먼트 셰이더를 만들 수 있음
- Depth Pre-Pass는 전체 씬을 매우 단순하고 저렴한 material로 먼저 렌더링해 각 픽셀의 depth를 기록하는 방식임
- 씬을 두 번 렌더링하는 오버헤드가 있지만, overdraw가 많은 씬에서는 대체로 비용보다 이득이 큼
- overdraw가 많은 경우 Depth Pre-Pass 추가로 성능이 30% 이상 개선될 수 있음
- pre-pass 설정을 바꾸면 occluded fragment만 렌더링해, pre-pass 사용 시 건너뛰게 되는 프래그먼트를 시각화할 수 있음
- Three.JS 구현과 설정 세부 내용은 dedicated article에서 볼 수 있음
-
AI 기반 PBR 텍스처 합성
- AI-generated texture는 거의 모든 씬에서 사용됨
- 절제해서 쓰면 결과가 꽤 좋아 보일 수 있으며, 예시 씬의 모든 텍스처가 AI-generated임
- 텍스처 생성, PBR map 생성, 업스케일 없이 seamless 4K texture로 결합하는 과정은 별도 글에서 다룸
- 해당 글에서 PBR map 생성을 위해 언급한 웹사이트는 더 이상 사용할 수 없음
- 현재는 normal map 생성에 DeepBump를 쓰고, 필요한 경우 다른 map에는 Materialize 같은 비-AI 도구를 사용함
-
Volumetric Fog/Clouds
- volumetric rendering은 씬에 독특한 효과를 줄 수 있어 관심을 가져온 분야임
- 어떤 Three.JS 씬에도 cloud나 fog를 추가할 수 있는 비교적 범용적인 셰이더를 만듦
- Inigo Quilez의 Shadertoy에서 영감을 받아 유사한 LoD noise lookup을 쓰는 기본 volumetric clouds shader를 만들고, 이후 더 일반적이고 설정 가능한 형태로 확장함
- 이 셰이더는 드문드문한 레벨의 빈 공간을 채우고, 움직이는 cloud나 fog로 정적인 레벨에 동적 느낌을 더하는 데 유용함
three-good-godrays프로젝트에서 n8programs가 개발한 코드와 접근도 일부 사용함three-good-godrays도 자주 사용되며, 레벨에 매우 독특한 분위기를 더함
메시와 지오메트리
- 런타임 메시 생성은 점점 더 많이 다루는 영역임
- 소프트웨어 seed에서 월드가 자라나는 아이디어를 좋아하지만, 절차 생성을 내세우는 일부 게임의 “무한하지만 비어 있는” 현상은 피하려 함
- 그래서 핵심 경험 전체를 절차 생성하기보다 장식, 배경, 절차적 flourish를 레벨에 추가하는 데 주로 집중함
-
LoD Terrain
- terrain generation은 절차적 게임 개발의 대표적인 영역이며, 구현 자체는 특별하지 않음
- 대부분의 방식처럼 noise function으로 terrain heightmap을 만들고, 이를 triangle로 tessellate해 렌더링함
- 텍스처링에는 Triplanar Mapping 또는 Hex Tiling을 사용함
- 핵심은 LoD 시스템으로, terrain을 tile 단위로 만들고 각 tile을 여러 해상도로 생성함
- tile과 camera 사이의 거리에 따라 서로 다른 해상도를 동적으로 교체함
- 이 terrain generation system은 자주 재사용되며, 유연하고 효율적인 시스템 덕분에 적은 노력으로 여러 레벨에 적용 가능함
-
절차적 메시 처리·조작 파이프라인
- 최근 가장 많이 작업한 부분은 procedural mesh processing 파이프라인임
- 초기 목표는 동적으로 생성된 메시까지 포함해 low-poly mesh를 절차적으로 subdivide하고 deform하는 것이었음
- platform, boulder, 큰 구조물 같은 단순 메시를 레벨에 넣었을 때 더 현실적이거나 흥미롭게 보이게 하려는 목적임
- 이 작업은 브라우저 런타임에서 raw geometry data를 받아 임의로 수정하고, 렌더링 가능한 형식으로 다시 내보내는 소프트웨어 파이프라인으로 이어짐
- 이 과정에는 특히 normal 처리에서 세부적인 고려가 필요함
- 구현 세부 내용은 subdividing meshes for displacement 글에 있음
다음 실험 후보: Constructive Solid Geometry
- 여기에 나열된 도구 대부분은 원래 특정 사용 사례를 위한 일회성 구현으로 시작했지만, 다른 레벨과 맥락에서 반복적으로 재사용됨
- 다음으로 시도하고 싶은 주요 아이디어는 Constructive Solid Geometry임
- Constructive Solid Geometry는 3D 공간에 boolean operator를 적용하는 시스템임
- 임의의 두 mesh를 합칠 수 있음
- mesh에서 chunk를 잘라낼 수 있음
- 그 외 유사한 조작을 수행할 수 있음
- csg.js는 약 500 LoC의 주석 달린 JavaScript 파일 하나로 mesh primitive, boolean operator, 깔끔한 API를 포함한 CSG toolkit을 구현함
- 언젠가 이 라이브러리를 Rust로 포팅하면서 작동 방식을 더 잘 이해할 계획임
- CSG를 기존 mesh processing pipeline과 함께 쓰면 흥미로운 결과를 만들 가능성이 큼
- 특히 절차적으로 mesh를 damage하는 기능을 시도하고 싶어함
- building이나 bridge에서 chunk를 잘라내 decay나 weathering을 시뮬레이션함
- wall이나 road에 crack을 생성함
댓글과 토론
Hacker News 의견들
-
예전에 절차적 생성을 좀 만져봤고, 특히 보기 좋은 나무를 만들려고 했는데, 나에게 빠져 있던 조각은 지오메트리를 쉽게 연결하는 방법이었음
원통 두 개를 만드는 건 쉽지만, 둘을 자연스럽게 이어 붙이는 건 매우 어려웠다
이론적으로는 CSG가 이 영역을 채울 수 있지만, 문제를 그렇게 생각하는 방식이 쉽지 않았음. 단순히 정점을 추가하는 반복문으로 볼 수 없고, 모든 것을 3D 형상으로 모델링해야 하기 때문임
정점 루프 두 개를 받아 휴리스틱으로 면을 추가해 연결하는 루틴도 만들어 봤지만, 어떤 정점끼리 연결해야 할지 고르는 게 예상보다 훨씬 어려웠고, 보기 나쁜 연결이 쉽게 나왔음
언젠가는 서로 다른 모듈형 절차적 생성 시스템들이 즉흥성을 유지하며 협력하는 게임을 만들고 싶다. 예를 들어 시스템들이 월드의 일부를 “점유”하고, 그 일부를 다른 시스템에 위임하거나, 주변 요소들과 자연스럽게 연결하는 식임- 계속 해보되 너무 복잡하게 만들지 않는 게 좋겠음
최근 생성형 나무를 만드는 영상 [0]이 있었는데, 해법은 그냥 원통들이 서로 교차하게 두는 것이었음. 이런 해키한 방식도 잘 작동하고 결과가 괜찮을 수 있다
말한 것처럼 CSG도 가능하지만 과하게 복잡할 수 있음. 또 다른 방법은 나무의 골격을 만든 뒤 로프팅을 하고, 필요하면 CSG와 결합해서 줄기와 껍질을 만드는 것임
좋은 라이브러리는 가능성을 열어줌. 원통 결합이나 지오메트리 빼기 같은 불리언 연산을 해주는 3D 지오메트리 라이브러리를 찾으면 새로운 아이디어를 많이 시도할 수 있다. 몇 가지를 써봤는데 그나마 마음에 든 건 JSCAD [1]뿐이었음
[0] https://youtu.be/8zMbJmuwEUc?si=KQclrVPeSrIRmsbA
[1] https://github.com/jscad/OpenJSCAD.org - 부호 있는 거리장을 찾아보면 좋음. 절차적 지오메트리에서 가장 우아한 CSG 해법에 가깝다
3D 형상을 함수형으로 서술하고 변형하는 방식과 비슷함
데모씬에서 사람들이 이걸로 뭘 했는지 보려면 YouTube에서 Mercury Delight를 검색해보는 걸 강력히 추천함. Shadertoy에도 예제가 많고, 정말 놀라운 것들이 많다 - 반복문을 펼칠 수 있다면, 정점을 추가하는 반복문 방식으로도 볼 수 있음
https://en.wikibooks.org/wiki/OpenSCAD_User_Manual/Condition...
아래 예제도 참고할 만함
https://github.com/MaxBondABE/batteries/blob/master/src/geom...
- 계속 해보되 너무 복잡하게 만들지 않는 게 좋겠음
-
좋은 지형 생성은 월드 규모든 눈높이 규모든 전혀 사소하지 않음
단순한 접근은 글에서 보이는 것처럼 울퉁불퉁한 높이맵을 만들고, 현실의 지형과 거의 닮지 않았으며 탐험하기에도 별로 흥미롭지 않다
예를 들어 Dwarf Fortress는 기본적인 중점 변위에서 시작하지만, 이후에 맞춤형 후처리를 많이 거침- 이게 게임 업계 전체의 문제라고 봄
이 글에 나온 것 중 막연하게라도 사소한 건 없음. 작성자는 GPU 셰이더 렌더링에 한해서는 지구상의 거의 모든 사람과 비교해도 명백히 100배 개발자에 가까움
“단순한 접근”이라니, 정말 단순한가?
80억 명 중 이 글을 시작하는 데 필요한 “Hello World” 단계라도 구현할 수 있는 사람이 몇 퍼센트나 될까? 셰이더가 뭔지 아는 사람은 또 얼마나 될까? OpenGL 일자리가 그렇게 많은 것도 아닌데 말이다. “OpenGL이 뭐죠, 우리는 Unity만 써요” 같은 상황도 흔함
온라인 게이머는 어떨까? 2024년 7월 28일 오후 1시 22분 EST 기준 Counter Strike 2에 접속 중인 1,021,282명 [1] 중, 자신들이 플레이하는 게임에 필요한 셰이더의 첫 단계라도 구현할 수 있는 사람이 몇 퍼센트나 될까?
단순한 명령줄 C++ 프로그램을 컴파일하거나 브라우저에서 더 단순한 JavaScript 스크립트를 작성할 수 있는 사람은 몇 퍼센트일까? 사실 이건 약간 함정 질문인데, 대부분의 사람은 이메일도 겨우 다루기 때문임
[1] https://steamdb.info/app/730/charts/ - 울퉁불퉁한 높이맵을 만든 다음 침식 시뮬레이션으로 지형을 생성하는 방식을 본 적 있음
- 이 글은 “출시 가능한 수준”이라기보다 좋은 출발점으로 봤음
AI와 마찬가지로, 최고의 절차적 콘텐츠 생성도 최종 산출물에는 맞춤형 후처리가 들어갈 것임
- 이게 게임 업계 전체의 문제라고 봄
-
웹에서 쓰는 여러 절차적 기법을 잘 정리한 글이라 정말 도움이 됨
작업 중인 절차적 RPG 레벨 편집기에 관심 있으면 https://github.com/gamedevgrunt/3D-Action-RPG-JavaScript를 참고해도 좋음 -
깊이 사전 패스를 써본 경험은 엇갈렸음
몇 번 시도했을 때 중급~고급 데스크톱 GPU에서는 눈에 띄는 성능 향상을 보지 못했다
정확한 이유는 모르겠지만, 초기 Z 제거가 픽셀 셰이더 호출을 줄여줬기 때문일 수 있다고 봄. 보통 불투명 메시를 앞에서 뒤 순서로 렌더링함
다만 내 실험은 게임이 아니라 CAD/CAM 애플리케이션 맥락이었음. 장면도 일반적인 게임 환경과 꽤 달랐고, 텍스처는 적고 폴리곤 수가 매우 많은 지오메트리였다- 이런 기법들은 전부 상황 의존적이고, 게임과 엔진 전체뿐 아니라 특정 아트워크와 장면 구성에도 좌우됨
깊이 사전 패스는 종종 가려짐 컬링의 첫 단계이기도 하지만, 이것도 마찬가지로 상황에 따라 다르다. CAD 모델보다는 복잡한 도시 풍경에서 훨씬 유용할 가능성이 큼 - 정점 처리에 병목이 걸린 상태라면 깊이 사전 패스는 도움이 되지 않음
깊이 복잡도가 높고 조각 셰이더가 비싼 경우에 가장 유용하다. 공정하게 말하면 대부분의 게임이 여기에 해당함
지연 렌더러에서는 보통 필수는 아니지만, forward+에서는 대체로 꽤 이득이 된다
- 이런 기법들은 전부 상황 의존적이고, 게임과 엔진 전체뿐 아니라 특정 아트워크와 장면 구성에도 좌우됨