모래언덕 바람결
사막 언덕에 바람이 불면 모래 물결무늬가 저절로 생겼다가 흘러간다.
프롬프트를 복사해 claude.ai 에 붙여 넣으면 바로 실행됩니다 · Opus 5.5 · Effort High 기준
- 권장 모델
- Claude Opus 5.5 · Effort High
- 화면비
- 16:9 (가로)
- 조작
- 화면을 쓸어 바람 방향·세기 정하기 · 두 손가락으로 확대
- 결과물
- HTML 파일 하나 · three.js 를 인터넷에서 불러옵니다
프롬프트
Claude 가 읽는 제작 지시문입니다. 맨 위 [바꿀 곳]의 값을 바꿔 붙여 넣어도 됩니다.
낮은 해가 비치는 사막 모래언덕 위에 바람이 불면 모래 물결무늬가 저절로 생겨나 흘러가는 3D 장면을 HTML 파일 하나로 만들어 줘.
핵심 아이디어: 모래언덕 지형은 「큰 언덕(고정된 바탕 높이)」 + 「물결 층(시뮬레이션되는 얇은 높이맵)」 두 겹이다. 물결 층은 단순한 도약 규칙(높은 칸의 모래일수록 바람 따라 더 멀리 튄다 + 약한 굴러내림)만으로 물결이 저절로 자라고, 바람 방향이 바뀌면 옛 물결이 지워지고 새 방향의 물결이 돋는다. 해는 지평선 위 12°에 낮게 떠 있어 언덕 그림자가 비스듬히 길게 눕고, 물결 하나하나의 바람 반대쪽 비탈이 어두운 줄무늬가 된다. 카메라는 언덕 위 높은 곳에서 비스듬히 내려다보아, 화면 가득 물결 깔린 모래밭과 대각선으로 놓인 언덕 2~3줄이 보인다.
[바꿀 곳]
- windAngleStart = -0.45 (라디안, 처음 바람 방향. x-z 평면에서 +x 축으로부터 +z 쪽으로 잰 각)
- windAngleMain = 0.50 (2초에 바뀌어 갈 바람 방향 — 해의 빛이 나아가는 방향과 같다)
- windStrengthStart = 0.7, windStrengthMain = 1.0 (세기 s, 범위 0.3~1.5)
- sunElevationDeg = 12, sunAzimuthDeg = 29 (빛이 나아가는 방향의 각, 바람 각과 같은 기준)
- gridN = 208 (높이맵 한 변 칸 수 = 지형 메시 한 변 정점 수)
- worldSize = 120 (지형 한 변, 월드 단위. 칸 간격 dx = 120 / 208 ≈ 0.577)
- duneHeight = 3.0, dunePeriod = 50, duneAngle = 0.50 (큰 언덕의 단면 방향 — 마루는 이 방향에 수직)
- hopBase = 5 (도약 기본 거리, 칸), hopGain = 20 (높이 1 월드 단위당 늘어나는 도약 칸)
- liftPerStep = 0.003 (s = 1 일 때 한 걸음에 칸마다 들리는 모래 높이)
- creep = 0.02 (굴러내림 확산 계수)
- simHz = 30 (초당 시뮬레이션 걸음)
- sandColor = #d8ab78, crestColor = #e6be8a, troughColor = #c7955f
- sunColor = #ffd9b0, sunIntensity = 3.0, skyColor = #b4c3dc, skyIntensity = 1.2
- fogColor = #e3cdb0
[크기]
- 화면 짧은 변을 S 라 한다. 캔버스는 창 전체다. 21:9·16:10 가로 화면과 폰 세로 화면(390×844) 모두에서 아래 숫자가 맞아야 한다.
- 가로 화면 첫 화면: 또렷한 모래 지형이 화면 아래 약 70%를 가득 채우고, 위 약 30%는 안개에 녹은 먼 모래와 하늘이다. 지형의 끝(모서리·잘린 면)은 어느 시점에서도 보이지 않는다.
- 세로 화면 첫 화면: 또렷한 모래가 아래 약 65%, 위 약 35% 는 안개와 하늘. 카메라를 더 높이 올려 더 내려다본다. 가로 시야가 약 39° 라 물결이 화면 폭에 8~9줄 보인다. 그늘진 비탈 한 면이 화면 절반을 넘게 차지하면 실패다.
- 물결 한 파장은 약 6.8칸 ≈ 3.9 월드. 바라보는 점 깊이에서 화면 높이의 약 0.12배(가로 화면). 화면 폭에 물결 줄무늬가 21:9 에서 약 20줄, 16:10 에서 약 14줄. 가까운 아래쪽은 굵고 먼 쪽은 촘촘하다.
- 큰 언덕: 높이 3.0, 마루 간격 50. 첫 화면에서 마루가 화면 왼쪽 아래에서 오른쪽 위로 비스듬히 2~3줄 지나간다. 그림자는 마루에서 화면 오른쪽 아래(카메라 쪽)로 3.0 / tan 12° ≈ 14 길이로 눕는다 — 언덕 한 주기의 약 30%가 그늘, 70%는 해를 받는 물결 밭이다.
- 안내 글 한 줄: 화면 아래 가운데(아래에서 0.06S), 글자 크기 0.024S, 색 #fff8ee, text-shadow 0 0 6px rgba(60,40,20,0.6) 로 「쓸어서 바람 · 두 손가락으로 확대」. 0초부터 보이고 4초에서 5초 사이에 사라진다. 그 밖의 글자는 없다.
[장면]
지형 높이 y = base(x, z) + mask(x, z) × ripple(x, z).
큰 언덕 base: duneAngle 의 단위 벡터 dD = (cos 0.50, sin 0.50) 로 u = p·dD + 5·sin(0.05·(p·dD⊥)) 를 잡고(⊥ 는 수직 방향, p 는 땅 위 위치 (x, z)), 주기 dunePeriod 의 비대칭 톱니를 부드럽게 쓴다 — 한 주기 중 75% 는 완만히 오르고(바람 맞는 쪽, 기울기 약 0.08) 25% 는 내려간다(미끄럼면, 기울기 약 0.24). 꼭짓점은 반경 2 정도로 둥글게. 높이 0~duneHeight 에 시드 고정 값 노이즈(주기 35) ±1.0 을 더한다. 마루는 dD 에 수직이므로 해의 빛과 바람(windAngleMain)에 수직으로 놓인다.
가장자리 처리: 지형 중심에서 거리 r 이 50~58 이면 base 와 물결을 smoothstep 으로 0 까지 낮추고, 58 밖은 높이 0 의 평평한 모래다. 지형 끝에 언덕 단면이 서지 않게 하기 위해서다. base 는 시작할 때 한 번 계산해 배열에 둔다.
mask: base 기울기 크기가 0.20 이상이면 줄고 0.30 이상이면 0.65 가 된다(smoothstep, 최솟값 0.65). 미끄럼면의 물결은 조금 옅어질 뿐 사라지지 않는다 — 시점을 돌려 큰 비탈을 마주해도 물결이 읽혀야 한다.
물결 층 ripple: gridN × gridN 의 Float32Array h (월드 단위 높이, 평균 0). 가장자리는 서로 이어진다(주기 경계). 칸 간격 dx ≈ 0.577. 한 걸음(1/simHz 초)의 규칙:
1. 바람 단위 벡터 w(칸 좌표), 세기 s, q = liftPerStep × s.
2. 모든 칸 k 에서 q 를 빼고, 도약 거리 L = clamp(hopBase + hopGain × h[k], 1.5, 2 × hopBase) 칸으로 정한다(높은 칸의 모래가 더 멀리 튄다 — 물결이 자라는 원리).
3. 뺀 q 를 바람 따라 (L−1), L, (L+1) 칸 떨어진 세 점에 0.25 : 0.5 : 0.25 로 나눠 쌓는다. 각 점은 쌍선형으로 네 칸에 나눈다. 이 세 갈래 나눔이 2~3칸짜리 잔물결을 지운다 — 빼지 않는다.
4. 굴러내림: h += creep × (네 이웃 합 − 4h).
5. 들썩임: 한 걸음에 시드 난수로 고른 200 칸에 +0.004, 그 오른쪽 이웃에 −0.004 (총량 보존). 새 방향 물결의 씨앗이다.
6. h 를 ±0.22 로 자른다.
선형 계산으로 이 규칙에서 가장 빨리 자라는 파장은 약 6.8칸(≈ 3.9 월드), 성장률은 한 걸음에 약 0.028(1초에 약 2.3배)이다. 물결 마루는 바람에 수직으로 선다.
흐름: 물결 무늬 전체를 바람 방향으로 초당 0.9 × s 칸 밀어 준다. 누적 오프셋이 한 칸을 넘으면 배열을 정수 칸 굴리고(주기 경계라 손실 없음), 남은 소수 부분은 그릴 때 쌍선형 표본으로 메워 매끄럽게 움직인다. 물결 한 파장이 약 7.5초에 한 번 지나간다.
처음 상태(첫 프레임부터 물결이 보이도록): h = 0.08 × sin(2π (p·wStart) / 6.8칸 + 1.2 × 값노이즈(주기 40칸)) + 0.01 × 노이즈. 즉 첫 화면부터 처음 바람 방향의 물결이 지형 전체에 깔려 있다.
시간표:
- 0~2초: 바람 windAngleStart, 세기 0.7. 물결은 해의 빛과 54° 비껴 서 있어 줄무늬가 은은하다.
- 2.0~3.5초: 바람이 windAngleMain 으로 돌고 세기 1.0 이 된다(easeInOutCubic).
- 3.5~6초: 옛 물결이 지워진다. 6~12초: 해의 빛과 같은 방향의 바람이 새 물결을 세운다 — 마루가 빛에 수직이라 바람 반대쪽 비탈마다 어두운 줄이 서서 화면 전체가 밝고 어두운 줄무늬로 바뀐다. 이것이 시그니처 순간이다: 12초 무렵 화면 폭에 선명한 빛/그림자 줄무늬(21:9 에서 약 20줄, 폰 세로에서 8~9줄)가 큰 언덕 마루와 나란히 비스듬히 서서 화면 오른쪽 아래(바람 쪽)로 천천히 흐른다.
- 그 뒤: 세기는 s × (1 + 0.15 sin(0.4t)) 로 숨 쉰다. 마지막 입력(입력이 없었으면 시작) 뒤 20초가 지나면 30초마다 바람이 windAngleMain 기준 +40° / −40° 를 번갈아 2초에 걸쳐 돈다.
입력:
- 끌기는 시점을 돌린다(카메라 절 참고). 손을 뗄 때 끈 길이가 0.04S 이상이면 그 끌기가 바람도 정한다: 화면 벡터 (mx, my) 를 끌기 시작 때의 yaw 로 땅 위 방향(화면 오른쪽 = 카메라 오른쪽, 화면 위 = 카메라 앞)으로 바꾼 것이 바람 방향, 세기 s = clamp(길이 / 0.35S, 0.3, 1.5). 1.2초에 걸쳐 짧은 쪽으로 돈다.
- 두 손가락 벌리기/오므리기, 마우스 휠 = 확대(범위는 카메라 절). 두 손가락일 때는 회전하지 않고 바람도 바꾸지 않는다.
날리는 모래알: 점 500개(THREE.Points, 크기 0.05 월드, 색 #f0d3a8, 불투명도 0.55 × min(s,1), depthWrite false). 카메라가 바라보는 점 반경 30 안의 지면에서 바람 따라 길이 1.6 × s, 높이 0.25 의 포물선으로 한 번 튀고, 떨어지면 시드 난수로 새 자리에서 다시 시작한다. 속도 4 × s 월드/초. 바람이 보이게 하는 정도만 — 더 늘리지 않는다.
하늘: 반지름 400 의 구(BackSide, fog 없음, 정점색). 지평선과 그 아래 전부는 fogColor 와 똑같은 색, 위로 갈수록 천정 #9fb0c0 까지, 해 쪽 지평선 위는 #f3dcb8 로 조금 밝게. 해 원반·광륜은 그리지 않는다.
안개: new THREE.Fog(CONFIG.fogColor 의 hex 그대로, near, far), near = R − 5, far = R + 35 (R 은 지금 카메라 거리, 매 프레임 맞춘다). 안개 색은 선형으로 바꾸지 않는다 — r128 은 안개를 출력(sRGB) 변환 뒤에 섞으므로, 선형으로 바꿔 넣으면 안개에 묻힌 땅이 #c49c6e 쪽으로 어두워져 하늘과 곧은 경계선을 만든다. 줌 범위 안에서 지형 끝(중심에서 60)은 시야 안 어디서든 카메라에서 약 R + 37 보다 멀어 안개에 완전히 묻히고, 땅과 하늘이 같은 #e3cdb0 으로 이어진다.
먼 테두리 색: 지형 중심에서 r 이 52~58 인 정점은 빛 계산을 마친 색을 lin(fogColor) 쪽으로 smoothstep 으로 섞고, 58 밖 정점은 정확히 lin(fogColor) 다. 안개가 덜 덮인 시점에서도 네모난 지형 끝과 평평한 테두리가 하늘색과 같아 드러나지 않게 하기 위해서다.
[카메라]
- 화면 비율 a = 가로/세로, k = clamp((1 − a) / 0.5, 0, 1) — 가로 화면은 0, 폰 세로(a ≈ 0.46)는 1. 창 크기가 바뀔 때마다 다시 구한다.
- PerspectiveCamera 세로 시야 = 45° + 30°·k, near 0.5, far 900. 가로 시야는 21:9 에서 약 88°, 16:10 에서 약 67°, 폰 세로에서 약 39° 다. 세로 화면에서 세로 시야 45° 를 그대로 두지 않는다 — 가로 시야가 20° 아래로 좁아져 비탈 한 면만 보인다.
- 기본 거리 R0 = 40 × (1 + 0.15k), 기본 기울기 P0 = 0.36 + 0.12k (라디안).
- 바라보는 점 (0, 1, 0). 위치 = 바라보는 점 + R × (sin yaw · cos pitch, sin pitch, cos yaw · cos pitch).
- 첫 화면: yaw = 0.28, pitch = P0, R = R0. 가로 화면에서 카메라 높이 약 15, 땅 위 앞 방향 (−0.28, −0.96). 해는 앞 왼쪽 45° 먼 곳에 있고, 언덕 마루는 왼쪽 아래→오른쪽 위로 비스듬하며, 그림자는 오른쪽 아래(카메라 쪽)로 눕는다.
- 카메라 아래 지형 높이 + 4 보다 낮아지지 않게 위치 y 를 올린다.
- 입력 없을 때: yaw 가 초당 0.035 라디안씩 돈다(한 바퀴 약 3분). pitch = P0 + 0.04 sin(0.21t).
- 끌기: 가로 이동 S 당 yaw 2.0 라디안(제한 없음), 세로 이동 S 당 pitch 2.0 라디안, pitch 범위 0.20~0.85.
- 확대: 두 손가락 벌리기/오므리기와 마우스 휠로 R 을 0.6 R0 ~ 1.15 R0 사이에서 바꾼다.
- 손을 떼고 3초 뒤, 자동 움직임으로 1.5초에 걸쳐 부드럽게 돌아간다(그 자리의 yaw 에서 이어서 돈다 — 튀지 않는다).
[빛]
- renderer: antialias true, outputEncoding = THREE.sRGBEncoding, toneMapping = NoToneMapping. 그림자 맵과 빛 객체(DirectionalLight 등)는 쓰지 않는다 — 해가 낮으면 그림자 맵 한 칸이 땅 위에서 4~5배로 늘어나 그림자 경계가 계단처럼 깨지기 때문이다. 대신 빛과 그림자를 정점마다 직접 계산한다.
- 지형 재질: MeshBasicMaterial({ vertexColors: true, fog: true }). 색 속성에 아래 식으로 계산한 선형 색을 매 프레임 쓴다. hex 색은 THREE.Color 로 만든 뒤 convertSRGBToLinear() 로 선형으로 바꿔 계산한다.
- 색 공간 규칙(r128, outputEncoding = sRGB): 정점색·재질 color(하늘 구 정점색, 모래알 PointsMaterial 색 포함)는 선형으로 바꿔 넣어야 화면에 hex 그대로 나온다. THREE.Fog 색과 renderer 바탕색(setClearColor)은 hex 그대로 넣는다. 그래야 하늘 구 지평선·안개·바탕이 모두 화면에서 #e3cdb0 한 색이 된다.
- 해를 향한 방향 L = (−cos12°·cos29°, sin12°, −cos12°·sin29°) ≈ (−0.856, 0.208, −0.474). 세계에 고정 — 카메라와 함께 돌지 않는다.
- 하늘빛 방향 F = 정규화((−0.613, 1, −0.340)) ≈ (−0.502, 0.819, −0.278) (위쪽에서 해 쪽으로 기운 방향).
- 정점 색 = albedo × ( sunColor × sunIntensity × max(0, N·L) × sh + skyColor × skyIntensity × (0.45 + 0.55 × max(0, N·F)) ), 채널마다 1 로 자른다.
- 검산: 평평한 양지 ≈ sRGB #e3ad7d(밝은 모래), 평평한 그늘 ≈ #9e876b(회갈색, 양지의 약 45% 밝기). 그늘이 진갈색(#6a4a2a 쪽)으로 가라앉으면 틀린 것이다. 그늘 안에서도 하늘빛 항이 법선을 따라 바뀌어 물결 줄무늬가 읽힌다.
- albedo: 물결 높이로 골 troughColor → sandColor(h = 0) → 마루 crestColor 를 섞고(h = ±0.12 에서 양 끝), 큰 언덕 위아래로 밝기 ±6% 값노이즈.
- 그림자 sh (0~1, 1 = 양지):
- 큰 언덕 그림자 shBase: 시작할 때 한 번. 정점마다 L 의 땅 위 방향으로 0.5칸씩 60걸음 나아가며 base 를 쌍선형 표본하고, 넘친 높이 e = max(표본 − (출발 높이 + 나아간 거리 × tan12° (= 0.2126))). shBase = 1 − smoothstep(0, 0.35, e).
- 물결 그림자 shRip: 매 프레임. 같은 방향으로 1칸씩 5걸음, 전체 높이(가장 가까운 칸)로 같은 계산, shRip = 1 − smoothstep(0, 0.06, e).
- sh = min(shBase, shRip). 정점 사이는 보간되어 그림자 경계가 부드럽다.
- 법선 N 은 매 프레임 높이 배열의 중앙 차분으로 직접 계산한다(computeVertexNormals 를 부르지 않는다).
[구현]
- HTML 파일 하나로 만든다. 라이브러리는 three.js 한 줄만 쓴다: <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script> — 이 r128 판에 없는 것(모듈 import, OrbitControls 같은 확장 파일, CapsuleGeometry, outputColorSpace)은 쓰지 않는다. 그 밖의 바깥 파일·글꼴·네트워크 요청은 쓰지 않는다.
- three.js 다음 <script> 맨 앞에 CONFIG 객체를 두고 [바꿀 곳]의 값과 이 글에 나온 숫자(크기·색·빛·속도)를 이름 붙여 모은다.
- 장면은 requestAnimationFrame 의 타임스탬프로 구한 시간 t(초)로 진행한다. 한 프레임에 밀린 시간은 0.1초까지만 반영한다. setTimeout·setInterval·CSS 애니메이션으로 움직이지 않는다.
- 난수는 시드 고정 난수(mulberry32 등)만 쓴다. Math.random 은 쓰지 않는다.
- 텍스처는 코드로 만든다(CanvasTexture·DataTexture). 바깥 그림 파일을 쓰지 않는다.
- 캔버스는 늘 창 전체 크기다. 창 크기가 바뀌면 카메라 비율과 렌더러 크기를 다시 맞춘다. 스크롤이 생기지 않게 한다. renderer.setPixelRatio 는 2 까지만.
- 입력이 없으면 카메라가 스스로 천천히 움직인다. 마우스로 끌거나 손가락으로 끌면 시점이 돈다(가로 = 좌우 회전, 세로 = 위아래 기울기, 범위 제한). 손을 떼고 3초 뒤 자동 움직임으로 부드럽게 돌아간다. 터치는 페이지 스크롤·확대가 일어나지 않게 막는다(touch-action: none).
- 시점 각도를 window.__view = { yaw, pitch } (라디안)에 두고 매 프레임 갱신한다. 카메라는 이 두 각도로 놓는다 — 자동 움직임도 끌기도 이 각도를 바꾼다.
- 브라우저 저장소(localStorage·sessionStorage·IndexedDB·쿠키)를 쓰지 않는다 — 미리보기 칸에서 막혀 오류가 난다.
- 탭이 숨으면 멈추고 돌아오면 이어서 한다.
- WebGL 을 못 쓰는 기기에서는 오류 대신 화면에 짧은 안내 문구를 띄운다.
- 글자를 쓰면 시스템 글꼴만: 'Pretendard', 'Apple SD Gothic Neo', 'Malgun Gothic', 'Noto Sans KR', sans-serif.
- 모든 함수·상수를 먼저 정의하고, 시작은 스크립트 맨 끝에서 한 번만 부른다.
- 콘솔에 오류가 나지 않게 한다. 스스로 대조하는 표는 console.table, 어긋남은 console.warn 으로만 낸다.
이 작품만의 구현:
- 지형은 PlaneGeometry(worldSize, worldSize, gridN−1, gridN−1) 를 눕힌 메시 하나(삼각형 약 8만 6천). 정점 (i, j) 가 높이맵 칸 (i, j) 와 같다. position·color 속성을 매 프레임 덮어쓰고 needsUpdate 를 켠다(법선은 빛 계산에만 쓰므로 Float32Array 로 따로 둔다).
- 문서 구조: <title>모래언덕 바람결</title>. canvas 를 먼저 body 에 붙이고 안내 글 #hint 는 그 뒤에 둔다. CSS 로 canvas 는 position: fixed; inset: 0; z-index: 0, #hint 는 position: fixed; z-index: 2; pointer-events: none. 두 요소 모두 fixed 인데 z-index 가 없으면 나중에 붙은 canvas 가 안내 글을 덮는다.
- 시뮬레이션은 고정 걸음: 누적 시간으로 1/simHz 씩 돌리되 한 프레임에 최대 3걸음. 쌓기용 두 번째 Float32Array 를 미리 만들어 두고 번갈아 쓴다(매 걸음 새 배열을 만들지 않는다).
- 그릴 때 정점 높이 = base[k] + mask[k] × ripple(i − fx, j − fz), fx·fz 는 흐름 오프셋의 소수 부분, ripple 은 주기 경계 쌍선형 표본.
- 바람 방향 각은 짧은 쪽으로 보간한다(각도 차를 −π~π 로 접는다).
- 성능: 처음 3초의 평균 프레임 시간이 28ms 를 넘으면 console.warn 과 함께 simHz 를 20 으로 낮춘다. gridN 은 바꾸지 않는다.
- window.__wind = { angle, strength } 와 window.__ripple = { rms, align } 를 매 초 갱신한다. rms = h 의 제곱평균제곱근, align = 바람 방향 기울기 제곱 평균 / (바람 방향 + 그 수직 방향 기울기 제곱 평균). 마루가 바람에 수직이면 align 이 1 에 가깝다.
- 합격 기준 — 시작할 때와 12초에 console.table 로 대조하고, 어긋나면 console.warn:
- 지형 삼각형 수 ≤ 300,000 (기대 약 85,700), 그림자 맵 0장.
- 카메라: 세로 시야(가로 화면 45°, 폰 세로 75°), R0, P0 가 위 식대로.
- 시작 때 그늘(shBase < 0.5) 정점 비율이 15~40% (기대 약 30%).
- 0초: rms 0.04~0.08, 지금 바람(windAngleStart) 기준 align ≥ 0.8, windAngleMain 기준으로 따로 잰 align 약 0.33 (두 바람이 54° 벌어져 있어 cos²54°).
- 12초: rms 0.04~0.16, align ≥ 0.65, 바람 각 = windAngleMain(입력이 없었다면).
- 12초 rms 가 0.03 미만이면 최후에만, console.warn 과 함께 liftPerStep 을 한 번 두 배로 올린다. 다른 자동 보정은 하지 않는다.
- 결과 HTML 은 100KB 아래로, 코드는 짧고 곧게.
[하지 말 것]
- 물결을 사인 무늬 텍스처나 노멀맵으로 그려 넣지 않는다 — 물결은 높이맵 규칙에서 자라나야 바람이 바뀔 때 다시 짜인다(처음 상태 한 번만 예외).
- gridN 을 올리거나 지형을 여러 장으로 쪼개지 않는다 — 폰에서 시뮬레이션과 정점 갱신이 버거워진다.
- 후처리(블룸·피사계 심도·EffectComposer)를 쓰지 않는다 — 성능 예산 밖이고 r128 기본 파일에 없다.
- 렌즈 플레어·해 원반·빛줄기를 그리지 않는다 — 낮은 해의 힘은 긴 그림자로 보인다.
- 그림자 맵·DirectionalLight·HemisphereLight·MeshStandardMaterial 을 쓰지 않는다 — 낮은 해에서 그림자 맵이 계단 무늬를 내고, 빛을 정점에서 직접 셈해야 그늘 색이 숫자대로 나온다.
- 카메라를 첫 화면에서 언덕 비탈 가까이 낮게 두지 않는다 — 비탈 한 면이 화면을 덮으면 물결 밭이 사라진다.
- 하늘 구의 지평선 아래를 fogColor 와 다른 색으로 칠하지 않고, 안개 색을 선형 변환하지 않는다 — 둘 중 하나라도 어긋나면 지형 끝이 진한 띠로 드러난다.
- 모래 폭풍 입자·먼지 구름·반짝이를 더하지 않는다 — 점 500개 외의 입자는 줄무늬를 가린다.
- 하늘·안개에 남청색이나 채도 높은 색을 쓰지 않는다 — 사막의 흐린 빛이 깨진다.
- 선인장·낙타·사람·새·건물·발자국을 넣지 않는다 — 모래와 빛만의 장면이다.
- 화면 모서리 라벨·수치 표시·바람 화살표·버튼을 띄우지 않는다 — 안내 한 줄만, 5초에 사라진다.
- 발광 재질(emissive)을 쓰지 않는다 — 모래는 해에서만 빛을 받는다.
- 바깥 글꼴·그림·소리 파일을 불러오지 않는다.
사용 방법
필요한 것: claude.ai 계정 — 무료 계정에서도 Claude Opus 5.5 를 고를 수 있습니다
- claude.ai 에서 새 대화를 엽니다.
- 모델 메뉴에서 Claude Opus 5.5 를 고르고, 같은 메뉴의 Effort(답변 깊이)를 High 로 둡니다.
- 복사한 프롬프트를 붙여 넣고 전송합니다. 결과로 나온 HTML 파일을 브라우저로 열면 바로 해 볼 수 있습니다.
추가 요청 예시
결과가 나온 뒤 같은 대화에서 이어서 요청하면 해당 부분만 수정됩니다.
- 해가 지고 달이 뜨는 밤 사막으로 바꿔 줘
- 언덕 위에 낙타 한 마리가 천천히 걸어가게 해 줘
- 바람이 세지면 모래가 날리는 먼지를 더해 줘
3D