vibeui
3D무료셰이더새 카드

밤바다 위의 모놀리스: 셰이더로 그리는 3D 히어로

검은 바다 한가운데 빛나는 정육면체가 떠 있다. 스크롤을 내리면 카메라가 옆에서 정면으로 천천히 다가가며 정육면체가 화면을 채운다. 3D 라이브러리를 하나도 쓰지 않고 캔버스 한 장에 셰이더로 그린다.

마지막 수정

프롬프트 보고 복사하기복사해서 새 대화에 그대로 붙이면 됩니다

이 프롬프트를 실제로 실행해 나온 화면입니다

영상 원본 받기결과물 HTML 받기더블클릭하면 브라우저에서 바로 열립니다
복사해서 새 대화에 그대로

붙일 프롬프트

프롬프트 언어
붙일 도구
결과 스택
Claude Code · HTML 단일 파일
59줄 · 5,397
너는 이 저장소에서 작업하는 프론트엔드 엔지니어다. 아래 지시를 읽고 먼저 계획을 3줄로 말한 뒤 바로 파일을 만들어라. 질문은 정말 막힐 때 한 번만 해라.

# 작업: 밤바다 위의 모놀리스: 셰이더로 그리는 3D 히어로

## 언제 쓰는 작업인가
건축·부동산·인프라처럼 '규모'를 보여 줘야 하는 회사 소개 첫 화면에 쓴다. Three.js 도 Spline 도 붙이지 않고 60줄 남짓한 셰이더로 끝나기 때문에, 외부 파일을 하나도 받지 않고도 화면 전체를 움직이게 할 수 있다.

## 준비물(이미 갖춰져 있다고 가정)
- 새 프로젝트다. Next.js 는 1번 뼈대가 깔려 있고, HTML 은 빈 index.html 에서 시작한다
- 브랜드명과 히어로 문구가 있다: 라벨 한 줄, 대문자 제목 두 줄, 설명 두 문장
- 아래 섹션에 채울 항목이 있다. 프로젝트 3개(제목·한 문단), 숫자 4개, 단계 3개, 인용 한 문장
- WebGL2 를 쓴다. 지원하지 않는 브라우저에서는 캔버스를 배경 그라디언트로 대체한다

## 이 카드에 필요한 것
- WebGL2 셰이더. 패키지를 설치하지 않는다. 캔버스 한 장에 프래그먼트 셰이더로 그린다.

## 건드릴 범위
첫 화면의 셰이더 배경과 그 아래 다섯 섹션(프로젝트 3열·숫자 띠·3단계·인용·마감 CTA)까지다. 폼 제출·로그인·다국어는 만들지 않는다. 라이트 모드도 만들지 않는다. 이 화면은 다크 단일이다.

## 결과가 이렇게 보여야 한다
화면 가운데 위쪽에 한 변이 화면 높이의 8분의 1쯤 되는 정육면체가 떠 있다. 겉면은 포화된 파랑으로 스스로 빛나고, 표면에는 크고 작은 패널 격자가 겹쳐 있어 일정한 간격의 칸마다 밝기가 조금씩 다르다. 좌우에는 산맥 실루엣이 있는데 거의 검고, 정육면체를 향한 사면만 파랗게 물든다. 아래쪽 절반은 잔물결이 이는 검은 바다이고, 정육면체 바로 아래로 긴 빛기둥이 화면 앞까지 늘어진다. 물 위에는 작은 반딧불 같은 입자가 천천히 떠다닌다. 정육면체를 제외하면 화면 전체가 거의 검다.

