# AI 꾸러미 — 태양 표면 · 코로나 · 홍염 — Solar granulation
> 흐르는 3D 잡음으로 태양 표면 쌀알 무늬 · 중위도 흑점 · 가장자리 어둡게(주연 감광)를 칠하고, 둘레 판에 줄무늬 코로나 · 붉은 채층 · 고리 홍염을 그린다.  
> 견본: https://ai-techstudio.web.app/#t/i528

이 문서를 코드 도우미(Claude Code · Cursor · ChatGPT 등)에 그대로 주면 돼요. 「## 주문서」 가 할 일, 나머지는 참고 자료예요.

## 주문서

### 만들어 줘: 태양 표면 · 코로나 · 홍염 — Solar granulation

#### 1. 목표
태양 학습 체험을 태양으로 그려 줘 — 쌀알 무늬 · 흑점 · 주연 감광 · 줄무늬 코로나 · 고리 홍염까지, 느낌은 관측 사진처럼.

#### 2. 핵심 기술 용어
- **Solar granulation** — 태양 표면 쌀알 무늬 (대류 칸)
- **Limb darkening** — 가장자리가 어두워 보이는 주연 감광
- **Corona streamers (polar noise)** — 극좌표로 잡음을 읽은 줄무늬 빛
- **Prominence loops** — 가장자리에 발을 둔 반타원 고리 홍염

#### 3. 환경
- 플랫폼: three.js r186 (ES 모듈 · TypeScript, `import * as THREE from "three"`), WebGL2, 외부 라이브러리 추가 없이
- 화면: 3D · 브라우저 — PC 와 폰(가로 844×390 · 세로 390×844) 모두, 60fps 목표

#### 4. 조건
- 표면 무늬는 3D 잡음 텍스처를 구 법선(vN)으로 — UV 이음매 없이
- 흑점은 위도 띠 안에서만, 반암부(0.74) · 암부(0.78) 두 문턱
- 주연 감광은 μ = 법선 · 시선 으로
- 코로나 판은 해보다 크게(7.2), AdditiveBlending · depthWrite false, 해 안쪽(r < 0.97)은 discard
- ACESFilmic 톤 매핑 — 밝은 표면이 하얗게만 날아가지 않게

#### 5. 완성 기준 (이게 보이면 성공)
- 표면에 쌀알 무늬가 천천히 끓고, 중위도에 흑점 무리가 있다
- 가장자리가 가운데보다 어둡고 붉다 (주연 감광)
- 둘레로 줄무늬 코로나가 뻗고, 가장자리에 붉은 채층 테 · 고리 홍염이 흐른다
- 「흑점」 · 「코로나 세기」 · 「홍염」 조절이 바로 바뀐다

#### 6. 진행 방식
- 핵심 코드 위주로, 설명은 짧게. 내 프로젝트에 끼워 넣기 쉬운 함수 · 클래스로 나눠 줘.
- 처음 화면에 바로 결과가 보이게, 그리고 켬/끔(또는 전/후) 비교를 할 수 있게 만들어 줘.
- 그림 · 소리 · 모델 파일이 필요하면 코드로 만든 임시 대체물로 먼저 돌아가게 하고, 진짜 파일로 바꿀 자리를 표시해 줘.
- 마지막에 「확인 방법」(무엇을 보면 성공인지)과 「조절할 값」 목록을 짧게 정리해 줘.
- 답변과 코드 주석은 한국어로 해 줘.

## 원리
- 표면 밝기는 3D 잡음을 구 법선으로 읽는다. 능선 잡음 두 크기(10 · 23배)의 제곱을 빼면 밝은 칸 · 어두운 틈 쌀알 무늬가 된다. 잡음 좌표를 천천히 흘린다.
- 흑점은 큰 잡음이 문턱(0.74 · 0.78)을 넘는 곳, 위도 띠(|y| 0.06 ~ 0.6) 안에서만. 둘레 가장자리 쪽에 밝은 백반.
- 주연 감광: 가장자리로 갈수록(시선과 법선 μ 가 작을수록) 어둡다 — 1 - 0.6(1-μ) - 0.22(1-μ)².
- 코로나는 해를 덮는 판 하나에 그린다. 각도(atan)와 반지름으로 잡음을 읽어 바깥으로 뻗는 줄무늬 빛, 바로 가장자리엔 붉은 채층 테.
- 홍염은 가장자리에 발을 둔 반타원 고리까지의 거리로 밝기를 주고, 잡음으로 실 · 흐름을 더한다.

