
네덜란드의 한 프로그래머가 현대적인 스타일링 기능을 본래의 용도에서 훨씬 벗어난 수준으로 밀어붙였습니다.
요약: 현대적인 CSS는 지난 몇 년 동안 몇몇 특이한 영역으로 확장되어 왔지만, 오직 HTML 요소와 스타일 규칙만으로 렌더링되어 완벽하게 플레이 가능한 버전의 둠(Doom)만큼 이를 극단적으로 밀어붙인 실험은 거의 없었습니다. 최근의 한 프로젝트에서 네덜란드 개발자 닐스 린허(Niels Leenheer)는 모든 벽, 바닥, 드럼통, 그리고 임프(imp)가 3D 공간에서 변형된 div가 되도록 이 클래식 슈팅 게임의 비주얼을 재구축했으며, 자바스크립트는 게임 루프를 처리하고 CSS는 모든 렌더링을 도맡아 수행합니다.
린허는 WhichBrowser 사용자 에이전트 파서인 HTML5test.com을 만든 것으로 가장 잘 알려져 있습니다. 그는 오실로스코프에서 게임을 실행했던 이전 실험을 마친 후 CSS 기반의 둠 렌더러를 탐색하기 시작했으며, 오리지널 WAD 파일에서 맵 데이터를 추출하기 위해 코드를 재사용했습니다.
그의 최신 실험은 해당 작업을 직접적으로 기반으로 삼고 있습니다. 추출된 데이터는 수천 개의 div 요소로 구성된 정적 장면으로 컴파일되며, 각 요소에는 가공되지 않은 둠의 좌표가 주석으로 달려 있습니다. 그런 다음 브라우저의 레이아웃 및 합성 시스템은 CSS 변형(transforms)과 hypot(), atan2() 같은 수학 함수를 사용하여 이들을 위치가 지정된 3D 객체로 렌더링합니다. 자바스크립트가 전체 3D 기하학을 계산하도록 하는 대신, 스크립트는 바닥 및 천장 높이와 함께 원본 좌표 쌍을 커스텀 속성으로 전달하여 CSS 엔진이 벽의 너비와 회전을 위한 삼각법을 수행하도록 남겨둡니다.

이러한 분리는 의도된 것입니다. 자바스크립트는 둠의 오픈 소스 C 코드에서 파생된 게임 루프를 실행하는 반면, 얇은 렌더링 레이어는 새로운 좌표, 상태 속성 및 소수의 커스텀 속성만을 CSS에 노출합니다. 게임 상태와 로직을 전적으로 CSS에 유지하려는 시도는 비현실적이어서 포기되었지만, 렌더링 경로는 거의 전적으로 스타일링 측면에 남아 있습니다.
프로젝트의 많은 부분은 둠의 좌표계 및 렌더링 트릭을 브라우저가 3D를 처리하는 방식과 일치시키는 것으로 귀결됩니다. 바닥은 X축을 중심으로 90도 회전된 div 요소로 모델링된 다음, 복잡한 윤곽선과 홀짝 채우기 규칙을 지원하기 위해 polygon()이 포함된 clip-path 또는 최신의 shape() 구문을 사용하여 임의의 섹터 다각형으로 잘려 나갑니다.
구역 전반에 걸친 텍스처 타일링은 배경 위치를 "월드" 좌표에 고정함으로써 이음새 없이 유지됩니다. 예를 들어, 어떤 요소가 가로로 200px, 세로로 400px 오프셋되어 있는 경우, background-position은 -200px -400px로 설정되어 마치 연속된 평면 전체에 그려진 것처럼 패턴이 정렬됩니다.

움직임은 CSS에 존재하지 않는 카메라를 사용하는 대신 월드를 이동시키는 방식으로 작동합니다. 자바스크립트는 네 개의 커스텀 속성에서 플레이어의 위치와 각도를 추적하고, CSS는 해당 값들을 반전시켜 장면을 반대 방향으로 이동시키고, 추가적인 translate로 원근감을 보정하며 나머지는 브라우저에 맡깁니다. 적과 발사체를 위한 스프라이트 처리는 빌보드(billboard) 기술에 의존합니다. CSS는 각 스프라이트가 감상자를 향하도록 회전시키고, 필요할 때 scaleX로 프레임을 대칭 이동하며, step() 애니메이션으로 스프라이트 시트를 전진시키는 반면 자바스크립트는 상태 속성을 업데이트합니다.
조명, 문, 리프트 및 발사체는 모두 상태를 가지는 스타일 문제로 처리됩니다. 섹터의 조명 수준은 filter: brightness()와 함께 사용되는 계단식 커스텀 속성으로 저장되므로, 어두운 섹터 내부의 모든 것은 각 요소를 개별적으로 조정하지 않고도 어두워집니다. 문을 여는 것은 커스텀 속성에 대한 CSS 트랜지션을 통해 컨테이너를 위쪽으로 이동시킵니다. 게임 루프는 데이터 속성만 토글하고 애니메이션은 브라우저에 맡깁니다. 발사체는 시작 및 끝 좌표와 지속 시간을 가지고 생성됩니다. CSS는 translate()를 사용하여 이들을 A 지점에서 B 지점으로 애니메이션화하는 한편, 별도의 rotate() 속성은 이들이 플레이어를 계속 향하도록 유지합니다.
현대적인 레이아웃 기능의 보다 특이한 응용 중 하나는 게임을 반응형으로 만드는 것입니다. DOM 기반의 HUD는 원래의 고정 너비 상태 표시줄에서 탄약, 체력, 얼굴, 방어구 및 열쇠를 위한 별도의 요소로 재구성되었으며, 좁은 화면에서 자연스럽게 줄바꿈이 되도록 Flexbox로 배치되었습니다. 관전자 모드는 CSS 기반의 카메라 로직을 한 층 더 추가합니다. 따라가기 뷰는 플레이어의 각도에 sin()과 cos()를 결합한 CSS calc()를 사용하여 오프셋을 도출함으로써 플레이어의 뒤쪽과 위쪽에 카메라를 배치하고, 별도의 translate() 및 rotate() 속성을 사용하여 1인칭 뷰와 따라가기 뷰 간의 전환이 부드럽게 보간되도록 합니다.