## 구현 순서
1. 무대를 먼저 잡는다. 바깥 요소에 height 460vh 를 주고, 그 안의 히어로를 position sticky·top 0·height 100svh·overflow hidden 으로 붙인다. 히어로 안에 position absolute 로 inset 0 인 <canvas> 를 깔고, 그 위에 z-index 3 인 내용(라벨·제목·설명·버튼)을 아래쪽 정렬로 놓는다. 캔버스와 내용 사이에는 위아래만 어둡게 하는 그라디언트를 한 겹 깐다: to bottom, rgba(4,7,13,0.62) 0%, 투명 22%, 투명 58%, rgba(4,7,13,0.58) 84%, rgba(4,7,13,0.92) 100%. 무대 안에서 스크롤이 지나가는 동안 장면이 화면에 머문다.
2. WebGL2 컨텍스트를 { antialias: false, alpha: false } 로 얻는다. 정점 셰이더는 화면을 덮는 삼각형 하나만 그리면 되므로 gl_Position = vec4(a, 0.0, 1.0) 한 줄이다. 실제 그림은 전부 프래그먼트 셰이더에서 나온다. uniform 은 vec2 uRes 와 float uTime 두 개뿐이다.
3. 프래그먼트 셰이더 맨 위에 색 상수 두 개를 둔다. const vec3 GLOW = vec3(0.10, 0.74, 1.00) 은 정육면체와 그 빛의 색이고, const vec3 DEEP = vec3(0.004, 0.016, 0.030) 은 빛이 닿지 않는 바닷물이다. 화면의 모든 파란색은 GLOW 에 밝기만 곱해서 만든다. 다른 색상을 새로 만들지 않는다.
4. 노이즈를 먼저 만든다. hash(vec2) 는 sin 과 dot 으로, noise(vec2) 는 hash 네 점을 smoothstep 보간으로, fbm(vec2) 은 noise 를 다섯 번 겹치되 매번 좌표를 회전 행렬 mat2(0.8,0.6,-0.6,0.8) 로 돌리고 2.03 배 키우며 진폭은 절반으로 줄인다. 이 fbm 하나로 산맥·물결·물비늘을 전부 만든다. 그 위에 거리 함수 두 개를 얹는다. 산맥은 ridge(vec2 xz) 로, 가운데 폭 6.4 를 비워 두고(abs(x) - 3.2) 바깥으로 갈수록 높이 2.5 까지 올라가게 한 뒤 fbm 으로 굴곡을 준다. 정육면체는 sdBox 로 반쪽 크기 vec3(0.50, 0.54, 0.50) 에 모서리를 0.02 깎고, 중심을 y = 1.30 에 두되 sin(uTime*0.35) 로 ±0.045 만큼 위아래로 숨 쉬게 한다. 회전은 sin(uTime*0.09) * 0.20 - 0.10 라디안이다. 한 바퀴 돌리지 않고 정면 부근에서만 느리게 흔든다.
5. 카메라를 스크롤에 건다. 진행도를 uScroll 유니폼으로 넘기고, 카메라 위치를 vec3(1.60, 0.72, 7.40) 에서 vec3(0.22, 1.05, 3.55) 로 보간한다. 시작은 옆에서 멀리, 끝은 정면에서 가까이다. 보간에는 smoothstep 을 태워 시작과 끝을 눅인다. 시선도 함께 돌린다. 화면 아래쪽(rd.y < -0.004)은 수면, 위쪽은 하늘·산·정육면체로 갈라서 처리한다. 하늘·산·정육면체는 world(ro, rd, maxT) 함수 하나로 만들어 두고 수면 반사에서 한 번 더 부른다. 레이마칭 반복은 90회, 도달 판정은 0.0012 + 거리*0.0006 으로 거리에 비례시켜 멀리서 스친 광선의 톱니를 줄인다.
6. 수면은 평면 교차로 t 를 바로 구한다. waterH 로 잔물결 높이를 만들고(사인 두 개에 fbm 하나, 진폭 0.012·0.010·0.028), 그 기울기로 법선을 얻어 reflect 한 방향으로 world 를 다시 샘플한다. 프레넬은 0.04 + 0.96 * pow(1 - dot(-rd, n), 4.0) 으로 구해 DEEP 과 반사를 섞되 반사 쪽에 0.34 만 곱한다. 여기에 정육면체 아래로 늘어지는 빛기둥 exp(-abs(x)*2.2) 을 0.85 만큼, 그리고 먼 쪽에만 물비늘을 더한다.
7. 산은 정육면체를 향한 면만 밝힌다. 법선과 정육면체 방향의 내적을 lit 라 할 때 GLOW * lit * fall * 1.5 로 칠하고, 여기에 GLOW * pow(lit, 6.0) * fall * 2.6 을 더해 가장 정면인 부분만 하얗게 뜨게 한다. fall 은 1/(1 + 거리²*0.42) 이다. 기본 바탕색은 vec3(0.006, 0.012, 0.024) 로 거의 검게 둔다.
8. 정육면체 표면은 면마다 밝기를 나눈다. 법선에서 윗면 성분과 옆면 성분을 뽑아 0.55 + 0.30*옆면 + 0.52*윗면 으로 음영을 만들고, 한쪽 옆면을 0.18 만큼 더 눌러 두 면이 다르게 보이게 한다. 같은 밝기로 두면 종이 상자처럼 평평해 보인다. 그 위에 안쪽에서 흐르는 가로 띠 세 겹(sin 으로 각각 다른 속도)과 가는 격자, 모서리에 서는 두께 광을 더한다. 톤매핑은 채널마다 따로 하지 말고 가장 밝은 채널 기준으로 한 번만 눌러라. float mx = max(r, max(g, b)) 로 col *= (mx / (mx + 0.40)) / mx 한 뒤 pow(col, vec3(0.88)) 이다. 채널마다 나누면 파랑이 먼저 눌려 정육면체가 흰 판처럼 바랜다.
9. 성능을 잡는다. 캔버스 해상도를 CSS 크기의 60% 로 잡고(devicePixelRatio 는 1.5 로 상한) 나머지는 CSS 가 늘리게 둔다. IntersectionObserver 로 히어로가 화면 밖으로 나가면 requestAnimationFrame 안에서 그리기를 건너뛴다. prefers-reduced-motion 이면 첫 프레임만 그리고 루프를 멈춘다.
10. 아래 다섯 섹션은 셰이더 없이 CSS 로만 만든다. 프로젝트 3열은 각 카드 안에 transform-style: preserve-3d 로 여섯 면을 세운 정육면체를 놓고 18~24초 주기로 rotateY 360도를 돌린다. 면 간격은 --s 하나에서 calc(var(--s)/2) 로 뽑아 크기를 바꿔도 면이 어긋나지 않게 한다. 숫자 띠는 4열에 세로 구분선, 3단계는 각 칸 상단에 길이 44px 의 발광 선, 마감 CTA 는 아래에서 올라오는 방사형 빛 위에 놓는다. 마지막으로 400px 과 1440px 에서 가로 스크롤이 없는지, 900px 미만에서 3열이 1열로 접히고 좌측 챕터 목록과 상단 링크가 숨는지, 캔버스가 없는 환경에서 히어로가 검은 사각형으로 남지 않는지 보고 빌드(Next.js 는 npm run build)를 통과시킨다.

