@guinetik/gcanvas 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- package/vitest.config.js +13 -0
|
@@ -0,0 +1,994 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sphere Shaders for WebGL Rendering
|
|
3
|
+
*
|
|
4
|
+
* These shaders render a sphere using ray-sphere intersection on a 2D quad.
|
|
5
|
+
* This approach (sometimes called "impostor" or "billboard" spheres) is very
|
|
6
|
+
* efficient as it only requires 2 triangles regardless of sphere detail.
|
|
7
|
+
*
|
|
8
|
+
* The fragment shader does the heavy lifting:
|
|
9
|
+
* 1. Ray-sphere intersection to determine if we hit the sphere
|
|
10
|
+
* 2. Normal calculation for lighting
|
|
11
|
+
* 3. UV calculation for texture/procedural patterns
|
|
12
|
+
* 4. Various procedural effects (noise, gradients, etc.)
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// VERTEX SHADER
|
|
17
|
+
// =============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Standard vertex shader for sphere impostor rendering
|
|
21
|
+
* Simply passes through position and UV coordinates
|
|
22
|
+
*/
|
|
23
|
+
export const SPHERE_VERTEX = `
|
|
24
|
+
precision highp float;
|
|
25
|
+
|
|
26
|
+
attribute vec2 aPosition;
|
|
27
|
+
attribute vec2 aUv;
|
|
28
|
+
|
|
29
|
+
varying vec2 vUv;
|
|
30
|
+
|
|
31
|
+
void main() {
|
|
32
|
+
vUv = aUv;
|
|
33
|
+
gl_Position = vec4(aPosition, 0.0, 1.0);
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
// =============================================================================
|
|
38
|
+
// COMMON SHADER FUNCTIONS
|
|
39
|
+
// =============================================================================
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Common functions included in all fragment shaders
|
|
43
|
+
* - Noise functions
|
|
44
|
+
* - Ray-sphere intersection
|
|
45
|
+
* - Lighting calculations
|
|
46
|
+
*/
|
|
47
|
+
export const SPHERE_COMMON = `
|
|
48
|
+
precision highp float;
|
|
49
|
+
|
|
50
|
+
varying vec2 vUv;
|
|
51
|
+
|
|
52
|
+
// Uniforms common to all sphere shaders
|
|
53
|
+
uniform float uTime;
|
|
54
|
+
uniform vec2 uResolution;
|
|
55
|
+
uniform vec3 uCameraRotation; // rotationX, rotationY, rotationZ
|
|
56
|
+
|
|
57
|
+
// =============================================================================
|
|
58
|
+
// NOISE FUNCTIONS
|
|
59
|
+
// =============================================================================
|
|
60
|
+
|
|
61
|
+
float hash(float n) {
|
|
62
|
+
return fract(sin(n) * 43758.5453123);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
float hash2(vec2 p) {
|
|
66
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
float hash3(vec3 p) {
|
|
70
|
+
return fract(sin(dot(p, vec3(127.1, 311.7, 74.7))) * 43758.5453123);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 3D Value noise
|
|
74
|
+
float noise3D(vec3 x) {
|
|
75
|
+
vec3 i = floor(x);
|
|
76
|
+
vec3 f = fract(x);
|
|
77
|
+
f = f * f * (3.0 - 2.0 * f);
|
|
78
|
+
|
|
79
|
+
float n = dot(i, vec3(1.0, 57.0, 113.0));
|
|
80
|
+
|
|
81
|
+
return mix(
|
|
82
|
+
mix(mix(hash(n + 0.0), hash(n + 1.0), f.x),
|
|
83
|
+
mix(hash(n + 57.0), hash(n + 58.0), f.x), f.y),
|
|
84
|
+
mix(mix(hash(n + 113.0), hash(n + 114.0), f.x),
|
|
85
|
+
mix(hash(n + 170.0), hash(n + 171.0), f.x), f.y), f.z
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// FBM (Fractional Brownian Motion)
|
|
90
|
+
float fbm(vec3 p, int octaves) {
|
|
91
|
+
float value = 0.0;
|
|
92
|
+
float amplitude = 0.5;
|
|
93
|
+
float frequency = 1.0;
|
|
94
|
+
|
|
95
|
+
for (int i = 0; i < 8; i++) {
|
|
96
|
+
if (i >= octaves) break;
|
|
97
|
+
value += amplitude * noise3D(p * frequency);
|
|
98
|
+
frequency *= 2.0;
|
|
99
|
+
amplitude *= 0.5;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return value;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// =============================================================================
|
|
106
|
+
// RAY-SPHERE INTERSECTION
|
|
107
|
+
// =============================================================================
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Ray-sphere intersection
|
|
111
|
+
* @param rayOrigin - Ray origin (camera position)
|
|
112
|
+
* @param rayDir - Normalized ray direction
|
|
113
|
+
* @param sphereCenter - Sphere center
|
|
114
|
+
* @param sphereRadius - Sphere radius
|
|
115
|
+
* @return t value for intersection, -1.0 if no hit
|
|
116
|
+
*/
|
|
117
|
+
float raySphereIntersect(vec3 rayOrigin, vec3 rayDir, vec3 sphereCenter, float sphereRadius) {
|
|
118
|
+
vec3 oc = rayOrigin - sphereCenter;
|
|
119
|
+
float a = dot(rayDir, rayDir);
|
|
120
|
+
float b = 2.0 * dot(oc, rayDir);
|
|
121
|
+
float c = dot(oc, oc) - sphereRadius * sphereRadius;
|
|
122
|
+
float discriminant = b * b - 4.0 * a * c;
|
|
123
|
+
|
|
124
|
+
if (discriminant < 0.0) {
|
|
125
|
+
return -1.0;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return (-b - sqrt(discriminant)) / (2.0 * a);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// =============================================================================
|
|
132
|
+
// CAMERA AND ROTATION
|
|
133
|
+
// =============================================================================
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Create rotation matrix from Euler angles
|
|
137
|
+
*/
|
|
138
|
+
mat3 rotationMatrix(vec3 rotation) {
|
|
139
|
+
float cx = cos(rotation.x);
|
|
140
|
+
float sx = sin(rotation.x);
|
|
141
|
+
float cy = cos(rotation.y);
|
|
142
|
+
float sy = sin(rotation.y);
|
|
143
|
+
float cz = cos(rotation.z);
|
|
144
|
+
float sz = sin(rotation.z);
|
|
145
|
+
|
|
146
|
+
mat3 rx = mat3(
|
|
147
|
+
1.0, 0.0, 0.0,
|
|
148
|
+
0.0, cx, -sx,
|
|
149
|
+
0.0, sx, cx
|
|
150
|
+
);
|
|
151
|
+
|
|
152
|
+
mat3 ry = mat3(
|
|
153
|
+
cy, 0.0, sy,
|
|
154
|
+
0.0, 1.0, 0.0,
|
|
155
|
+
-sy, 0.0, cy
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
mat3 rz = mat3(
|
|
159
|
+
cz, -sz, 0.0,
|
|
160
|
+
sz, cz, 0.0,
|
|
161
|
+
0.0, 0.0, 1.0
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
return rz * ry * rx;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Calculate ray direction from UV coordinates
|
|
169
|
+
* Uses a simple pinhole camera model
|
|
170
|
+
*/
|
|
171
|
+
vec3 getRayDirection(vec2 uv) {
|
|
172
|
+
// Convert UV to normalized device coordinates (-1 to 1)
|
|
173
|
+
vec2 ndc = uv * 2.0 - 1.0;
|
|
174
|
+
// Field of view ~53 degrees (atan(0.5) * 2)
|
|
175
|
+
return normalize(vec3(ndc * 0.5, 1.0));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// =============================================================================
|
|
179
|
+
// LIGHTING
|
|
180
|
+
// =============================================================================
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Simple diffuse + ambient lighting
|
|
184
|
+
*/
|
|
185
|
+
float lighting(vec3 normal, vec3 lightDir, float ambient) {
|
|
186
|
+
float diffuse = max(0.0, dot(normal, lightDir));
|
|
187
|
+
return ambient + (1.0 - ambient) * diffuse;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Fresnel effect for rim lighting
|
|
192
|
+
*/
|
|
193
|
+
float fresnel(vec3 normal, vec3 viewDir, float power) {
|
|
194
|
+
return pow(1.0 - abs(dot(normal, viewDir)), power);
|
|
195
|
+
}
|
|
196
|
+
`;
|
|
197
|
+
|
|
198
|
+
// =============================================================================
|
|
199
|
+
// STAR SHADER
|
|
200
|
+
// =============================================================================
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Star surface shader with plasma/fire effects, corona, and self-rotation
|
|
204
|
+
* Features:
|
|
205
|
+
* - Boiling plasma surface with spherical UV distortion
|
|
206
|
+
* - Multi-layer corona with flame structures
|
|
207
|
+
* - Hot bubbles that appear and pop
|
|
208
|
+
* - Self-rotation around axis
|
|
209
|
+
* - Temperature-based 4-tier color system
|
|
210
|
+
* - Limb darkening and organic rim glow
|
|
211
|
+
*/
|
|
212
|
+
export const STAR_FRAGMENT = `
|
|
213
|
+
${SPHERE_COMMON}
|
|
214
|
+
|
|
215
|
+
uniform vec3 uStarColor;
|
|
216
|
+
uniform float uTemperature; // Kelvin, affects color
|
|
217
|
+
uniform float uActivityLevel; // 0-1, affects turbulence
|
|
218
|
+
uniform float uRotationSpeed; // Self-rotation speed (radians/second)
|
|
219
|
+
|
|
220
|
+
// Tidal disruption uniforms
|
|
221
|
+
uniform float uTidalStretch; // 0 = sphere, 1+ = elongated toward BH
|
|
222
|
+
uniform float uStretchDirX; // Direction to black hole (X component)
|
|
223
|
+
uniform float uStretchDirZ; // Direction to black hole (Z component)
|
|
224
|
+
uniform float uStressLevel; // 0-1, surface chaos from tidal forces
|
|
225
|
+
uniform float uBaseRadius; // Dynamic base radius for proper sizing
|
|
226
|
+
uniform float uTidalFlare; // 0-1, sudden brightness burst at disruption start
|
|
227
|
+
uniform float uTidalWobble; // 0-1, violent geometry wobble during trauma
|
|
228
|
+
|
|
229
|
+
// =============================================================================
|
|
230
|
+
// TIDAL DISTORTION - True Spaghettification via Ellipsoid Deformation
|
|
231
|
+
// Uses ray-ellipsoid intersection for physically correct stretching
|
|
232
|
+
// =============================================================================
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Ray-Ellipsoid intersection
|
|
236
|
+
* Ellipsoid defined by semi-axes (a, b, c) where:
|
|
237
|
+
* - a = stretch along BH direction (in XZ plane)
|
|
238
|
+
* - b = Y axis (slight compression)
|
|
239
|
+
* - c = perpendicular to BH direction in XZ plane (compression)
|
|
240
|
+
*
|
|
241
|
+
* Technique: Transform ray into "unit sphere space" via inverse scaling
|
|
242
|
+
*/
|
|
243
|
+
float rayEllipsoidIntersect(vec3 rayOrigin, vec3 rayDir, vec3 center, vec3 semiAxes) {
|
|
244
|
+
// Scale ray into unit sphere space
|
|
245
|
+
vec3 scaledOrigin = (rayOrigin - center) / semiAxes;
|
|
246
|
+
vec3 scaledDir = rayDir / semiAxes;
|
|
247
|
+
|
|
248
|
+
// Standard ray-sphere intersection in scaled space
|
|
249
|
+
float a = dot(scaledDir, scaledDir);
|
|
250
|
+
float b = 2.0 * dot(scaledOrigin, scaledDir);
|
|
251
|
+
float c = dot(scaledOrigin, scaledOrigin) - 1.0;
|
|
252
|
+
float discriminant = b * b - 4.0 * a * c;
|
|
253
|
+
|
|
254
|
+
if (discriminant < 0.0) {
|
|
255
|
+
return -1.0;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return (-b - sqrt(discriminant)) / (2.0 * a);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Calculate ellipsoid normal at hit point
|
|
263
|
+
* Normal = gradient of ellipsoid equation = 2*(x/a², y/b², z/c²)
|
|
264
|
+
*/
|
|
265
|
+
vec3 ellipsoidNormal(vec3 hitPoint, vec3 center, vec3 semiAxes) {
|
|
266
|
+
vec3 localPos = hitPoint - center;
|
|
267
|
+
// Gradient of (x/a)² + (y/b)² + (z/c)² = 1
|
|
268
|
+
vec3 grad = localPos / (semiAxes * semiAxes);
|
|
269
|
+
return normalize(grad);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Build tidal stretch axes from BH direction
|
|
274
|
+
* Returns semi-axes (stretchAxis, Y, perpAxis) for ellipsoid
|
|
275
|
+
* Includes violent wobble effect during trauma
|
|
276
|
+
*/
|
|
277
|
+
vec3 tidalSemiAxes(float stretch, vec2 stretchDir, float baseRadius, float wobble, float time) {
|
|
278
|
+
// Stretch factor along BH direction (elongation toward/away from BH)
|
|
279
|
+
float stretchFactor = 1.0 + stretch * 0.8; // Up to 1.8x longer
|
|
280
|
+
|
|
281
|
+
// Compression factor perpendicular (volume roughly conserved)
|
|
282
|
+
float compressFactor = 1.0 / sqrt(stretchFactor); // Compress to conserve volume
|
|
283
|
+
|
|
284
|
+
// Y axis gets slight compression too
|
|
285
|
+
float yFactor = 1.0 - stretch * 0.15;
|
|
286
|
+
|
|
287
|
+
// === TRAUMA WOBBLE ===
|
|
288
|
+
// Violent, chaotic geometry distortion during tidal shock
|
|
289
|
+
if (wobble > 0.01) {
|
|
290
|
+
// Multiple frequency wobbles for organic chaos
|
|
291
|
+
float wobble1 = sin(time * 12.0) * cos(time * 7.3);
|
|
292
|
+
float wobble2 = sin(time * 19.0 + 1.5) * cos(time * 11.0);
|
|
293
|
+
float wobble3 = sin(time * 8.0 + 3.0);
|
|
294
|
+
|
|
295
|
+
// Asymmetric wobble - more violent on stretch axis
|
|
296
|
+
float stretchWobble = wobble * (0.3 + wobble1 * 0.2 + wobble2 * 0.15);
|
|
297
|
+
float yWobble = wobble * (wobble2 * 0.25 + wobble3 * 0.15);
|
|
298
|
+
float perpWobble = wobble * (wobble3 * 0.2 + wobble1 * 0.1);
|
|
299
|
+
|
|
300
|
+
stretchFactor *= (1.0 + stretchWobble);
|
|
301
|
+
yFactor *= (1.0 + yWobble);
|
|
302
|
+
compressFactor *= (1.0 + perpWobble);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
return vec3(
|
|
306
|
+
baseRadius * stretchFactor, // Stretch along BH radial
|
|
307
|
+
baseRadius * yFactor, // Slight Y compression
|
|
308
|
+
baseRadius * compressFactor // Compress perpendicular
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// =============================================================================
|
|
313
|
+
// PLASMA NOISE with flowing distortion
|
|
314
|
+
// =============================================================================
|
|
315
|
+
|
|
316
|
+
float plasmaNoise(vec3 p, float time) {
|
|
317
|
+
float value = 0.0;
|
|
318
|
+
float amplitude = 1.0;
|
|
319
|
+
float frequency = 1.0;
|
|
320
|
+
float totalAmp = 0.0;
|
|
321
|
+
|
|
322
|
+
for (int i = 0; i < 5; i++) {
|
|
323
|
+
vec3 offset = vec3(
|
|
324
|
+
sin(time * 0.1 + float(i)) * 0.5,
|
|
325
|
+
cos(time * 0.15 + float(i) * 0.7) * 0.5,
|
|
326
|
+
time * 0.05
|
|
327
|
+
);
|
|
328
|
+
value += amplitude * noise3D((p + offset) * frequency);
|
|
329
|
+
totalAmp += amplitude;
|
|
330
|
+
amplitude *= 0.5;
|
|
331
|
+
frequency *= 2.0;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
return value / totalAmp;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// =============================================================================
|
|
338
|
+
// HOT BUBBLES - bright spots that appear and pop
|
|
339
|
+
// =============================================================================
|
|
340
|
+
|
|
341
|
+
float hotBubbles(vec3 p, float time) {
|
|
342
|
+
// Large slow bubbles
|
|
343
|
+
vec3 p1 = p * 5.0 + vec3(0.0, time * 0.06, 0.0);
|
|
344
|
+
float b1 = noise3D(p1);
|
|
345
|
+
b1 = smoothstep(0.3, 0.6, b1);
|
|
346
|
+
|
|
347
|
+
// Medium bubbles, faster
|
|
348
|
+
vec3 p2 = p * 9.0 + vec3(time * 0.04, time * 0.08, 0.0);
|
|
349
|
+
float b2 = noise3D(p2);
|
|
350
|
+
b2 = smoothstep(0.35, 0.65, b2);
|
|
351
|
+
|
|
352
|
+
// Small rapid bubbles
|
|
353
|
+
vec3 p3 = p * 16.0 + vec3(time * 0.1, 0.0, time * 0.12);
|
|
354
|
+
float b3 = noise3D(p3);
|
|
355
|
+
b3 = smoothstep(0.4, 0.7, b3);
|
|
356
|
+
|
|
357
|
+
float bubbles = b1 * 0.5 + b2 * 0.35 + b3 * 0.15;
|
|
358
|
+
float pulse = sin(time * 2.0 + p.x * 10.0) * 0.3 + 0.7;
|
|
359
|
+
|
|
360
|
+
return bubbles * pulse;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// =============================================================================
|
|
364
|
+
// BOILING TURBULENCE - fast chaotic movement
|
|
365
|
+
// =============================================================================
|
|
366
|
+
|
|
367
|
+
float boilingTurbulence(vec3 p, float time) {
|
|
368
|
+
float turb = 0.0;
|
|
369
|
+
float amp = 1.0;
|
|
370
|
+
float freq = 4.0;
|
|
371
|
+
|
|
372
|
+
for (int i = 0; i < 4; i++) {
|
|
373
|
+
vec3 offset = vec3(
|
|
374
|
+
sin(time * 0.3 + float(i) * 1.7) * 0.5,
|
|
375
|
+
cos(time * 0.25 + float(i) * 2.3) * 0.5,
|
|
376
|
+
time * 0.15 * (1.0 + float(i) * 0.3)
|
|
377
|
+
);
|
|
378
|
+
turb += amp * abs(noise3D(p * freq + offset));
|
|
379
|
+
amp *= 0.5;
|
|
380
|
+
freq *= 2.1;
|
|
381
|
+
}
|
|
382
|
+
return turb;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
// =============================================================================
|
|
386
|
+
// CORONA FLAMES - structures around the edge
|
|
387
|
+
// =============================================================================
|
|
388
|
+
|
|
389
|
+
float coronaFlames(float angle, float rimFactor, float time, float activity) {
|
|
390
|
+
// Multiple flame frequencies
|
|
391
|
+
float flames = 0.0;
|
|
392
|
+
|
|
393
|
+
// Large slow flames
|
|
394
|
+
float f1 = sin(angle * 5.0 + time * 0.5) * 0.5 + 0.5;
|
|
395
|
+
f1 *= noise3D(vec3(angle * 2.0, time * 0.3, 0.0));
|
|
396
|
+
|
|
397
|
+
// Medium flames
|
|
398
|
+
float f2 = sin(angle * 12.0 + time * 0.8) * 0.5 + 0.5;
|
|
399
|
+
f2 *= noise3D(vec3(angle * 4.0, time * 0.5, 5.0));
|
|
400
|
+
|
|
401
|
+
// Small rapid flames
|
|
402
|
+
float f3 = sin(angle * 25.0 + time * 1.5) * 0.5 + 0.5;
|
|
403
|
+
f3 *= noise3D(vec3(angle * 8.0, time * 0.8, 10.0));
|
|
404
|
+
|
|
405
|
+
flames = f1 * 0.5 + f2 * 0.3 + f3 * 0.2;
|
|
406
|
+
|
|
407
|
+
// Flames only visible at rim
|
|
408
|
+
flames *= pow(rimFactor, 1.5);
|
|
409
|
+
flames *= 0.5 + activity * 0.5;
|
|
410
|
+
|
|
411
|
+
return flames;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// =============================================================================
|
|
415
|
+
// SELF ROTATION - rotate normal around Y axis
|
|
416
|
+
// =============================================================================
|
|
417
|
+
|
|
418
|
+
vec3 rotateY(vec3 v, float angle) {
|
|
419
|
+
float c = cos(angle);
|
|
420
|
+
float s = sin(angle);
|
|
421
|
+
return vec3(v.x * c + v.z * s, v.y, -v.x * s + v.z * c);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
void main() {
|
|
425
|
+
// === CIRCULAR MASK - prevents square canvas artifacts ===
|
|
426
|
+
vec2 center = vUv - 0.5;
|
|
427
|
+
float distFromCenter = length(center) * 2.0;
|
|
428
|
+
|
|
429
|
+
// Wider cutoff for stretched ellipsoid
|
|
430
|
+
if (distFromCenter > 1.6) {
|
|
431
|
+
gl_FragColor = vec4(0.0);
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
float circularMask = 1.0 - smoothstep(1.3, 1.6, distFromCenter);
|
|
436
|
+
|
|
437
|
+
// Setup ray - camera looking at sphere from fixed position
|
|
438
|
+
vec3 rayOrigin = vec3(0.0, 0.0, -2.5);
|
|
439
|
+
vec3 rayDir = getRayDirection(vUv);
|
|
440
|
+
|
|
441
|
+
float time = uTime;
|
|
442
|
+
float selfRotation = time * uRotationSpeed;
|
|
443
|
+
|
|
444
|
+
// === TIDAL ELLIPSOID SETUP ===
|
|
445
|
+
// Direction toward black hole in XZ plane
|
|
446
|
+
vec2 stretchDir2D = normalize(vec2(uStretchDirX, uStretchDirZ) + 0.0001);
|
|
447
|
+
float stretch = uTidalStretch;
|
|
448
|
+
|
|
449
|
+
// Build rotation matrix to align ellipsoid X-axis with stretch direction
|
|
450
|
+
// This rotates the ellipsoid so its long axis points toward the BH
|
|
451
|
+
float stretchAngle = atan(stretchDir2D.y, stretchDir2D.x);
|
|
452
|
+
float cs = cos(stretchAngle);
|
|
453
|
+
float sn = sin(stretchAngle);
|
|
454
|
+
|
|
455
|
+
// Rotation matrix around Y axis (to align stretch in XZ plane)
|
|
456
|
+
mat3 stretchRot = mat3(
|
|
457
|
+
cs, 0.0, -sn,
|
|
458
|
+
0.0, 1.0, 0.0,
|
|
459
|
+
sn, 0.0, cs
|
|
460
|
+
);
|
|
461
|
+
mat3 stretchRotInv = mat3(
|
|
462
|
+
cs, 0.0, sn,
|
|
463
|
+
0.0, 1.0, 0.0,
|
|
464
|
+
-sn, 0.0, cs
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
// Transform ray into ellipsoid-aligned space
|
|
468
|
+
vec3 rotatedRayDir = stretchRotInv * rayDir;
|
|
469
|
+
vec3 rotatedRayOrigin = stretchRotInv * rayOrigin;
|
|
470
|
+
|
|
471
|
+
// Calculate ellipsoid semi-axes based on stretch
|
|
472
|
+
// Use dynamic base radius passed from JS (scales with render texture size)
|
|
473
|
+
// Falls back to 0.4 if uniform not set
|
|
474
|
+
float baseRadius = uBaseRadius > 0.0 ? uBaseRadius : 0.4;
|
|
475
|
+
vec3 semiAxes = tidalSemiAxes(stretch, stretchDir2D, baseRadius, uTidalWobble, time);
|
|
476
|
+
|
|
477
|
+
// Ray-ellipsoid intersection for SURFACE
|
|
478
|
+
float t = rayEllipsoidIntersect(rotatedRayOrigin, rotatedRayDir, vec3(0.0), semiAxes);
|
|
479
|
+
|
|
480
|
+
// === NO CORONA - just render solid ellipsoid ===
|
|
481
|
+
// If ray doesn't hit the surface, render transparent
|
|
482
|
+
if (t < 0.0) {
|
|
483
|
+
gl_FragColor = vec4(0.0);
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
// === SURFACE RENDERING ===
|
|
488
|
+
// Calculate hit point in rotated (ellipsoid-aligned) space
|
|
489
|
+
vec3 rotatedHitPoint = rotatedRayOrigin + rotatedRayDir * t;
|
|
490
|
+
|
|
491
|
+
// Calculate ellipsoid normal (gradient of implicit surface)
|
|
492
|
+
vec3 rotatedNormalRaw = ellipsoidNormal(rotatedHitPoint, vec3(0.0), semiAxes);
|
|
493
|
+
|
|
494
|
+
// Transform hit point and normal back to world space
|
|
495
|
+
vec3 hitPoint = stretchRot * rotatedHitPoint;
|
|
496
|
+
vec3 normal = normalize(stretchRot * rotatedNormalRaw);
|
|
497
|
+
|
|
498
|
+
// Apply inverse camera rotation to the normal (camera orbit)
|
|
499
|
+
mat3 camRotMat = rotationMatrix(-uCameraRotation);
|
|
500
|
+
vec3 rotatedNormal = camRotMat * normal;
|
|
501
|
+
|
|
502
|
+
// Apply self-rotation to surface features
|
|
503
|
+
rotatedNormal = rotateY(rotatedNormal, selfRotation);
|
|
504
|
+
|
|
505
|
+
// === SPHERICAL DISTORTION for boiling effect ===
|
|
506
|
+
vec2 sp = normal.xy;
|
|
507
|
+
float r = dot(sp, sp);
|
|
508
|
+
|
|
509
|
+
float brightness = 0.15 + (uTemperature / 10000.0) * 0.1;
|
|
510
|
+
float distortStrength = 2.0 - brightness;
|
|
511
|
+
|
|
512
|
+
vec2 warpedUV;
|
|
513
|
+
if (r < 0.0001) {
|
|
514
|
+
// At pole - use alternative coords
|
|
515
|
+
float poleAngle = atan(rotatedNormal.y, rotatedNormal.x) + time * 0.15;
|
|
516
|
+
float poleElev = acos(clamp(rotatedNormal.z, -1.0, 1.0));
|
|
517
|
+
warpedUV = vec2(cos(poleAngle), sin(poleAngle)) * (poleElev / 3.14159) * distortStrength;
|
|
518
|
+
} else {
|
|
519
|
+
sp *= distortStrength;
|
|
520
|
+
r = dot(sp, sp);
|
|
521
|
+
float f = (1.0 - sqrt(abs(1.0 - r))) / (r + 0.001) + brightness * 0.5;
|
|
522
|
+
warpedUV = sp * f + vec2(time * 0.05, 0.0);
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
// === PLASMA TEXTURE ===
|
|
526
|
+
vec3 plasmaCoord = vec3(warpedUV * 3.0, time * 0.12);
|
|
527
|
+
float plasma1 = plasmaNoise(plasmaCoord, time);
|
|
528
|
+
float plasma2 = plasmaNoise(plasmaCoord * 1.3 + vec3(50.0), time * 1.2);
|
|
529
|
+
float plasma = plasma1 * 0.6 + plasma2 * 0.4;
|
|
530
|
+
plasma = plasma * 0.5 + 0.5;
|
|
531
|
+
|
|
532
|
+
// === VIEW GEOMETRY ===
|
|
533
|
+
float viewAngle = dot(normal, -rayDir);
|
|
534
|
+
float edgeDist = 1.0 - viewAngle;
|
|
535
|
+
float limbDarkening = pow(max(0.0, viewAngle), 0.4);
|
|
536
|
+
|
|
537
|
+
// === TIDAL FACE INTENSITY ===
|
|
538
|
+
// The side facing the black hole experiences more violent tidal forces
|
|
539
|
+
// Calculate how much this surface point faces the BH direction
|
|
540
|
+
vec3 bhDir3D = normalize(vec3(uStretchDirX, 0.0, uStretchDirZ));
|
|
541
|
+
float facingBH = dot(normal, bhDir3D); // -1 to 1, positive = facing BH
|
|
542
|
+
float tidalFace = smoothstep(-0.2, 0.8, facingBH); // Gradual transition
|
|
543
|
+
tidalFace = tidalFace * tidalFace; // More concentrated on BH side
|
|
544
|
+
|
|
545
|
+
// Tidal face boost - up to 3x more violent on the BH-facing side
|
|
546
|
+
float tidalFaceBoost = 1.0 + tidalFace * uStressLevel * 2.0;
|
|
547
|
+
|
|
548
|
+
// === MULTI-LAYER EFFECTS (stress-enhanced) ===
|
|
549
|
+
// Stress amplifies all turbulent effects - star is being torn apart!
|
|
550
|
+
// Much more violent - up to 5x chaos at max stress
|
|
551
|
+
float stressBoost = 1.0 + uStressLevel * 4.0;
|
|
552
|
+
|
|
553
|
+
// Combined boost: general stress + extra violence on BH-facing side
|
|
554
|
+
float combinedBoost = stressBoost * tidalFaceBoost;
|
|
555
|
+
|
|
556
|
+
float turbIntensity = boilingTurbulence(rotatedNormal, time * combinedBoost) * 0.6;
|
|
557
|
+
turbIntensity *= combinedBoost;
|
|
558
|
+
|
|
559
|
+
float bubbles = hotBubbles(rotatedNormal, time * combinedBoost);
|
|
560
|
+
bubbles *= combinedBoost * 1.5; // More dramatic bubbles on tidal face
|
|
561
|
+
|
|
562
|
+
// Granulation becomes violent under stress - larger and faster
|
|
563
|
+
float gran = noise3D(rotatedNormal * 15.0 + time * 0.5 * combinedBoost);
|
|
564
|
+
gran *= combinedBoost * 1.2;
|
|
565
|
+
|
|
566
|
+
// === TIDAL FRACTURING ===
|
|
567
|
+
// Stress causes visible cracks/tears - concentrated on BH-facing side
|
|
568
|
+
float fractures = 0.0;
|
|
569
|
+
if (uStressLevel > 0.15) { // Start fractures earlier (was 0.3)
|
|
570
|
+
// Fractures are more intense on the tidal face
|
|
571
|
+
float fractureBoost = 1.0 + tidalFace * 2.0; // Up to 3x on BH side
|
|
572
|
+
float fractureNoise = noise3D(rotatedNormal * 6.0 + time * 0.8 * fractureBoost);
|
|
573
|
+
float fractureThreshold = 1.0 - (uStressLevel - 0.15) * 1.2 * fractureBoost;
|
|
574
|
+
fractures = smoothstep(fractureThreshold, fractureThreshold + 0.08, fractureNoise);
|
|
575
|
+
fractures *= uStressLevel * 1.2 * fractureBoost; // More intense on BH side
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// === PULSATION (amplified by stress) ===
|
|
579
|
+
float pulse1 = cos(time * 0.5) * 0.5;
|
|
580
|
+
float pulse2 = sin(time * 0.25) * 0.5;
|
|
581
|
+
float pulseAmp = uActivityLevel * (1.0 + uStressLevel);
|
|
582
|
+
float pulse = (pulse1 + pulse2) * 0.3 * pulseAmp;
|
|
583
|
+
|
|
584
|
+
// === TIDAL HOTSPOT ===
|
|
585
|
+
// Bright glowing region on the BH-facing side - like matter being pulled off
|
|
586
|
+
float tidalHotspot = pow(tidalFace, 3.0) * uStressLevel;
|
|
587
|
+
// Add some flickering/chaos to the hotspot
|
|
588
|
+
tidalHotspot *= 0.7 + 0.3 * noise3D(rotatedNormal * 8.0 + time * 2.0);
|
|
589
|
+
|
|
590
|
+
// === COMBINED INTENSITY ===
|
|
591
|
+
float totalIntensity = plasma * 0.35 + turbIntensity * 0.25 + gran * 0.2;
|
|
592
|
+
totalIntensity += bubbles * 0.4;
|
|
593
|
+
totalIntensity += fractures * 0.5; // Fractures glow hot
|
|
594
|
+
totalIntensity += tidalHotspot * 0.8; // Bright tidal hotspot
|
|
595
|
+
totalIntensity *= 1.0 + pulse;
|
|
596
|
+
|
|
597
|
+
// === 4-TIER COLOR SYSTEM ===
|
|
598
|
+
vec3 baseColor = uStarColor;
|
|
599
|
+
float maxComp = max(baseColor.r, max(baseColor.g, baseColor.b));
|
|
600
|
+
if (maxComp > 0.01) baseColor = baseColor / maxComp * 0.85;
|
|
601
|
+
|
|
602
|
+
// Temperature-based color blending
|
|
603
|
+
float tempBlend = smoothstep(5000.0, 7500.0, uTemperature);
|
|
604
|
+
|
|
605
|
+
vec3 hotColor = baseColor * vec3(1.6, 1.35, 1.2);
|
|
606
|
+
vec3 coolColor = mix(baseColor * vec3(0.5, 0.3, 0.2), baseColor * vec3(0.7, 0.8, 0.95), tempBlend);
|
|
607
|
+
vec3 warmColor = mix(baseColor * vec3(1.2, 1.0, 0.85), baseColor * vec3(1.0, 1.05, 1.2), tempBlend);
|
|
608
|
+
vec3 blazingColor = mix(baseColor * vec3(2.0, 1.6, 1.3), baseColor * vec3(1.4, 1.5, 1.8), tempBlend);
|
|
609
|
+
|
|
610
|
+
// Map intensity to color
|
|
611
|
+
vec3 surfaceColor;
|
|
612
|
+
if (totalIntensity < 0.35) {
|
|
613
|
+
surfaceColor = mix(coolColor, warmColor, totalIntensity / 0.35);
|
|
614
|
+
} else if (totalIntensity < 0.65) {
|
|
615
|
+
surfaceColor = mix(warmColor, hotColor, (totalIntensity - 0.35) / 0.3);
|
|
616
|
+
} else if (totalIntensity < 1.0) {
|
|
617
|
+
surfaceColor = mix(hotColor, blazingColor, (totalIntensity - 0.65) / 0.35);
|
|
618
|
+
} else {
|
|
619
|
+
surfaceColor = blazingColor * (1.0 + (totalIntensity - 1.0) * 0.8);
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// Bubble highlights
|
|
623
|
+
float bubbleHighlight = pow(bubbles, 1.5) * turbIntensity;
|
|
624
|
+
surfaceColor += blazingColor * bubbleHighlight * 0.6;
|
|
625
|
+
|
|
626
|
+
// === LIMB DARKENING ===
|
|
627
|
+
surfaceColor *= 0.75 + limbDarkening * 0.25;
|
|
628
|
+
|
|
629
|
+
// === ORGANIC RIM GLOW ===
|
|
630
|
+
float rimAngle = atan(normal.y, normal.x) + selfRotation;
|
|
631
|
+
float rimNoise = noise3D(vec3(rimAngle * 3.0, edgeDist * 2.0, time * 0.2));
|
|
632
|
+
rimNoise = rimNoise * 0.5 + 0.5;
|
|
633
|
+
|
|
634
|
+
float rimIntensity = pow(edgeDist, 2.0) * (0.4 + rimNoise * 0.6);
|
|
635
|
+
vec3 rimColor = baseColor * vec3(1.3, 0.95, 0.6);
|
|
636
|
+
surfaceColor += rimColor * rimIntensity * 0.6 * uActivityLevel;
|
|
637
|
+
|
|
638
|
+
// === EDGE GLOW (corona bleeding into surface) ===
|
|
639
|
+
float edgeGlow = pow(edgeDist, 0.5) * 0.3 * uActivityLevel;
|
|
640
|
+
surfaceColor += warmColor * edgeGlow;
|
|
641
|
+
|
|
642
|
+
// === CENTER BOOST ===
|
|
643
|
+
float centerBoost = pow(viewAngle, 1.5) * 0.2;
|
|
644
|
+
surfaceColor += baseColor * centerBoost;
|
|
645
|
+
|
|
646
|
+
// === SHIMMER ===
|
|
647
|
+
float shimmer = sin(turbIntensity * 10.0 + time * 3.0) * 0.05 + 1.0;
|
|
648
|
+
surfaceColor *= shimmer;
|
|
649
|
+
|
|
650
|
+
// === TIDAL FLARE ===
|
|
651
|
+
// Sudden brightness burst when disruption begins
|
|
652
|
+
// Concentrated on the BH-facing side with violent flickering
|
|
653
|
+
if (uTidalFlare > 0.01) {
|
|
654
|
+
// Flare is brightest on the BH-facing side
|
|
655
|
+
float flareFace = 0.3 + tidalFace * 0.7;
|
|
656
|
+
|
|
657
|
+
// Violent flickering during the flare
|
|
658
|
+
float flareFlicker = 0.7 + 0.3 * noise3D(rotatedNormal * 10.0 + time * 8.0);
|
|
659
|
+
|
|
660
|
+
// White-hot flare color
|
|
661
|
+
vec3 flareColor = vec3(1.0, 0.95, 0.8);
|
|
662
|
+
|
|
663
|
+
// Additive flare - makes entire star brighter
|
|
664
|
+
float flareIntensity = uTidalFlare * flareFace * flareFlicker * 2.0;
|
|
665
|
+
surfaceColor += flareColor * flareIntensity;
|
|
666
|
+
|
|
667
|
+
// Extra bloom at the BH-facing tip
|
|
668
|
+
float tipFlare = pow(tidalFace, 4.0) * uTidalFlare * 1.5;
|
|
669
|
+
surfaceColor += vec3(1.0, 0.9, 0.7) * tipFlare;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
surfaceColor = clamp(surfaceColor, 0.0, 3.5); // Allow brighter for flare
|
|
673
|
+
|
|
674
|
+
gl_FragColor = vec4(surfaceColor, 1.0);
|
|
675
|
+
}
|
|
676
|
+
`;
|
|
677
|
+
|
|
678
|
+
// =============================================================================
|
|
679
|
+
// BLACK HOLE SHADER
|
|
680
|
+
// =============================================================================
|
|
681
|
+
|
|
682
|
+
/**
|
|
683
|
+
* Black hole shader - subtle, physics-based with gravitational lensing
|
|
684
|
+
*
|
|
685
|
+
* Design philosophy:
|
|
686
|
+
* - Dormant: Nearly invisible, just a dark void with subtle edge
|
|
687
|
+
* - Main effect is gravitational lensing of background starfield
|
|
688
|
+
* - Only shows glow effects when awakened/feeding
|
|
689
|
+
* - The accretion disk handles most visual drama (separate component)
|
|
690
|
+
*/
|
|
691
|
+
export const BLACK_HOLE_FRAGMENT = `
|
|
692
|
+
${SPHERE_COMMON}
|
|
693
|
+
|
|
694
|
+
uniform float uAwakeningLevel; // 0 = dormant, 1 = fully active
|
|
695
|
+
uniform float uFeedingPulse; // Temporary glow from feeding
|
|
696
|
+
uniform float uRotation; // Black hole spin angle (Kerr rotation)
|
|
697
|
+
|
|
698
|
+
void main() {
|
|
699
|
+
vec2 uv = vUv;
|
|
700
|
+
vec2 center = uv - 0.5;
|
|
701
|
+
float dist = length(center) * 2.0; // 0 at center, 1 at edge
|
|
702
|
+
float angle = atan(center.y, center.x);
|
|
703
|
+
|
|
704
|
+
float time = uTime;
|
|
705
|
+
float awakeFactor = uAwakeningLevel;
|
|
706
|
+
float pulseFactor = uFeedingPulse;
|
|
707
|
+
|
|
708
|
+
// Spin angle for rotating effects (frame dragging)
|
|
709
|
+
// Using uTime since custom uniforms don't update properly
|
|
710
|
+
float spinAngle = angle + uTime * 4.0; // Faster spin
|
|
711
|
+
|
|
712
|
+
// === RADII (normalized to quad size) ===
|
|
713
|
+
float eventHorizon = 0.42; // Slightly larger core
|
|
714
|
+
float photonSphere = 0.54; // Tighten ring closer to horizon
|
|
715
|
+
float shadowEdge = 0.5; // Shadow boundary
|
|
716
|
+
|
|
717
|
+
// === CIRCULAR MASK ===
|
|
718
|
+
if (dist > 1.5) {
|
|
719
|
+
gl_FragColor = vec4(0.0);
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
// === NO INTERNAL STARFIELD ===
|
|
724
|
+
// The real starfield is rendered separately in the scene
|
|
725
|
+
// This shader just renders the dark void + subtle edge effects
|
|
726
|
+
// True gravitational lensing would require render-to-texture of background
|
|
727
|
+
|
|
728
|
+
// === EVENT HORIZON - Gradient from pure black to very dark edge ===
|
|
729
|
+
// Edge color ~#110b06 = RGB(17,11,6) = vec3(0.067, 0.043, 0.024)
|
|
730
|
+
if (dist < shadowEdge) {
|
|
731
|
+
// Use shadowEdge (0.52) as outer boundary for smooth transition to ring
|
|
732
|
+
float edgeT = dist / shadowEdge; // 0 at center, 1 at shadow edge
|
|
733
|
+
|
|
734
|
+
// Very steep curve - stays pure black until very close to edge
|
|
735
|
+
float glowFactor = pow(edgeT, 8.0);
|
|
736
|
+
|
|
737
|
+
// Very dark brownish-black - NO yellow, matches #110b06
|
|
738
|
+
vec3 edgeColor = vec3(0.067, 0.043, 0.024) * glowFactor;
|
|
739
|
+
|
|
740
|
+
gl_FragColor = vec4(edgeColor, 1.0);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
// === PHOTON SPHERE - Subtle ring with gentler spin asymmetry ===
|
|
745
|
+
float photonRingWidth = 0.035;
|
|
746
|
+
float photonDist = abs(dist - photonSphere);
|
|
747
|
+
float photonRing = exp(-photonDist * photonDist / (photonRingWidth * photonRingWidth));
|
|
748
|
+
|
|
749
|
+
// Softer Doppler asymmetry to avoid pointy highlights
|
|
750
|
+
float doppler = 0.78 + 0.22 * cos(spinAngle); // narrower asymmetry
|
|
751
|
+
photonRing *= 0.18 + doppler * 0.38; // 18%..56% brightness
|
|
752
|
+
|
|
753
|
+
// Soft tip highlight to indicate spin without a spike
|
|
754
|
+
float tipAlign = max(0.0, cos(spinAngle));
|
|
755
|
+
float tipRadial = smoothstep(photonRingWidth * 1.2, 0.0, photonDist);
|
|
756
|
+
float hotSpotGlow = tipAlign * tipAlign * tipRadial * 0.25;
|
|
757
|
+
|
|
758
|
+
// Scale with awakening - more visible when feeding
|
|
759
|
+
photonRing *= 0.15 + awakeFactor * 0.35;
|
|
760
|
+
|
|
761
|
+
// === FEEDING PULSE - Subtle ripple when consuming ===
|
|
762
|
+
float pulseRipple = 0.0;
|
|
763
|
+
if (pulseFactor > 0.01) {
|
|
764
|
+
float ripplePhase = fract(time * 1.5) * 0.3;
|
|
765
|
+
float rippleRadius = shadowEdge + ripplePhase;
|
|
766
|
+
float ripple = exp(-pow(dist - rippleRadius, 2.0) * 80.0);
|
|
767
|
+
pulseRipple = ripple * pulseFactor * 0.15; // Subtle
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// === EDGE GLOW - keep subtle; avoid wide smear
|
|
771
|
+
float edgeGlow = 0.0;
|
|
772
|
+
if (dist > shadowEdge && dist < photonSphere + 0.08) {
|
|
773
|
+
float edgeFactor = smoothstep(shadowEdge, photonSphere, dist);
|
|
774
|
+
edgeFactor *= smoothstep(photonSphere + 0.08, photonSphere, dist);
|
|
775
|
+
edgeGlow = edgeFactor * pulseFactor * 0.06;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
// === COMBINE EFFECTS ===
|
|
779
|
+
vec3 color = vec3(0.0);
|
|
780
|
+
|
|
781
|
+
// Photon sphere ring (warm orange-yellow)
|
|
782
|
+
vec3 photonColor = vec3(1.0, 0.8, 0.45);
|
|
783
|
+
color += photonColor * photonRing;
|
|
784
|
+
|
|
785
|
+
// Soft tip highlight (spin indicator)
|
|
786
|
+
vec3 hotSpotColor = vec3(1.0, 0.9, 0.65);
|
|
787
|
+
color += hotSpotColor * hotSpotGlow;
|
|
788
|
+
|
|
789
|
+
// Edge glow when feeding
|
|
790
|
+
vec3 glowColor = vec3(1.0, 0.5, 0.2);
|
|
791
|
+
color += glowColor * edgeGlow;
|
|
792
|
+
|
|
793
|
+
// Feeding pulse ripple
|
|
794
|
+
vec3 pulseColor = vec3(1.0, 0.6, 0.3);
|
|
795
|
+
color += pulseColor * pulseRipple;
|
|
796
|
+
|
|
797
|
+
// === OUTER FADE ===
|
|
798
|
+
float outerFade = 1.0 - smoothstep(0.9, 1.25, dist);
|
|
799
|
+
color *= outerFade;
|
|
800
|
+
|
|
801
|
+
// === ALPHA ===
|
|
802
|
+
// Event horizon and photon sphere are fully opaque to occlude background
|
|
803
|
+
float alpha;
|
|
804
|
+
if (dist < photonSphere) {
|
|
805
|
+
alpha = 1.0;
|
|
806
|
+
color = (dist < shadowEdge) ? vec3(0.0) : color; // keep core solid black
|
|
807
|
+
} else {
|
|
808
|
+
// Alpha based on visible content
|
|
809
|
+
float contentBrightness = max(max(color.r, color.g), color.b);
|
|
810
|
+
alpha = smoothstep(0.01, 0.06, contentBrightness);
|
|
811
|
+
alpha = max(alpha, smoothstep(photonSphere + 0.12, shadowEdge, dist) * 0.25);
|
|
812
|
+
alpha *= outerFade;
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
gl_FragColor = vec4(color, alpha);
|
|
816
|
+
}
|
|
817
|
+
`;
|
|
818
|
+
|
|
819
|
+
// =============================================================================
|
|
820
|
+
// ROCKY PLANET SHADER
|
|
821
|
+
// =============================================================================
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Rocky planet shader with terrain and atmosphere
|
|
825
|
+
*/
|
|
826
|
+
export const ROCKY_PLANET_FRAGMENT = `
|
|
827
|
+
${SPHERE_COMMON}
|
|
828
|
+
|
|
829
|
+
uniform vec3 uBaseColor;
|
|
830
|
+
uniform float uHasAtmosphere; // 0-1
|
|
831
|
+
uniform float uSeed;
|
|
832
|
+
|
|
833
|
+
void main() {
|
|
834
|
+
// Setup ray - camera looking at sphere from fixed position
|
|
835
|
+
vec3 rayOrigin = vec3(0.0, 0.0, -2.5);
|
|
836
|
+
vec3 rayDir = getRayDirection(vUv);
|
|
837
|
+
|
|
838
|
+
// Ray-sphere intersection (sphere at origin with radius 0.5)
|
|
839
|
+
float t = raySphereIntersect(rayOrigin, rayDir, vec3(0.0), 0.5);
|
|
840
|
+
|
|
841
|
+
if (t < 0.0) {
|
|
842
|
+
// Atmosphere halo
|
|
843
|
+
if (uHasAtmosphere > 0.0) {
|
|
844
|
+
vec2 center = vUv - 0.5;
|
|
845
|
+
float dist = length(center) * 2.0;
|
|
846
|
+
float atmo = smoothstep(0.6, 0.5, dist) * smoothstep(0.45, 0.52, dist);
|
|
847
|
+
atmo *= uHasAtmosphere * 0.4;
|
|
848
|
+
vec3 atmoColor = vec3(0.5, 0.7, 1.0) * atmo;
|
|
849
|
+
// Premultiplied alpha
|
|
850
|
+
gl_FragColor = vec4(atmoColor * atmo, atmo);
|
|
851
|
+
} else {
|
|
852
|
+
gl_FragColor = vec4(0.0);
|
|
853
|
+
}
|
|
854
|
+
return;
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
// Calculate hit point and normal
|
|
858
|
+
vec3 hitPoint = rayOrigin + rayDir * t;
|
|
859
|
+
vec3 normal = normalize(hitPoint);
|
|
860
|
+
|
|
861
|
+
// Apply inverse camera rotation for surface features
|
|
862
|
+
mat3 rotMat = rotationMatrix(-uCameraRotation);
|
|
863
|
+
vec3 rotatedNormal = rotMat * normal;
|
|
864
|
+
|
|
865
|
+
// Seeded noise for consistent terrain
|
|
866
|
+
vec3 noiseCoord = rotatedNormal * 4.0 + uSeed * 100.0;
|
|
867
|
+
float terrain = fbm(noiseCoord, 5);
|
|
868
|
+
|
|
869
|
+
// Height-based coloring
|
|
870
|
+
vec3 lowColor = uBaseColor * 0.6; // Valleys/lowlands
|
|
871
|
+
vec3 highColor = uBaseColor * 1.2; // Mountains/highlands
|
|
872
|
+
vec3 surfaceColor = mix(lowColor, highColor, terrain);
|
|
873
|
+
|
|
874
|
+
// Add some variation
|
|
875
|
+
float variation = noise3D(rotatedNormal * 10.0 + uSeed * 50.0);
|
|
876
|
+
surfaceColor *= 0.9 + variation * 0.2;
|
|
877
|
+
|
|
878
|
+
// Lighting
|
|
879
|
+
vec3 lightDir = normalize(vec3(1.0, 1.0, 0.5));
|
|
880
|
+
float light = lighting(normal, lightDir, 0.3);
|
|
881
|
+
surfaceColor *= light;
|
|
882
|
+
|
|
883
|
+
// Atmosphere scattering at edges
|
|
884
|
+
if (uHasAtmosphere > 0.0) {
|
|
885
|
+
float rim = fresnel(normal, -rayDir, 3.0);
|
|
886
|
+
vec3 atmoColor = vec3(0.5, 0.7, 1.0);
|
|
887
|
+
surfaceColor = mix(surfaceColor, atmoColor, rim * uHasAtmosphere * 0.4);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
gl_FragColor = vec4(surfaceColor, 1.0);
|
|
891
|
+
}
|
|
892
|
+
`;
|
|
893
|
+
|
|
894
|
+
// =============================================================================
|
|
895
|
+
// GAS GIANT SHADER
|
|
896
|
+
// =============================================================================
|
|
897
|
+
|
|
898
|
+
/**
|
|
899
|
+
* Gas giant shader with banded atmosphere and storms
|
|
900
|
+
*/
|
|
901
|
+
export const GAS_GIANT_FRAGMENT = `
|
|
902
|
+
${SPHERE_COMMON}
|
|
903
|
+
|
|
904
|
+
uniform vec3 uBaseColor;
|
|
905
|
+
uniform float uSeed;
|
|
906
|
+
uniform float uStormIntensity; // 0-1
|
|
907
|
+
|
|
908
|
+
void main() {
|
|
909
|
+
// Setup ray - camera looking at sphere from fixed position
|
|
910
|
+
vec3 rayOrigin = vec3(0.0, 0.0, -2.5);
|
|
911
|
+
vec3 rayDir = getRayDirection(vUv);
|
|
912
|
+
|
|
913
|
+
// Ray-sphere intersection (sphere at origin with radius 0.5)
|
|
914
|
+
float t = raySphereIntersect(rayOrigin, rayDir, vec3(0.0), 0.5);
|
|
915
|
+
|
|
916
|
+
if (t < 0.0) {
|
|
917
|
+
gl_FragColor = vec4(0.0);
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Calculate hit point and normal
|
|
922
|
+
vec3 hitPoint = rayOrigin + rayDir * t;
|
|
923
|
+
vec3 normal = normalize(hitPoint);
|
|
924
|
+
|
|
925
|
+
// Apply inverse camera rotation for surface features
|
|
926
|
+
mat3 rotMat = rotationMatrix(-uCameraRotation);
|
|
927
|
+
vec3 rotatedNormal = rotMat * normal;
|
|
928
|
+
|
|
929
|
+
// Convert to spherical coordinates for banding (use rotated normal)
|
|
930
|
+
float latitude = asin(rotatedNormal.y); // -PI/2 to PI/2
|
|
931
|
+
float longitude = atan(rotatedNormal.z, rotatedNormal.x); // -PI to PI
|
|
932
|
+
|
|
933
|
+
// Animated rotation
|
|
934
|
+
float time = uTime * 0.1;
|
|
935
|
+
|
|
936
|
+
// Create bands based on latitude
|
|
937
|
+
float bands = sin(latitude * 15.0 + time) * 0.5 + 0.5;
|
|
938
|
+
bands += sin(latitude * 25.0 - time * 0.5) * 0.25;
|
|
939
|
+
bands += sin(latitude * 40.0 + time * 0.3) * 0.125;
|
|
940
|
+
|
|
941
|
+
// Turbulent distortion of bands
|
|
942
|
+
vec3 noiseCoord = vec3(longitude + time * 0.2, latitude * 3.0, uSeed);
|
|
943
|
+
float turb = fbm(noiseCoord * 5.0, 4) * 0.3;
|
|
944
|
+
bands += turb;
|
|
945
|
+
|
|
946
|
+
// Color variation based on bands
|
|
947
|
+
vec3 lightBand = uBaseColor * 1.3;
|
|
948
|
+
vec3 darkBand = uBaseColor * 0.7;
|
|
949
|
+
vec3 surfaceColor = mix(darkBand, lightBand, bands);
|
|
950
|
+
|
|
951
|
+
// Add storm features
|
|
952
|
+
if (uStormIntensity > 0.0) {
|
|
953
|
+
// Great red spot style storm
|
|
954
|
+
float stormLat = 0.3; // Storm latitude
|
|
955
|
+
float stormLon = time * 0.5; // Storm drifts
|
|
956
|
+
vec2 stormCenter = vec2(stormLon, stormLat);
|
|
957
|
+
vec2 pos = vec2(longitude, latitude);
|
|
958
|
+
float stormDist = length(pos - stormCenter);
|
|
959
|
+
float storm = smoothstep(0.5, 0.2, stormDist);
|
|
960
|
+
storm *= uStormIntensity;
|
|
961
|
+
|
|
962
|
+
// Storm color and swirl
|
|
963
|
+
vec3 stormColor = vec3(0.8, 0.3, 0.2);
|
|
964
|
+
float swirl = sin(stormDist * 20.0 - time * 3.0) * 0.5 + 0.5;
|
|
965
|
+
surfaceColor = mix(surfaceColor, stormColor * swirl, storm);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// Lighting with some subsurface scattering effect
|
|
969
|
+
vec3 lightDir = normalize(vec3(1.0, 0.5, 0.3));
|
|
970
|
+
float light = lighting(normal, lightDir, 0.4);
|
|
971
|
+
surfaceColor *= light;
|
|
972
|
+
|
|
973
|
+
// Limb darkening
|
|
974
|
+
float viewAngle = dot(normal, -rayDir);
|
|
975
|
+
surfaceColor *= 0.7 + max(0.0, viewAngle) * 0.3;
|
|
976
|
+
|
|
977
|
+
gl_FragColor = vec4(surfaceColor, 1.0);
|
|
978
|
+
}
|
|
979
|
+
`;
|
|
980
|
+
|
|
981
|
+
// =============================================================================
|
|
982
|
+
// SHADER LIBRARY EXPORT
|
|
983
|
+
// =============================================================================
|
|
984
|
+
|
|
985
|
+
export const SPHERE_SHADERS = {
|
|
986
|
+
vertex: SPHERE_VERTEX,
|
|
987
|
+
common: SPHERE_COMMON,
|
|
988
|
+
star: STAR_FRAGMENT,
|
|
989
|
+
blackHole: BLACK_HOLE_FRAGMENT,
|
|
990
|
+
rockyPlanet: ROCKY_PLANET_FRAGMENT,
|
|
991
|
+
gasGiant: GAS_GIANT_FRAGMENT,
|
|
992
|
+
};
|
|
993
|
+
|
|
994
|
+
export default SPHERE_SHADERS;
|