성능 및 정확성 문제는 이 접근 방식의 몇 가지 한계를 노출시켰습니다. 3D로 변형된 수천 개의 요소가 포함된 거대한 맵은 모바일 Safari에서 브라우저 합성기를 끊김 현상으로 몰아넣거나 심지어 충돌을 일으킬 수 있으므로, 린허는 플레이어의 시야 밖에 있는 기하 구조를 숨기기 위해 컬링(culling)을 추가했습니다.
간단한 자바스크립트 구현은 요소들을 훑어보고 거리와 각도에 따라 숨겨진 속성을 토글합니다. 그는 또한 가시성 플래그를 계산한 다음 "타입 그라인딩(type grinding)" 트릭을 사용하는 순수 CSS 변형을 실험하기도 했습니다. 이는 지연 시간이 커스텀 속성을 통해 설정되어 요소를 표시하거나 숨기는 키프레임에 도달하도록 하는 일시 정지된 애니메이션입니다. 둠 오리지널 렌더러의 일부 부분은 단순히 깨끗하게 변환되지 않습니다. 게임은 실제 맵 기하 구조 앞에 놓일 수 있는 "벽"에 사실상 2D로 하늘 텍스처를 그리는 반면, CSS 버전은 그 뒤에 하늘이 그려진 진정한 3D 장면을 투영해야 합니다. 이로 인해 둠이 하늘 뒤에 숨기는 일부 표면이 보이게 되며, 이를 수정하려면 플레이어의 시점에서 하늘 벽 뒤에 위치한 객체를 숨기기 위한 추가적인 컬링 단계가 필요했습니다.
린허는 이 프로젝트가 WebGL 또는 WebGPU 렌더러를 대체하는 것이 아니며 성능이 계속 제한적이라는 점을 분명히 하고 있지만, 이것이 핵심은 아니라고 주장합니다. 그는 "이것은 CSS가 할 수 있는 것의 한계를 밀어붙이는 것에 관한 것"이라고 기록하며, 삼각 함수, @property 애니메이션, clip-path, SVG 필터 및 앵커 포지셔닝을 "사양 작성자들이 아마도 상상조차 하지 못했던 방식으로 사용되는 프로덕션 준비가 완료된 CSS 기능들"로 지목했습니다.
그 결과물은 충분한 div 요소와 현대적인 CSS의 아낌없는 사용을 통해, 그래픽 API에 전혀 의존하지 않고도 브라우저에서 둠의 세계를 재구축할 수 있음을 보여주는 플레이 가능한 데모입니다.
CSS는 사람들이 둠을 실행하도록 속인 비정상적으로 로우테크한 장치들 및 기타 객체들의 늘어나는 목록 중 가장 최신의 것입니다. 클래식 FPS를 실행할 수 있는 다른 것들은 다음과 같습니다:
키보드 키 하나, 고(故) 존 맥아피의 "해킹 불가능한" 암호화폐 지갑, 맥도날드 계산대, 맥도날드 무인 주문 키오스크, 플레이데이트(Playdate) 휴대용 게임기, 마인크래프트, 캡차(captcha), 15달러짜리 이케아 스마트 램프, 마더보드, 레고 브릭, 존 디어 콤바인, 메모장(Notepad), 아타리 ST, 캔디 바, 1밀리와트 신경 칩, 코모도어 64, 텔레텍스트(Teletext), 로봇 잔디깎이, 포트나이트, 자체 생성 AI 모델, 홀로그램, 닌텐도의 알라모(Alarmo) 시계, PDF 문서, 마이크로소프트 워드, 앵커(Anker) 보조배터리, 지구 상공 수백 킬로미터를 도는 인공위성, 윈도우 화면 보호기, 이어버드, 살아있는 인간 뉴런으로 만들어진 컴퓨터, 타입스크립트(TypeScript)의 타입 시스템, 게임이 들어 있는 상자, QR 코드, DNS 텍스트 레코드
※ 퀘이사존 공식 기사가 아닌 해외 뉴스/기사를 번역한 것으로, 퀘이사존 견해와 주관은 포함되어 있지 않습니다. |