위성(Google Earth) 시점으로 탐험하는 3D 절차 생성 도시. 10 km × 10 km 사막 한가운데 도시가 서 있고, 고층 빌딩·대로/골목·고속도로·강과 교량·공원과 강둑 숲·주차장·지하철·자동차·보행자가 모두 실시간으로 시뮬레이션됩니다.
- 교통 체계: 모든 교차로에 4구(빨강·노랑·좌회전 화살표·초록) 차량 신호등과 2구 보행자 신호등. 축별 직진 → 노랑 → 좌회전 화살표 순환. 차량은 진로(직진/우회전/좌회전)에 맞는 신호에만 진입하고, 횡단 중인 보행자에게 양보하며, 차선 점유 격자로 앞차와 겹치지 않게 정지합니다. 교차로 회전과 차선 변경은 베지어 곡선/조향으로 부드럽게 처리됩니다.
- 보행자: 인도·횡단보도·공원 산책로·잔디 위를 걷고, 보행 신호가 초록일 때만 건넙니다.
- 시간대: 교통량·보행자 수가 시간대 곡선(출퇴근 러시아워 포함)에 따라 자동 조절됩니다.
- 조명: 백열등 색의 가로등·창문·전조등, 블룸 후처리(MSAA 유지).
프로젝트를 내려받은 뒤 run.bat 을 더블클릭하면 됩니다. Node.js 설치 여부를 확인하고,
처음 실행할 때만 패키지를 자동으로 내려받은 뒤 개발 서버를 띄우고 브라우저를 엽니다.
Node.js 가 없으면 설치 페이지를 안내합니다. 배포용 정적 빌드를 보려면 build.bat 을 실행하세요.
npm install
npm run dev # http://localhost:5173
npm run build # dist/ 정적 빌드| 입력 | 동작 |
|---|---|
| 드래그 | 이동 (팬) |
| 우클릭 드래그 | 회전 / 기울이기 |
| 휠 | 커서 방향으로 줌 |
| W A S D | 이동 |
| 클릭 | 건물 정보 / 차량·버스 추적 |
| ESC | 추적 해제 |
| N | 밤 ↔ 낮 전환 |
| 우측 패널 | 시각 슬라이더, 시간 흐름, 지하철 X-ray, 그림자, 시점 초기화 |
| 파일 | 역할 |
|---|---|
src/citygen.js |
도로 그래프(대로/골목), 블록, 건물(층수·셋백), 공원/강둑 숲, 강, 교량, 지하철 노선 생성 |
src/terrain.js |
사막 지형(모래 언덕·강바닥) + 지형 채색 셰이더, 강 수면 셰이더 |
src/buildings.js |
건물 인스턴싱, 절차적 창문/야간 조명 셰이더, 블록 슬래브 |
src/roads.js |
도로 지오메트리, 차선·횡단보도·정지선·가로등 셰이더, 교량 |
src/vegetation.js |
20종 나무 인스턴싱, 적응형 공간 컬링, 바람 흔들림 |
src/traffic.js |
차량/버스 주행, 차선 점유 격자, 신호/보행자 양보, 곡선 회전 |
src/signals.js |
교차로 신호 주기, 차량·보행자 신호등 모델 |
src/schedule.js |
시간대별 교통량/보행자 밀도, 러시아워 |
src/parking.js |
주차장 슬래브·주차선·주차 차량 |
src/lamps.js |
가로등 |
src/people.js |
보행자 (인도·횡단보도·공원 산책로·잔디) |
src/subway.js |
지하철 열차 운행, 역 출입구, X-ray 오버레이 |
src/sky.js |
하늘 돔, 태양/반구광, 낮밤 주기, 안개 색 |
src/main.js |
렌더러(MSAA, 로그 깊이 버퍼, ACES 톤매핑), 카메라, 그림자, HUD, 루프 |
generateCity(seed) 의 시드를 바꾸면 다른 도시가 생성됩니다.
node scripts/simtest.mjs 로 렌더링 없이 교통/보행자 시뮬레이션을 헤드리스로 검증할 수 있습니다.
- 창문마다 실내 장면:
src/interiors.js가 소실점 원근의 방(천장 조명·뒷벽·바닥·박스 가구·사람 실루엣·블라인드/커튼)을 캔버스로 그려 256 타일 아틀라스(사무실 100·주거 100·상점 24·소등 32)를 만들고, 창마다 다른 타일 + 가짜 시차(parallax) + 건물 종류별 색온도(사무실 냉백색·주거 전구색)로 표시. 22시 이후 소등 비율 증가(uLate). - 후처리 스택(
src/postfx.js,src/main.js): 좁은 블룸 + 넓은 블룸(다단), 스타버스트 광선(아주 밝은 광원만), 비네팅(그레인은 지글거림 때문에 0으로 꺼둠), 자동 노출(24×24 저해상도 렌더의 평균 휘도), 장노출 라이트 트레일(L토글), 틸트시프트(T토글). - 원거리 야경: 창이 픽셀보다 작아지면 밉맵된 창불빛 텍스처(
buildFarTexture, 백열/할로겐 색 편차 포함)로 대체해 거리별 평균이 자연스럽게 나온다. 조명 색온도는 사무실 할로겐(3000K)·주거 백열(2700K). - 지면 연무(
src/haze.js): 카메라가 낮을 때만 보이는 얇은 안개 층. - 간판(
src/signage.js): 상가 1층 네온 간판, 고층 오피스 LED 전광판(스크롤 애니메이션). 붉은 항공장애등은 요청으로 제거. - Spot: 저장한 뷰포인트 이름을 클릭해도 이동.
- 화면 밖 보행자를 보수적인 경계 구로 판정해 GPU 전송에서 제외합니다. 인원·이동·횡단보도 시뮬레이션은 유지하며, 화면 안의 위치와 옷 색도 그대로 복사합니다.
- 보행자 상세 처리 범위를 사람마다 계산하지 않고 도로별로 캐시합니다. 차량의 도로 방향 배열은 재사용하고 차량 행렬은 활성 구간만 전송합니다.
- 신호등은 표시 상태가 변할 때만 색을 갱신하고 해당 버퍼 구간만 전송합니다.
- 자동 노출은 비동기 GPU 읽기를 사용하며, 노출 측정용 렌더에서 본 화면의 그림자를 재사용합니다. 해상도, MSAA, 4096 그림자, 블룸 설정은 유지합니다.
- 수목은
src/tree-species.js의 20종을 인스턴싱합니다. 참나무·느티나무·벚나무·버드나무·침엽수·야자수 등의 수관 모양, 높이, 색을 구분했습니다. 공원 식재는 별도의 Mulberry32 시드를 사용하므로 기존 도로·건물·지하철 배치는 바뀌지 않습니다. 산책로에서 5m를 비우고 나무 중심 간격은 최소 3m를 확보합니다. - 시드 7 기준 나무 38,918 → 60,492그루. 동일 정점 공유로 수목 정점 데이터는 기존 12,142,416 → 9,820,857개가 됩니다. 수종별 묶음은 2 → 20개이고 삼각형 수는 늘어나므로 전체 GPU 부하 감소율이나 FPS 개선율을 이 숫자로 단정할 수는 없습니다.
검증: node scripts/perfcheck.mjs와 node scripts/simtest.mjs.
원본 소스 디렉터리를 보관했다면 node scripts/perfcheck.mjs /absolute/path/to/original/src로 차량·보행자 상태와 신호 색을 원본과 비교할 수 있습니다. 동일 인구(차량 최대 18,500, 보행자 최대 180,000), 이동하는 관찰 지점과 밀도 변경을 포함합니다. CPU 측정은 60프레임 준비 후 300프레임이며 GPU 렌더링과 보행자 가시성 추출은 포함하지 않습니다. 로컬 비교에서 평균 CPU 시간 감소는 약 4~14%로 변동했습니다. 전체 FPS는 해상도·시점·GPU 부하에 따라 별도로 측정해야 합니다.
M1 Pro 환경에서 13개 조건을 대조 측정한 결과와 병목 우선순위는 프로파일링 보고서를 참고하세요. node scripts/profile-server.mjs 실행 후 http://127.0.0.1:5173/?profile=1에 접속하면 CPU 구간 시간과 효과별 FPS를 profiles/latest.json에 저장합니다. 일반 실행에서는 계측이 활성화되지 않습니다.
후속 변경으로 블룸 크기 계산, 나무 공간 컬링, 보행자 인덱스를 적용했습니다. 구현 및 검증 결과에 확인된 결과와 남은 브라우저 검증을 기록했습니다. 앞 절의 블룸 설정과 수종별 묶음 설명은 최초 최적화 단계 기준입니다.
추가로 차량 반복 작업 제거, 고정 시각의 조명·HUD 갱신 생략, 정적 그림자 캐시를 적용했습니다. 추가 최적화 검증 결과에 CPU 비교와 그림자 픽셀 검증을 기록했습니다. 앞차 인덱스는 실측에서 느려져 기본 코드에서 제외했습니다. 그림자 캐시는 ?shadowCache=0으로 끌 수 있습니다. 비교 측정은 ?profile=compare&cycles=2로 두 번 순회할 수 있고, 수집 파일에는 마지막 순회가 남습니다. warmup=300처럼 조건별 준비 프레임 수도 지정할 수 있습니다.