# CSS로 구현한 3D DOOM 렌더링

> Clean Markdown view of GeekNews topic #27979. Use the original source for factual precision when an external source URL is present.

## Metadata

- GeekNews HTML: [https://news.hada.io/topic?id=27979](https://news.hada.io/topic?id=27979)
- GeekNews Markdown: [https://news.hada.io/topic/27979.md](https://news.hada.io/topic/27979.md)
- Type: GN+
- Author: [neo](https://news.hada.io/@neo)
- Published: 2026-03-29T23:33:06+09:00
- Updated: 2026-03-29T23:33:06+09:00
- Original source: [nielsleenheer.com](https://nielsleenheer.com/articles/2026/css-is-doomed-rendering-doom-in-3d-with-css/)
- Points: 1
- Comments: 1

## Topic Body

- **CSS만으로 3D DOOM을 렌더링**한 실험으로, 모든 벽과 오브젝트를 `&lt;div&gt;`와 **3D 변환(transform)** 으로 구성한 프로젝트
- 게임 로직은 JavaScript가 담당하지만, **렌더링은 전적으로 CSS**가 수행되며 브라우저와 현대 CSS의 한계를 탐구
- **삼각함수, clip-path, @property, SVG 필터, 앵커 포지셔닝** 등 최신 CSS 기능을 활용해 벽, 바닥, 조명, 스프라이트, 폭발 효과까지 구현
- 카메라 개념이 없는 CSS 특성상 **플레이어 대신 세계를 이동**시키는 방식으로 시점을 처리하고, 모든 이동은 커스텀 속성 업데이트로 제어
- WebGL 수준의 성능은 아니지만, **CSS의 표현력과 계산 능력 확장 가능성**을 입증한 사례

---

### CSS로 구현한 3D DOOM 렌더링
- **CSS만으로 DOOM을 렌더링**한 실험 프로젝트로, 모든 벽·바닥·오브젝트가 `&lt;div&gt;`로 구성되고 **3D 변환(transform)** 으로 배치됨
  - 게임 로직은 JavaScript에서 실행되지만, 렌더링은 전적으로 CSS가 담당
  - 프로젝트의 목적은 **브라우저와 현대 CSS의 한계 탐구**

### 고등학교 수학으로 돌아가기
- 원본 DOOM의 **WAD 파일 데이터**(vertices, linedefs, sidedefs, sectors)를 추출해 수천 개의 `&lt;div&gt;`로 정적 장면 구성
- 각 벽은 시작·끝 좌표와 바닥·천장 높이를 **CSS 커스텀 속성**으로 전달받음
- **`hypot()`** 과 **`atan2()`** CSS 함수로 벽의 길이와 회전 각도를 계산
- JavaScript는 원시 데이터를 전달하고, **CSS가 삼각함수를 계산**해 렌더링 수행
- 게임 루프와 렌더러는 분리되어 JS는 상태 관리와 좌표 갱신만 담당

### 좌표계 변환 문제
- DOOM은 Y가 북쪽으로 증가하는 2D 좌표계를 사용하지만, CSS 3D는 Y가 위쪽, Z가 시청자 방향으로 향함
- 변환 시 `translate3d(x,-z,-y)` 형태를 사용해 좌표계를 맞춤
- `rotateY(atan2(var(--delta-y), var(--delta-x)))` 계산이 **추가 변환 없이 작동**하는 점이 특징

### 카메라 대신 세계를 움직이기
- CSS에는 카메라 개념이 없어, **플레이어 대신 세계를 반대로 이동**시키는 방식 사용
- `--player-x/y/z/angle` 네 개의 커스텀 속성만 JS에서 갱신
- `translate: 0 0 var(--perspective)`로 시점 보정, `rotateY`와 `translate3d`로 시야 회전 및 위치 이동
- 모든 이동은 **속성 업데이트만으로 처리**

### 바닥은 눕힌 div
- 기본 DOM 요소는 수직 평면이므로, 바닥은 `rotateX(90deg)`로 눕혀 수평 배치
- **`clip-path`**, **`polygon()`**, **`path()`** 로 복잡한 다각형 영역과 구멍 표현
- 최신 CSS의 **`shape()`** 함수로 퍼센트 기반 경로와 `evenodd` 규칙을 함께 사용 가능

### 텍스처 정렬
- 인접 섹터 간 텍스처가 끊기지 않도록 **월드 좌표 기반의 `background-position`** 사용
- 모든 섹터가 동일한 텍스처 그리드를 공유해 **매끄러운 경계 연결** 구현

### 문, 리프트, @property 애니메이션
- 문 열림은 섹터 천장을 올리는 동작으로, 컨테이너 `&lt;div&gt;`의 `transform`을 **CSS 전환(transition)** 으로 처리
- 리프트는 플레이어가 함께 움직이므로 JS에서 `--player-z`를 동기화
- `@property`로 커스텀 속성을 **숫자형으로 등록**해 부드러운 낙하·이동 효과 구현

### 스프라이트와 미러링
- 적 스프라이트는 항상 카메라를 향하는 **빌보드 방식**
- 8방향 중 5세트만 실제 이미지, 나머지는 **좌우 반전(scaleX)** 으로 처리
- **`steps()` 애니메이션**으로 걷기·공격·사망 프레임 전환
- 모든 적이 동시에 걷는 문제는 JS의 **무작위 `animation-delay`** 로 해결

### 발사체, 폭발, 총탄 효과
- 로켓·화염구 등은 CSS 애니메이션으로 **A→B 이동 자동 처리**
- JS는 시작·끝 좌표와 지속시간만 설정, 충돌 시 요소 제거 및 폭발 스프라이트 생성
- 폭발과 총탄 연기는 **`steps()` 기반 3프레임 애니메이션** 후 자동 삭제

### 조명과 필터
- 섹터별 밝기값을 `--light` 속성으로 지정하고, 내부 요소는 **`filter: brightness()`** 로 상속
- 깜박이는 조명은 `@keyframes`로 `--light` 값을 주기적으로 변경
- 투명한 적(Spectre)은 **SVG 필터**(`feColorMatrix`, `feTurbulence`, `feDisplacementMap`)로 왜곡된 실루엣 표현

### 반응형 UI와 앵커 포지셔닝
- 게임은 **모바일 대응형**으로, HUD는 `flex-wrap`으로 줄바꿈
- 무기 스프라이트는 HUD 높이에 맞춰 **`anchor-name` / `position-anchor`** 로 위치 자동 조정
- 터치 조작 버튼도 동일한 앵커 방식으로 배치

### 관전자 모드
- **맵 전체 조망**과 **3인칭 추적 시점** 지원
- CSS의 `sin()`·`cos()` 함수를 이용해 플레이어 뒤쪽 카메라 위치 계산
- `rotate`·`translate` 속성을 분리해 **부드러운 시점 전환** 구현
- JS는 위치·각도만 갱신하고, 카메라 수학은 CSS가 처리

### 컬링과 성능
- 수천 개의 3D 요소로 인해 브라우저 **컴포지터 부하** 발생
- JS 기반 컬링: 시야 밖 요소를 `hidden` 처리
- CSS 기반 컬링 실험: 계산값으로 `visibility` 제어, **타입 그라인딩(type grinding)** 트릭 사용
- `if()` 함수가 표준화되면 더 간결한 조건식으로 대체 가능

### 깊이 정렬
- 브라우저가 **깊이 정렬(z-order)** 을 자동 처리
- 동일 평면의 오브젝트는 **미세한 오프셋**을 주어 깜빡임 방지

### DOOM의 ‘속임수’와 하늘 처리
- 원본 DOOM은 하늘을 2D 텍스처로 “벽” 위에 그리는 **투영 트릭** 사용
- CSS 렌더러는 실제 3D 공간에 하늘을 배치해야 하므로, 일부 장면에서 **맵 뒤쪽이 노출**되는 문제 발생
- 해결책은 컬링 단계에서 하늘 벽 뒤의 요소를 **렌더링 제외**

### 결론 — CSS의 한계와 가능성
- 전체 게임 루프는 JS로, 렌더링은 **순수 CSS 기반**으로 분리
- **삼각함수, @property, clip-path, SVG 필터, 앵커 포지셔닝** 등 최신 CSS 기능을 극한까지 활용
- WebGL 수준의 성능은 아니지만, **CSS 표현력의 확장 가능성**을 입증
- Safari·Chrome의 3D 관련 버그와 성능 이슈 다수 발견
- 최종 결론: “CSS로 DOOM을 실행할 수 있는가?”
  → **가능하다. Yes, it can.**

## Comments



### Comment 54083

- Author: neo
- Created: 2026-03-29T23:33:06+09:00
- Points: 1

###### [Hacker News 의견들](https://news.ycombinator.com/item?id=47557960) 
- “이걸 DOOM으로 돌려봤다”류의 사람들은 정부의 **우주 추진 시스템 부서**에 고용되어야 한다고 생각함  
  그들은 손가락만 굴리기엔 너무 **이색적인 과제**가 필요한 사람들임  
  - 하지만 결국 그들이 만드는 추진 시스템조차 DOOM을 돌릴 수 있게 만들 것 같음  

- 이건 “할 수 있으니까 해본다”식의 프로젝트 같음  
  CSS가 원래는 선언적 스타일링 언어였는데, 이제는 조건문, 수학 함수, 렌더링 트릭까지 생기면서 점점 **프로그래밍 가능한 시스템**으로 변하고 있음  
  중요한 건 “CSS로 DOOM을 돌릴 수 있나”가 아니라, 원래 그런 용도가 아니었던 **레이어에 얼마나 많은 로직을 밀어넣고 있는가**임  
  - 이건 전형적인 **추상화 역전(abstraction inversion)** 의 사례임  
    CSS는 프로그래밍 언어가 되려는 욕망을 숨기고 있지만, 결국 완전히 잘못된 추상화로 변해버렸음  
  - 핵심은 **표현(CSS)** 과 **상호작용(JavaScript)** 의 경계가 어디까지인가임  
    예전엔 드롭다운, 툴팁, 레이아웃을 위해 JS가 필요했지만, 이제는 CSS 속성으로 앵커 위치나 조건(if())까지 지정할 수 있음  
    애니메이션, 디테일 토글, 접근성 관련 효과까지 CSS로 처리 가능해짐  

- CSS로 3D 장면을 만드는 건 예전부터 가능했지만, 상호작용엔 JS가 필요했음  
  이제는 [x86CSS 프로젝트](https://lyra.horse/x86css/)처럼 JS 없이도 CPU를 **CSS만으로 에뮬레이션**할 수 있음  
  그래서 DOOM도 순수 CSS로 실시간 구현이 가능할지 궁금함  
  - 하지만 CSS x86 CPU는 너무 느려서 게임 루프를 처리할 수 없다고 함. 결국 JS가 필요함  
  - 이런 CSS의 진화는 **예견된 결과**였고, HTML 진영이 애초에 DSSSL을 채택했어야 했다고 생각함  

- 이 사례는 사람들이 왜 **TypeScript 기반 CSS**를 원하게 되는지를 잘 보여줌  
  Chrome에서만 작동하는 if() 같은 기능 때문에, 개발자들은 이런 꼼수를 씀  
  예를 들어, `animation-delay`와 `@keyframes`를 이용해 **가시성 토글**을 흉내내는 트릭을 쓰는 식임  
  CSS if()가 표준화되면 이런 해킹 없이 깔끔하게 조건 처리가 가능해질 것임  

- DOOM 치트 코드인 **IDDQD**와 **IDKFA**는 아쉽게도 작동하지 않았음  

- 예전에 div에 둥근 모서리를 만들려면 **GIF 네 장**이 필요했던 시절이 떠오름  
  - div라니, 그 전엔 전부 **table 레이아웃**으로 하던 시절도 있었음  

- 정말 인상적임! div 하나만 지워도 **벽 통과(wall hack)** 가 가능함  
  - 더 나아가 `.wall`에 `opacity: 0.7`만 주면, 예전식 **투명 벽핵** 느낌을 그대로 재현할 수 있음  

- “이걸 어디서 직접 돌려볼 수 있나?” 싶었는데, [cssdoom.wtf](https://cssdoom.wtf/)에서 가능함  
  - 휴대폰으로 실행하자마자 **기기가 뜨거워짐**  
  - 모바일에서 DOOM을 이렇게 부드럽게 돌려본 건 처음임  
  - Safari에서도 완벽히 작동함 — 이런 일은 거의 없음  
  - Firefox에서는 잘 돌아가지만, **Alt 키 매핑**이 메뉴를 열고 닫아서 불편했음  
    Chromium에서는 오히려 더 버벅였고, **strafing 키**는 찾지 못했음  
    그래도 전반적으로 놀라운 구현임  

- CSS는 **위원회 설계의 한계**를 보여주는 대표적인 명세임  
  SVG와 함께 “가장 보기 흉한 스펙” 경쟁을 벌이고 있음  
  - 혹시 제목만 보고 댓글 단 거 아니냐는 반응도 있었음  

- 이 멋진 구현에 대해 한 가지 덧붙이자면,  
  실제로는 **플레이어가 움직이는 게 아니라 세상이 움직이는 것**임  
  카메라는 단지 시야각(frustrum)을 계산하기 위한 개념적 도구일 뿐임
