@@ -178,6 +178,73 @@ function hslToColor(h, s, l) {
178178 return c
179179}
180180
181+ const STAR_FRAGMENT = /* glsl */ `
182+ ${ SIMPLEX_GLSL }
183+ uniform float uTime;
184+ uniform vec3 uCore;
185+ uniform vec3 uFlare;
186+ varying vec3 vObjPos;
187+ varying vec3 vNormal;
188+ varying vec3 vViewDir;
189+
190+ void main() {
191+ vec3 unit = normalize(vObjPos);
192+ // Rolling plasma surface: two noise samples at different scales/speeds.
193+ float n1 = snoise(unit * 2.5 + vec3(uTime * 0.6));
194+ float n2 = snoise(unit * 5.0 - vec3(uTime * 0.9));
195+ float heat = 0.5 + 0.5 * (n1 * 0.6 + n2 * 0.4);
196+ vec3 col = mix(uCore, uFlare, smoothstep(0.3, 0.95, heat));
197+ // Strong fresnel halo so bloom can latch onto the rim.
198+ float rim = pow(1.0 - max(dot(normalize(vNormal), normalize(vViewDir)), 0.0), 2.0);
199+ col += uFlare * rim * 1.4;
200+ gl_FragColor = vec4(col, 1.0);
201+ }
202+ `
203+
204+ // A glowing star — used for pages that have children. Bright, emissive-feeling
205+ // material that interacts well with UnrealBloomPass. No light/shading: stars
206+ // emit, they don't receive.
207+ export function buildStarObject ( node ) {
208+ const params = planetParams ( node )
209+ const group = new THREE . Group ( )
210+ // Stars read bigger than planets so they anchor the system visually.
211+ const baseR = 5.5 * params . radius
212+
213+ const geometry = new THREE . SphereGeometry ( baseR , 48 , 32 )
214+ // Star colour: warm yellow-orange biased by the page's hue, so it still
215+ // varies per page but reads as a star, not a random planet.
216+ const coreHue = ( params . hue * 0.2 + 0.08 ) % 1.0
217+ const flareHue = ( coreHue + 0.04 ) % 1.0
218+ const material = new THREE . ShaderMaterial ( {
219+ vertexShader : VERTEX_SHADER ,
220+ fragmentShader : STAR_FRAGMENT ,
221+ uniforms : {
222+ uTime : { value : 0 } ,
223+ uCore : { value : hslToColor ( coreHue , 0.85 , 0.7 ) } ,
224+ uFlare : { value : hslToColor ( flareHue , 1.0 , 0.85 ) } ,
225+ } ,
226+ } )
227+ const sphere = new THREE . Mesh ( geometry , material )
228+ sphere . onBeforeRender = ( ) => {
229+ material . uniforms . uTime . value = performance . now ( ) * 0.001
230+ }
231+ group . add ( sphere )
232+
233+ // Soft corona — additive sprite-like shell that fakes glow even without bloom,
234+ // and gives bloom something extra to latch onto when it's enabled.
235+ const coronaGeom = new THREE . SphereGeometry ( baseR * 1.6 , 32 , 24 )
236+ const coronaMat = new THREE . MeshBasicMaterial ( {
237+ color : hslToColor ( flareHue , 1.0 , 0.7 ) ,
238+ transparent : true ,
239+ opacity : 0.18 ,
240+ blending : THREE . AdditiveBlending ,
241+ depthWrite : false ,
242+ } )
243+ group . add ( new THREE . Mesh ( coronaGeom , coronaMat ) )
244+
245+ return group
246+ }
247+
181248// Build a procedural planet mesh. Each node gets its own ShaderMaterial
182249// instance (uniforms differ) but three.js dedups the program compile
183250// because the shader source is identical across all planets.
0 commit comments