## 핵심 코드 — 태양 표면 — 쌀알 · 흑점 · 주연 감광
(발췌: demos/demosSpace.ts SUN_FS 를 정리)
```glsl
uniform highp sampler3D uN3; uniform float uTime, uSpots;
varying vec3 vN, vWN, vWP;
void main(){
  vec3 n = normalize(vN);
  float mu = max(dot(normalize(vWN), normalize(cameraPosition - vWP)), 0.0);
  float tt = uTime;
  vec3 w = vec3(texture(uN3, n * 0.9 + vec3(tt * 0.004)).r, texture(uN3, n * 0.9 + vec3(0.5, tt * 0.003, 0.2)).r, 0.0) - 0.5;
  float gA = texture(uN3, n * 10.0 + vec3(w.xy * 0.18, tt * 0.006)).g;   // 능선 잡음 (쌀알)
  float gB = texture(uN3, n * 23.0 + vec3(tt * 0.01, w.x * 0.2, 0.3)).g;
  float sup = texture(uN3, n * 0.8 + vec3(0.3, 0.1, tt * 0.002)).r;
  float I = 1.0 + 0.3 * (sup - 0.5) - 0.42 * gA * gA - 0.18 * gB * gB;  // 밝은 칸 · 어두운 틈
  // 흑점 — 중위도 띠 안에서만
  float s = texture(uN3, n * 0.62 + vec3(0.31, 0.11, 0.73)).r + (gA - 0.5) * 0.03;
  float belt = smoothstep(0.06, 0.18, abs(n.y)) * smoothstep(0.6, 0.38, abs(n.y));
  float pen = smoothstep(0.74, 0.765, s) * belt * uSpots;
  float umb = smoothstep(0.78, 0.79, s) * belt * uSpots;
  I *= 1.0 - pen * 0.5 - umb * 0.42;
  I += smoothstep(0.6, 0.7, s) * (1.0 - pen) * belt * pow(1.0 - mu, 1.5) * 0.7 * uSpots; // 백반
  float L = I * (1.0 - 0.6 * (1.0 - mu) - 0.22 * (1.0 - mu) * (1.0 - mu));               // 주연 감광
  vec3 col = mix(vec3(0.85, 0.14, 0.0), vec3(1.0, 0.45, 0.06), smoothstep(0.05, 0.55, L));
  col = mix(col, vec3(1.0, 0.78, 0.4), smoothstep(0.65, 1.1, L));
  gl_FragColor = vec4(col * L * 1.7, 1.0);
  #include <tonemapping_fragment>
  #include <colorspace_fragment>
}
// 코로나 판: r = length(vP), a = atan(vP.y, vP.x) / TAU 로 잡음을 읽어 줄무늬,
//   glow = exp(-x * 12.0) * 0.35 + pow(r, -8.0) * 0.3 * streak + exp(-x * 1.8) * 0.008 * streak; (x = r - 1)
//   채층 = vec3(1.0, 0.18, 0.05) * exp(-x * 70.0) * 1.6
```

## 흔한 실수 · 확인 목록
- [ ] **구 UV 로 무늬를 그리면 극에서 무늬가 몰리고 이음매가 보인다** — 구 법선으로 3D 잡음을 읽는다.
- [ ] **코로나 판을 해 안쪽까지 칠하면 표면이 뿌옇게 덮인다** — 반지름 0.97 안은 discard, 가장자리는 smoothstep 으로 이어 붙인다.
- [ ] **셰이더 안에서 3D 잡음을 여러 번 계산하면 느리고 컴파일이 오래 걸린다** — 64³ 잡음 텍스처를 한 번 굽고 여러 크기로 읽는다.

## 완성 기준 체크리스트
- [ ] 표면에 쌀알 무늬가 천천히 끓고, 중위도에 흑점 무리가 있다
- [ ] 가장자리가 가운데보다 어둡고 붉다 (주연 감광)
- [ ] 둘레로 줄무늬 코로나가 뻗고, 가장자리에 붉은 채층 테 · 고리 홍염이 흐른다
- [ ] 「흑점」 · 「코로나 세기」 · 「홍염」 조절이 바로 바뀐다