## 기본값(필요하면 바꿔라)
- 빛 색 GLOW: vec3(0.10, 0.74, 1.00) (바꿀 수 있는 범위: 파랑 계열만. 초록을 0.74 위로 올리면 청록, 0.5 아래로 내리면 남색이 된다)
- 정육면체 크기: vec3(0.50, 0.54, 0.50) (바꿀 수 있는 범위: 0.35 ~ 0.75. 0.8 을 넘으면 산맥 사이를 벗어나 화면을 다 덮는다)
- 톤매핑 세기: 0.40 (바꿀 수 있는 범위: 0.25 ~ 0.85(작을수록 전체가 밝고, 클수록 정육면체만 남는다))
- 산이 물드는 범위: 1/(1 + 거리²*0.42) (바꿀 수 있는 범위: 0.15 ~ 0.9(작을수록 산 전체가 파래진다))
- 무대 높이: 460vh (바꿀 수 있는 범위: 300vh ~ 700vh(길수록 카메라가 천천히 다가간다))

## 하지 말 것
- Three.js·Spline·Babylon 을 설치하지 않는다. 이 장면은 캔버스 하나와 셰이더 한 장으로 끝난다.
- 톤매핑을 col / (col + k) 로 채널마다 따로 하지 않는다. 파랑이 먼저 눌려 정육면체가 흰 판이 된다.
- 레이마칭 반복을 200회 이상으로 올리지 않는다. 90회로 충분하고, 그 위는 모바일에서 화면이 멈춘다.
- 캔버스를 devicePixelRatio 그대로 그리지 않는다. 레티나에서 픽셀 수가 네 배가 되어 프레임이 무너진다.
- 산·물·하늘에 GLOW 말고 다른 색을 섞지 않는다. 색이 두 가지가 되는 순간 밤바다 느낌이 깨진다.
- 히어로 제목을 정육면체와 같은 높이에 두지 않는다. 겹치면 둘 다 읽히지 않는다.

