게자리 펄사 자기권
게자리 펄사 자기권
Crab Pulsar Magnetosphere
해당 글은 AI로 대부분의 코드가 작성됨을 알림.
사람이 일부 수정한 부분은 있으나, 수학적 표현 및 코드 대부분 AI가 작성함.
PSR B0531+21의 비스듬한 회전자, 펄사풍 종단충격, 위습, 제트, striped-wind 전류 시트와 초신성 잔해를 하나의 3차원 방출·소광 부피로 적분하는 순수 WebGL/GLSL 시각화
1. 개요 (Overview)
이 프로젝트의 핵심은 펄사 위에 2D 성운 이미지를 얹는 것이 아니다. 카메라에서 출발한 하나의 원근 광선 안에서 외곽 초신성 쉘, 펄사풍 성운, 종단충격 토러스, 팽창 위습, 양극 제트, 자기권 전류 시트와 중앙 중성자별을 같은 월드 좌표와 같은 깊이 순서로 계산한다.
따라서 카메라를 기울이면 모든 구조가 함께 회전하고, 전면의 가스는 후면의 토러스와 제트를 자연스럽게 감쇠한다. 중앙 펄사만 3D이고 주변 성운은 화면에 고정된 2D 배경 합성 방식은 사용하지 않는다.
이 프로젝트에서 Kali 구면반전은 독립적인 우주 배경이 아니라 초신성 쉘의 희박한 밀도 변조에만 사용한다.
2. 장면 구성과 밀도 모델
Ultimate는 여러 완성 이미지를 더하는 합성기가 아니라, 한 위치 에서 방출계수 와 소광계수 를 반환하는 단일 볼륨 샘플러다.
공간 구조 | 수학적 표현 | 구현 함수 | 깊이에서의 역할 |
|---|---|---|---|
초신성 잔해 | 비등방 타원체 shell + rotated FBM/ridged noise + Kali 변조 |
| 앞·뒤 쉘이 중심 구조를 둘러싸고 부분적으로 가린다 |
확산 펄사풍 | 저주파 3D noise로 변조한 광학적으로 얇은 싱크로트론 방출 |
| 내부 공간의 희박한 청색 발광 |
종단충격 | 회전축에 수직인 3D torus SDF |
| 펄사풍이 감속되는 밝은 고리 |
위습 | 시간에 따라 반지름이 커지는 세 개의 불완전 torus |
| 종단충격에서 바깥으로 전달되는 파동 구조 |
제트 | 고정 회전축을 따르는 굽은 원뿔형 밀도장 |
| 자기축과 함께 회전하지 않는 양극 유출 |
자기권 | 닫힌 쌍극자 자속 + retarded striped-wind 영점면 |
| 중심의 회전 구조와 전류 시트 |
중심 엔진 | analytic ray-sphere + 비등방성 noisy corona |
| 정확한 깊이에서 후면을 차폐하는 작은 중성자별 |
- 공간 구조
초신성 잔해
- 수학적 표현
비등방 타원체 shell + rotated FBM/ridged noise + Kali 변조
- 구현 함수
sampleUltimateVolume()- 깊이에서의 역할
앞·뒤 쉘이 중심 구조를 둘러싸고 부분적으로 가린다
- 공간 구조
확산 펄사풍
- 수학적 표현
저주파 3D noise로 변조한 광학적으로 얇은 싱크로트론 방출
- 구현 함수
sampleUltimateVolume()- 깊이에서의 역할
내부 공간의 희박한 청색 발광
- 공간 구조
종단충격
- 수학적 표현
회전축에 수직인 3D torus SDF
- 구현 함수
sampleUltimateVolume()- 깊이에서의 역할
펄사풍이 감속되는 밝은 고리
- 공간 구조
위습
- 수학적 표현
시간에 따라 반지름이 커지는 세 개의 불완전 torus
- 구현 함수
sampleUltimateVolume()- 깊이에서의 역할
종단충격에서 바깥으로 전달되는 파동 구조
- 공간 구조
제트
- 수학적 표현
고정 회전축을 따르는 굽은 원뿔형 밀도장
- 구현 함수
sampleUltimateVolume()- 깊이에서의 역할
자기축과 함께 회전하지 않는 양극 유출
- 공간 구조
자기권
- 수학적 표현
닫힌 쌍극자 자속 + retarded striped-wind 영점면
- 구현 함수
pulsar-model.glsl,ult.frag- 깊이에서의 역할
중심의 회전 구조와 전류 시트
- 공간 구조
중심 엔진
- 수학적 표현
analytic ray-sphere + 비등방성 noisy corona
- 구현 함수
main()과sampleUltimateVolume()- 깊이에서의 역할
정확한 깊이에서 후면을 차폐하는 작은 중성자별
이 모델 경계가 중요한 이유는, 각 구조를 별도의 화면 레이어로 되돌리면 tilt·원근·가림이 다시 분리되기 때문이다.
3. 관측값, 모델값, 시각 배율
물리적으로 측정된 값과 화면에서 구조를 읽기 위해 고른 값을 구분한다.
항목 | 구현값 | 성격 | 사용처 |
|---|---|---|---|
자전 주파수 | 2025 timing reference를 반올림 | 회전 위상 | |
자전 주기 | 약 | 주파수에서 유도 | 문서와 위상 해석 |
주펄스–간펄스 간격 | cycle | 경험적 X-ray 광도곡선 | 두 번째 Gaussian 위치 |
간펄스 진폭 | 시각 광도곡선 계수 | pulse profile | |
시선–회전축 경사 | 관측 기반 | 회전축과 토러스 투영 | |
자기축 경사 | 대표 oblique-rotator 값 | 자기 모멘트 궤적 | |
화면 위치각 | 시각 배치값 | 화면상의 회전축 방향 | |
볼륨 경계 반지름 | world unit | 시각 배율 | raymarch 구간 |
광원통 반지름 | world unit | 시각 배율 | near/far magnetosphere 경계 |
종단충격 반지름 | world unit | 시각 배율 | torus와 위습의 시작점 |
중성자별 반지름 | world unit | 과장된 시각 배율 | subpixel 소실을 막는 중심 코어 |
- 항목
자전 주파수
- 구현값
- 성격
2025 timing reference를 반올림
- 사용처
회전 위상
- 항목
자전 주기
- 구현값
약
- 성격
주파수에서 유도
- 사용처
문서와 위상 해석
- 항목
주펄스–간펄스 간격
- 구현값
cycle
- 성격
경험적 X-ray 광도곡선
- 사용처
두 번째 Gaussian 위치
- 항목
간펄스 진폭
- 구현값
- 성격
시각 광도곡선 계수
- 사용처
pulse profile
- 항목
시선–회전축 경사
- 구현값
- 성격
관측 기반
- 사용처
회전축과 토러스 투영
- 항목
자기축 경사
- 구현값
- 성격
대표 oblique-rotator 값
- 사용처
자기 모멘트 궤적
- 항목
화면 위치각
- 구현값
- 성격
시각 배치값
- 사용처
화면상의 회전축 방향
- 항목
볼륨 경계 반지름
- 구현값
world unit
- 성격
시각 배율
- 사용처
raymarch 구간
- 항목
광원통 반지름
- 구현값
world unit
- 성격
시각 배율
- 사용처
near/far magnetosphere 경계
- 항목
종단충격 반지름
- 구현값
world unit
- 성격
시각 배율
- 사용처
torus와 위습의 시작점
- 항목
중성자별 반지름
- 구현값
world unit
- 성격
과장된 시각 배율
- 사용처
subpixel 소실을 막는 중심 코어
중성자별, 수천 km 규모의 광원통, 약 광년 규모의 종단충격, 수 광년 규모의 초신성 잔해는 한 화면에서 실제 비율로 동시에 표시할 수 없다. 시간 상수와 공간 배율은 같은 종류의 값이 아니다. 공간 수치는 물리 단위가 아니라 구조 간 의미 관계를 보이기 위한 world-space 배치다.
4. 카메라와 광선–볼륨 교차
4.1 원근 카메라
화면 좌표는 세로 해상도로 나눠 종횡비를 보정한다.
카메라 위치 에서 원점을 바라보는 전방벡터 , 우측벡터 , 상향벡터 를 만들고 광선 방향을 계산한다.
구현 — shaders/ult.frag, main()
vec2 screen = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
vec3 forward = normalize(-rayOrigin);
vec3 right = normalize(cross(forward, vec3(0.0, 1.0, 0.0)));
vec3 up = normalize(cross(right, forward));
vec3 rayDirection = normalize(
forward * 1.48
+ right * screen.x * uZoom
+ up * screen.y * uZoom
);여기서 는 UI의 Field Zoom이다. 기본 카메라 거리는 , 기본 pitch는 이며 드래그가 yaw와 pitch를 바꾼다. 이 카메라는 쉘부터 별 표면까지 모든 구조에 한 번만 적용된다.
4.2 경계구 교차
광선은
이고 반지름 인 원점 중심 구와의 교차는 다음 이차식의 두 근으로 구한다.
구현 — shaders/ult.frag, raySphere()
float projection = dot(rayOrigin, rayDirection);
float discriminant = projection * projection
- dot(rayOrigin, rayOrigin) + radius * radius;
if (discriminant < 0.0) return vec2(-1.0);
float root = sqrt(discriminant);
return vec2(-projection - root, -projection + root);인 픽셀은 볼륨을 전혀 샘플링하지 않는다. 교차한 픽셀만 구간을 52 step으로 전진한다.
5. 방출·소광 적분 (Emission–Extinction Integration)
각 step의 위치는 이고, 볼륨 샘플러는 vec4(emission.rgb, extinction)을 반환한다.
연속 방사전달식의 현재 이산 근사는 다음과 같다.
GLSL의 step alpha는
구현 — shaders/ult.frag, main()의 52-step 적분
float alpha = 1.0 - exp(-sampleValue.a * stepSize * 2.15);
accumulated += transmittance * sampleValue.rgb * stepSize * 1.36;
transmittance *= 1.0 - alpha;
if (transmittance < 0.012) break;1 - alpha가 바로 다. 1.36과 2.15는 실제 분광 계수가 아니라, 광학적으로 얇은 synchrotron 방출과 약한 소광을 분리하는 렌더링 배율이다.
광학적으로 얇은 싱크로트론 방출은 소광보다 강하게 누적한다. 이 분리는 초기 구현에서 외곽 쉘이 짙은 안개처럼 중심 토러스와 제트를 모두 삼키던 문제를 해결한다.
고정 step의 층상 밴딩을 줄이기 위해 각 픽셀의 첫 샘플 위치에 hash jitter를 더한다.
float stepSize = (farDistance - nearDistance) / float(VOLUME_STEPS);
float jitter = hash(fragCoord + fract(iTime)) * stepSize;
float sampleDistance = nearDistance + jitter;투과율이 가 되면 조기 종료한다. 배경 별은 볼륨 적분 이후에만 더해지므로 성운의 전면 밀도가 배경을 올바르게 감쇠한다.
6. 초신성 쉘의 3D 밀도장
6.1 비등방 타원체
외곽 잔해는 구가 아니라 축비 인 타원체 거리로 정의한다.
는 느리게 이류된 위치다. 세 축에 서로 다른 위상과 주파수를 주어 외곽 물질이 평면 원반처럼 강체 회전하지 않도록 한다.
구현 — shaders/ult.frag, sampleUltimateVolume()
float delayedFlow = time * 0.055 - radius * 3.7;
vec3 advectedPoint = point + 0.045 * vec3(
sin(2.8 * point.y + delayedFlow),
sin(2.6 * point.z + delayedFlow + 2.1),
sin(3.0 * point.x + delayedFlow + 4.2)
);
float ellipsoidRadius = length(advectedPoint / vec3(1.22, 0.72, 0.96));
float shellBand = exp(-pow((ellipsoidRadius - 0.91) / 0.12, 2.0));, 은 world-space 시각 시간의 이류 계수이며 물리 SI 단위가 아니다.
6.2 FBM과 ridged noise
큰 가스 덩어리는 3-octave FBM, 날카로운 필라멘트는 ridged noise가 담당한다.
구현 — shaders/ult.frag, fbm3()·ridged3()
for (int octave = 0; octave < 3; octave++) {
value += amplitude * noise3(point);
point = point.yzx * 2.03 + vec3(5.2, 1.3, 8.1);
amplitude *= 0.48;
}
float ridge = 1.0 - abs(2.0 * noise3(point) - 1.0);
value += amplitude * ridge * ridge;첫 옥타브의 value-noise 격자가 정사각형 또는 voxel band로 보이지 않도록 고정 3D 기저 rotateNoiseSpace()를 먼저 적용한다. 저주파 cloud 값을 다시 좌표에 넣는 domain warp가 필라멘트를 셀 격자에서 휘어진 가스 흐름으로 바꾼다.
6.3 Kali 구면반전의 제한된 사용
Kali 계열 구면반전은 세 번 반복한다.
구현 — shaders/ult.frag, kaliDensity()
for (int fold = 0; fold < 3; fold++) {
folded = abs(folded)
/ max(dot(folded, folded), 0.075)
- 0.659;
}그러나 이 결과를 독립 프랙탈 우주로 렌더하지 않는다. 궤도 길이 에너지를 희박한 shell density modulator로만 사용해 불규칙한 빈 공간과 응축부를 만든다.
7. 펄사풍 성운: 토러스, 위습, 제트
회전축 와 두 수직 기저 로 모든 점을 pulsar frame에 투영한다.
구현 — shaders/ult.frag, sampleUltimateVolume()
vec3 frame = vec3(
dot(point, equatorA),
dot(point, equatorB),
dot(point, spinAxis)
);
float equatorialRadius = length(frame.xy);
float azimuth = atan(frame.y, frame.x);적도 반지름을 , 축 방향 거리를 라 둔다.
7.1 종단충격 토러스
float torusDistance = length(vec2(
equatorialRadius - WORLD_TERMINATION_SHOCK,
frame.z
));
float torus = exp(-pow(torusDistance / 0.025, 2.0));방위각 knot noise와 접근면 가중치를 추가해 완벽히 균일한 네온 링이 되지 않게 한다.
7.2 팽창 위습
세 위습의 나이는 서로 cycle만큼 어긋난다.
float age = mod(time / 4.6 + float(wispIndex) / 3.0, 1.0);
float wispRadius = WORLD_TERMINATION_SHOCK + age * 0.33;
float wispDistance = length(vec2(equatorialRadius - wispRadius, frame.z));
float arc = exp(-pow(wispDistance / (0.012 + age * 0.020), 2.0));
float wisp = arc * fragment * pow(1.0 - age, 1.7);나이가 들수록 폭은 넓어지고 밝기는 로 감소한다. 방위각 sinusoid로 일부를 잘라 위습이 닫힌 원이 아니라 관측 영상의 불완전 arc처럼 보이게 한다.
7.3 고정 회전축 제트
제트 폭은 축을 따라 완만하게 증가한다.
float alongJet = frame.z;
float jetBend = 0.018 * sin(12.0 * alongJet - time * 0.55)
* smoothstep(0.10, 0.72, abs(alongJet));
vec2 bentJet = frame.xy
- vec2(jetBend, -jetBend * 0.45) * sign(alongJet);
float jetRadius = length(bentJet);
float jetWidth = 0.020 + 0.040 * abs(alongJet);중심선에는 느린 kink를 주지만 축의 소유자는 항상 다. 제트가 회전하는 자기축 를 따라 프로펠러처럼 도는 구현은 사용하지 않는다.
8. 비스듬한 회전자와 자기권
8.1 자기축
회전축을 , 그 수직 기저를 , 자기축 경사를 라 하면 자기 모멘트는
구현 — shaders/pulsar-model.glsl, magneticAxis()
return normalize(
cos(MAGNETIC_OBLIQUITY) * spinAxis
+ sin(MAGNETIC_OBLIQUITY)
* (cos(spinPhase) * basisA + sin(spinPhase) * basisB)
);이다. 두 자기극은 언제나 과 으로 정확히 반대편이다. 관측된 두 광도 피크의 cycle 간격을 두 극의 공간 각도로 사용하는 오류를 범하지 않는다.
8.2 광원통과 감속 광도
구현 경계 — shaders/pulsar-model.glsl
const float LIGHT_CYLINDER = 0.105;
const float MAGNETIC_OBLIQUITY = 0.87266463;이 두 물리식은 모델의 구조적 기준이다. 현재 셰이더는 , , 를 SI 단위로 적분해 광도를 계산하지 않으므로 에 대응하는 허위 코드를 제시하지 않는다. 구현된 부분은 를 정규화한 경계와 의 기하뿐이다.
화면의 WORLD_LIGHT_CYLINDER=0.105는 의 물리적 길이를 그대로 환산한 값이 아니라 near-zone과 wind-zone을 읽기 위한 시각 반지름이다.
8.3 닫힌 쌍극자장
광원통 내부는 쌍극자 자속 불변량을 사용한다.
구현 — shaders/ult.frag, sampleUltimateVolume()
vec3 moment = magneticAxis(
spinPhase - radius / WORLD_LIGHT_CYLINDER * 0.12,
FIELD_PA
);
float magneticCosine = dot(radialDirection, moment);
float sinThetaSquared = max(0.016, 1.0 - magneticCosine * magneticCosine);
float radiusUnits = radius / WORLD_LIGHT_CYLINDER;
float fluxInvariant = sinThetaSquared / max(radiusUnits, 0.08);
float closed = 1.0 - smoothstep(0.88, 1.12, radiusUnits);
float fieldLines = pow(
max(0.0, 0.5 + 0.5 * cos(10.0 * fluxInvariant)),
16.0
) * closed;셰이더는 의 등고선을 가느다란 방출 밴드로 바꾸고, 광원통에 접근할수록 닫힌 영역을 감쇠한다.
8.4 retarded striped-wind current sheet
광원통 바깥은 진공 쌍극자 탐조등이 아니라 retarded split-monopole의 전류 시트 영점면을 사용한다.
코드에서는
를 계산하고
구현 — shaders/ult.frag, sampleUltimateVolume()
float lightCylinderUnits = radius / WORLD_LIGHT_CYLINDER;
vec3 retardedAxis = magneticAxis(
spinPhase - lightCylinderUnits,
FIELD_PA
);
float sheetCoordinate = dot(retardedAxis, radialDirection);
float sheetWidth = 0.028 + 0.005 * min(lightCylinderUnits, 5.0);
float sheet = exp(-pow(sheetCoordinate / sheetWidth, 2.0))
* smoothstep(0.86, 1.14, lightCylinderUnits)
* exp(-radius * 2.0);로 유한 폭의 3D 발광 곡면을 만든다. 이 구조는 화면에 그린 2D spiral line이 아니라 광선을 따라 앞뒤 면이 교차하는 실제 볼륨 표면이다.
9. 펄스 광도곡선 — 구의 맥동이 아니다
위상 경계를 감싼 거리는
이고, 주펄스와 간펄스는 두 Gaussian으로 근사한다.
구현 — shaders/pulsar-model.glsl, wrapPhase()·pulseProfile()
float wrapPhase(float d) {
d = abs(fract(d));
return min(d, 1.0 - d);
}
float mainPulse = exp(-0.5 * pow(mainDistance / MAIN_SIGMA, 2.0));
float interPulse = exp(-0.5 * pow(interDistance / INTER_SIGMA, 2.0));
return mainPulse + INTERPULSE_AMP * interPulse;이 함수는 별의 크기나 위치를 바꾸지 않는다. 회전하는 자기권 방사 패턴이 관측 시선을 지날 때 생기는 caustic 광도 변화를 작은 polar cap과 중심 자기권 방출에만 적용한다.
실제 회전을 그대로 그리면 60 Hz 디스플레이에서 한 회전당 약 두 프레임만 얻는다. 따라서
const float CRAB_FREQUENCY = 29.58936;
const float PULSAR_SLOWDOWN = 60.0;
float phase = fract(time * CRAB_FREQUENCY / PULSAR_SLOWDOWN);처럼 60배 감속해 구조를 읽을 수 있게 한다. PULSAR_SLOWDOWN=60은 물리량이 아니라 관찰용 시간 배율이다.
10. 중앙 중성자별과 noisy corona
중성자별은 볼륨 밀도로 뭉개지 않고 별도의 작은 구로 정확히 교차시킨다. 반지름 에 대해 구의 앞면 깊이를 구한 뒤, raymarch sample이 그 깊이에 도달하는 순간 표면 방출을 삽입하고 뒤쪽 투과율을 낮춘다. 그래서 별 앞의 가스는 별을 가릴 수 있고, 별은 후면의 자기권을 차폐한다.
큰 백색 구를 유지하면 실제로 분해되지 않는 펄사가 행성처럼 보인다. 현재 반경은 이전 에서 로 줄였으며, 화면에서 사라지지 않을 최소한의 시각 과장만 남겼다.
노이즈는 중성자별 표면을 암석처럼 찌그러뜨리는 용도가 아니다. 반경 이내의 코로나에 다음 세 항을 결합한다.
형태의 compact envelope
형태의 자기극 집중
회전된 ridged noise로 만든 국소적인 밝기 구멍
구현 — shaders/ult.frag, sampleUltimateVolume()과 main()
float polarFlux = pow(abs(dot(radialDirection, coreMoment)), 9.0);
float returnCurrent = exp(-pow(dot(radialDirection, spinAxis) / 0.16, 2.0));
float coreEnvelope = exp(-pow(radius / 0.058, 2.0));
float coreTexture = 0.20 + 0.80 * ridged3(
rotateNoiseSpace(point * 56.0)
+ vec3(time * 0.09, -time * 0.05, time * 0.07)
);
float corePlasma = coreEnvelope * coreTexture
* (0.16 + 0.62 * polarFlux + 0.22 * returnCurrent);
const float STAR_RADIUS = 0.012;
vec2 starHit = raySphere(rayOrigin, rayDirection, STAR_RADIUS);따라서 중심은 완벽한 흰 공이 아니라 작은 불투명 코어와 끊어진 비등방성 플라즈마로 읽힌다.
11. 색, 노출과 톤매핑
모든 방출은 선형광에서 누적한 뒤 마지막에 한 번만 ACES 근사 톤매핑을 적용한다.
구현 — shaders/ult.frag, acesToneMap()과 main()
vec3 numerator = color * (2.51 * color + 0.03);
vec3 denominator = color * (2.43 * color + 0.59) + 0.14;
return clamp(numerator / denominator, 0.0, 1.0);
// main()
vec3 color = acesToneMap(accumulated * uIntensity * 1.34);
color = pow(max(color, 0.0), vec3(0.91));
color += (hash(fragCoord + fract(iTime)) - 0.5) / 255.0;이후 약한 gamma shaping과 이하의 dithering을 더한다. 각 구조에서 먼저 tone map한 이미지를 다시 합치지 않기 때문에 토러스와 쉘이 겹쳐도 색과 에너지 순서가 유지된다.
팔레트는 다음 물리적 구조의 구분을 참고하지만 분광 방출선을 직접 적분한 결과는 아니다.
청백색: synchrotron interior, 접근면 토러스, 제트
청색–자홍색: 서로 다른 polarity의 current sheet와 자기권
적색–주황색: 초신성 잔해의 응축 필라멘트
12. 성능 설계
경계구와 교차한 픽셀만 52-step raymarch를 수행한다.
shell noise는
shellBand > 0.002인 영역에서만 평가한다.noisy corona는 에서만 평가한다.
토러스와 제트도 각자의 좁은 거리 경계 안에서만 고비용 함수를 실행한다.
투과율 이면 조기 종료한다.
모든 GLSL 파일은 병렬 fetch하고, 시작 시 프로그램을 엄격하게 컴파일한다.
셰이더 로드나 컴파일 실패를 다른 화면으로 조용히 대체하지 않는다.
js/shaders.js는 프로젝트 수집기와 브라우저 모두가 의존 파일을 식별할 수 있도록 GLSL을 명시적인 URL manifest로 선언한다.
const SHADER_URLS = {
vertex: new URL('../shaders/vertex.vert', import.meta.url),
ultimate: new URL('../shaders/ult.frag', import.meta.url),
pulsarModel: new URL('../shaders/pulsar-model.glsl', import.meta.url)
};폴더 URL과 동적 파일명 조합은 사용하지 않는다.
13. 검증 기준
화면이 화려하다는 사실만으로 모델이 맞다고 판정하지 않는다. 다음 조건을 확인한다.
공통 카메라 — drag/tilt 때 쉘·토러스·위습·제트·전류 시트가 같은 원근으로 움직인다.
깊이 가림 — 전면 쉘과 확산 가스가 후면 구조를 감쇠하고, 별이 후면 자기권을 가린다.
축 분리 — 제트는 고정 회전축을 따르고, 자기축과 current sheet만 회전한다.
반대 자기극 — polar cap은 항상 과 에 위치한다.
펄스 의미 — pulse toggle은 방출 강도만 바꾸고 별의 반경이나 위치를 바꾸지 않는다.
위습 방향 — 위습은 종단충격에서 바깥으로 이동하며 닫힌 고리로 고정되지 않는다.
오류 투명성 — 누락 GLSL이나 컴파일 실패는 오류 오버레이로 드러난다.
비활성 모델 차단 — legacy
pulsar.frag는 정식 loader와 UI에서 접근할 수 없다.
14. 디렉터리와 런타임 흐름
spinning-pulsar-of-the-crab-nebula/
├── index.html # Ultimate 3D 단일 진입점과 컨트롤 UI
├── style.css # fullscreen canvas와 off-canvas studio
├── README.md # 물리·수학·렌더링 문서
├── js/
│ ├── shaders.js # 명시적 GLSL URL, 모델 주입, preset registry
│ ├── renderer.js # WebGL 컴파일, uniform, 52-step render loop 구동
│ ├── controls.js # drag/zoom/toggle와 접근성 상태
│ └── main.js # 초기화와 명시적 오류 경계
└── shaders/
├── vertex.vert # fullscreen triangle pair
├── pulsar-model.glsl # 관측 상수, pulse profile, rotator frame의 단일 소유자
├── ult.frag # 정식 3D volume renderer
└── pulsar.frag # 비활성 legacy 투영; 현재 loader가 읽지 않음런타임 흐름은 다음과 같다.
index.html
→ main.js
→ shaders.js ──fetch──> vertex.vert / pulsar-model.glsl / ult.frag
→ renderer.js ─compile/link──> Ultimate WebGL program
→ controls.js ─uniform state──> renderer.jspulsar-model.glsl은 /*__PULSAR_MODEL__*/ marker에 정확히 한 번 주입된다. marker가 없거나 두 개 이상이면 초기화를 중단한다.
15. UI와 조작
입력 | 동작 |
|---|---|
마우스 드래그 | 카메라 yaw/pitch 변경 |
마우스 휠 |
|
| 자동 회전 정지/재개 |
| 제목과 FPS HUD 표시/숨김 |
| 컨트롤·GLSL 패널 열기/닫기 |
| 열린 패널 닫기 |
| 경험적 이중 피크 광도곡선 적용/해제 |
- 입력
마우스 드래그
- 동작
카메라 yaw/pitch 변경
- 입력
마우스 휠
- 동작
Field Zoom조절 (–)
- 입력
Space- 동작
자동 회전 정지/재개
- 입력
H- 동작
제목과 FPS HUD 표시/숨김
- 입력
Tab- 동작
컨트롤·GLSL 패널 열기/닫기
- 입력
Esc- 동작
열린 패널 닫기
- 입력
Pulse Modulation- 동작
경험적 이중 피크 광도곡선 적용/해제
패널의 GLSL Source 탭은 JavaScript 사본이 아니라 현재 컴파일된 ult.frag와 주입된 물리 모델을 보여준다.
16. 정직하게 밝히는 한계
완전한 MHD/PIC 시뮬레이션이 아니다. force-free 및 관측 구조에서 가져온 수학적 조건을 실시간 시각 모델로 조합한다.
공간 스케일은 비례하지 않는다. 중성자별과 종단충격을 동시에 읽기 위한 다중 시각 배율이다.
색은 선택된 팔레트다. 실제 분광 응답, 편광, Doppler spectrum을 계산하지 않는다.
는 대표값이다. 유일하게 확정된 직접 측정값처럼 해석하면 안 되는 것을 확인
광도곡선은 경험적 근사다. 두 Gaussian은 caustic 방출의 결과를 요약하며 입자 궤적을 직접 적분하지 않는다.
Kali 변조는 물리 법칙이 아니다. 잔해의 다중 스케일 공극을 만드는 절차적 밀도 도구다.
외곽 이류는 시각 시간이다. 초신성 잔해의 실제 수백 년 진화를 실시간으로 축약한 물리적 시간 적분이 아니다.
17. 참고문헌과 계보
펄사 자기권과 바람
Spitkovsky (2006), Time-dependent Force-free Pulsar Magnetospheres
Bogovalov (1999), On the physics of cold MHD winds from oblique rotators
게 성운 관측과 timing
절차적 밀도장
Kali — ShaderToy, spherical-inversion fractal 계열
Inigo Quilez, procedural noise, SDF, raymarching 참고