## 이 기술 정보
- id: `i528` · 분류: 그래픽 · 셰이더 › 우주 표현 · 3D · 난이도 보통 · 폰 부담 보통 (폰 주의) — 태양 구 픽셀마다 3D 텍스처 약 6번, 코로나 판 픽셀마다 2D 텍스처 3번 + 홍염 3개 × 3번. 화면을 크게 덮으면 폰에서 조금 무겁다.
- 라이브 견본 (브라우저에서 직접 조작): https://ai-techstudio.web.app/#t/i528
- 쓰면 좋을 때: 태양 · 별을 가까이서 보여 줄 때 / 보스 · 마법 구슬처럼 이글거리는 공 / 우주 배경의 큰 해
- 쓰지 말 때: 멀리 작게 보이는 해 — 빛 스프라이트 하나면 충분 / 셰이더에서 3D 잡음을 계산 — 텍스처로 읽을 것

## 견본 실제 코드 (라이브 견본이 돌리는 코드 — three.js · TypeScript)
### i528 견본 항목 — `src/demos/demosSpace.ts:2163`
```ts
  i528: {
    kind: '3d',
    caption: '태양 — 흐르는 쌀알 무늬 · 흑점 · 주연 감광(가장자리가 어둡다), 둘레엔 줄무늬 코로나와 붉은 채층, 가장자리에 반타원 고리 홍염이 흐른다',
    make(): Scene3D {
      const bin = new Bin();
      const scene = new THREE.Scene();
      scene.background = new THREE.Color(0x000000);
      const cam = new THREE.PerspectiveCamera(40, 1.6, 0.1, 1000);
      cam.position.set(0, 0, 5.4);
      const S = makeStars(8000, 300, 66, { magMax: 6.5, tw: 0.05, gain: 0.6 });
      scene.add(S.pts);
      const U = { uN3: { value: noise3D() }, uTime: { value: 0 }, uSpots: { value: 1 } };
      const sun = new THREE.Mesh(new THREE.SphereGeometry(1, 160, 96), new THREE.ShaderMaterial({ uniforms: U, vertexShader: SUN_VS, fragmentShader: SUN_FS }));
      scene.add(sun);
      const UC = { uN2: { value: noise2D() }, uTime: { value: 0 }, uCor: { value: 1 }, uProm: { value: 1 } };
      const corona = new THREE.Mesh(new THREE.PlaneGeometry(7.2, 7.2), new THREE.ShaderMaterial({ uniforms: UC, vertexShader: CORONA_VS, fragmentShader: CORONA_FS, transparent: true, depthWrite: false, blending: THREE.AdditiveBlending }));
      corona.renderOrder = 1;
      scene.add(corona);
      bin.scene(scene);
      return {
        scene,
        camera: cam,
        tone: THREE.ACESFilmicToneMapping,
        update(t) {
          U.uTime.value = t;
          UC.uTime.value = t;
          S.U.uTime.value = t;
          sun.rotation.y = t * 0.025;
        },
        resize: (_w, h) => (S.U.uScale.value = h / 900),
        controls: [
          range('흑점', 0, 1.5, 0.05, 1, (v) => (U.uSpots.value = v)),
          range('코로나 세기', 0, 2.5, 0.05, 1, (v) => (UC.uCor.value = v)),
          range('홍염', 0, 2.5, 0.05, 1, (v) => (UC.uProm.value = v)),
        ],
        dispose: () => {
          bin.dispose();
          S.dispose();
        },
      };
    },
  }
```

## 관련 기술
- 먼저 알면 좋은 기술: [행성 대기 (림 산란 · 낮밤 경계 · 구름 · 도시 불빛)](https://ai-techstudio.web.app/ai/t/i526.md) `i526`
- 다음에 해 볼 기술: [초신성 · 충격파 고리](https://ai-techstudio.web.app/ai/t/i534.md) `i534` · [블랙홀 (중력 렌즈 · 강착 원반 도플러)](https://ai-techstudio.web.app/ai/t/i529.md) `i529`
- 참고 문서: [Wikipedia — Limb darkening](https://en.wikipedia.org/wiki/Limb_darkening) · [Wikipedia — Granule (solar physics)](https://en.wikipedia.org/wiki/Granule_(solar_physics))