## 출력 규칙 (HTML 단일 파일)
- 결과는 index.html 파일 하나로 저장한다. 외부 CDN 을 쓰지 않고 CSS·JS 를 파일 안에 넣는다.
- HTML 파일 하나로 완결한다. 별도 CSS·JS 파일을 만들지 않는다.
- 폰트는 시스템 폰트 스택을 기본으로 쓰고, 한글은 'Pretendard Variable', 'Apple SD Gothic Neo', 'Malgun Gothic' 순으로 폴백한다.
- 색·간격·둥글기는 :root 의 CSS 변수로 모아 두고, 본문에서는 변수만 참조한다.
- 1200px 데스크톱과 400px 모바일에서 가로 스크롤이 생기지 않아야 한다.
- 움직임은 prefers-reduced-motion 을 존중한다.

끝나면 만든 파일 목록과 브라우저에서 확인할 URL 을 한 줄씩 적어라.

복사한 뒤 Claude Code 새 대화에 그대로 붙이면 됩니다. 아래 값은 붙이기 전에 숫자만 바꿔도 결과가 달라집니다.

바꿔 볼 값

  • 빛 색 GLOWvec3(0.10, 0.74, 1.00)

    파랑 계열만. 초록을 0.74 위로 올리면 청록, 0.5 아래로 내리면 남색이 된다

  • 정육면체 크기vec3(0.50, 0.54, 0.50)

    0.35 ~ 0.75. 0.8 을 넘으면 산맥 사이를 벗어나 화면을 다 덮는다

  • 톤매핑 세기0.40

    0.25 ~ 0.85(작을수록 전체가 밝고, 클수록 정육면체만 남는다)

  • 산이 물드는 범위1/(1 + 거리²*0.42)

    0.15 ~ 0.9(작을수록 산 전체가 파래진다)

  • 무대 높이460vh

    300vh ~ 700vh(길수록 카메라가 천천히 다가간다)

붙이기 전에 한 번

언제 쓰고, 뭐가 필요한가

언제 쓰나

건축·부동산·인프라처럼 '규모'를 보여 줘야 하는 회사 소개 첫 화면에 쓴다. Three.js 도 Spline 도 붙이지 않고 60줄 남짓한 셰이더로 끝나기 때문에, 외부 파일을 하나도 받지 않고도 화면 전체를 움직이게 할 수 있다.

준비물

  • 새 프로젝트다. Next.js 는 1번 뼈대가 깔려 있고, HTML 은 빈 index.html 에서 시작한다
  • 브랜드명과 히어로 문구가 있다: 라벨 한 줄, 대문자 제목 두 줄, 설명 두 문장
  • 아래 섹션에 채울 항목이 있다. 프로젝트 3개(제목·한 문단), 숫자 4개, 단계 3개, 인용 한 문장
  • WebGL2 를 쓴다. 지원하지 않는 브라우저에서는 캔버스를 배경 그라디언트로 대체한다
복사해서 붙인 직후

결과는 이렇게 나옵니다

첫 화면을 열면 검은 바다 위에서 정육면체가 아주 느리게 숨 쉬고, 물결이 그 빛을 받아 화면 앞까지 늘어진다. 스크롤을 내리면 카메라가 옆에서 정면으로 다가가 정육면체가 화면을 채우고, 글은 그동안 비켜 준다. 무대를 지나면 카드 속 작은 정육면체들이 각자 다른 속도로 돌고 있어 같은 장면이 이어진 것처럼 보인다.

같이 쓰면 좋습니다

이어서 붙이면 좋은 카드