SubhojitGhimire
initial commit
d2226ad
import * as THREE from "three";
import { useFrame } from "@react-three/fiber";
import { useRef, useMemo, useLayoutEffect } from "react";
const vertexShader = `
attribute float size;
attribute vec3 customColor;
attribute float random;
varying vec3 vColor;
varying float vRandom;
void main() {
vColor = customColor;
vRandom = random;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
// Scale size based on distance (perspective)
// 300.0 is an arbitrary scale factor to make them look good
gl_PointSize = size * (300.0 / -mvPosition.z);
gl_Position = projectionMatrix * mvPosition;
}
`;
const fragmentShader = `
uniform float uTime;
varying vec3 vColor;
varying float vRandom;
void main() {
// Make it a circular particle
vec2 uv = gl_PointCoord.xy - 0.5;
float dist = length(uv);
// Discard corners of the square point
if (dist > 0.5) discard;
// Soft edge (glow)
// gradient from center (0.0) to edge (0.5)
float glow = 1.0 - (dist * 2.0);
glow = pow(glow, 1.5); // sharpen the glow curve
// Twinkle Logic
// Use sine wave based on Time + Random offset
// vRandom makes sure every star blinks out of phase
float twinkleSpeed = 3.0 + (vRandom * 2.0); // varied speeds
float twinkle = sin((uTime * twinkleSpeed) + (vRandom * 100.0));
// Map sine (-1 to 1) to a brightness factor (e.g. 0.6 to 1.0)
float brightness = 0.7 + (0.3 * twinkle);
gl_FragColor = vec4(vColor, glow * brightness);
}
`;
export function Stars({ data, visible }) {
const meshRef = useRef();
const materialRef = useRef();
const { positions, colors, sizes, randoms } = useMemo(() => {
if (!data)
return {
positions: new Float32Array(0),
colors: new Float32Array(0),
sizes: new Float32Array(0),
randoms: new Float32Array(0),
};
const starCount = data.stars.ids.length;
const posArray = new Float32Array(starCount * 3);
const colArray = new Float32Array(starCount * 3);
const sizeArray = new Float32Array(starCount);
const randArray = new Float32Array(starCount);
const radius = 100;
const color = new THREE.Color();
for (let i = 0; i < starCount; i++) {
const alt = data.stars.altitude[i];
const az = data.stars.azimuth[i];
const mag = data.stars.magnitude[i];
const id = data.stars.ids[i];
const phi = (90 - alt) * (Math.PI / 180);
const theta = az * (Math.PI / 180);
posArray[i * 3] = radius * Math.sin(phi) * Math.sin(theta);
posArray[i * 3 + 1] = radius * Math.cos(phi);
posArray[i * 3 + 2] = -radius * Math.sin(phi) * Math.cos(theta);
const spectralType = id % 10;
if (mag < 1.0) {
color.setHex(id % 2 === 0 ? 0xaaccff : 0xffddaa); // White-Blue or Gold for very bright stars
} else if (spectralType < 2) {
color.setHex(0xaaccff); // Blueish
} else if (spectralType < 4) {
color.setHex(0xffffff); // White
} else if (spectralType < 7) {
color.setHex(0xffebcd); // Yellow/White
} else {
color.setHex(0xffccaa); // Orange/Reddish
}
colArray[i * 3] = color.r;
colArray[i * 3 + 1] = color.g;
colArray[i * 3 + 2] = color.b;
let s = 4.0 - mag * 0.5;
if (s < 1.5) s = 1.5;
sizeArray[i] = s;
randArray[i] = Math.random(); // Random value for twinkling efect
}
return {
positions: posArray,
colors: colArray,
sizes: sizeArray,
randoms: randArray,
};
}, [data]);
useFrame((state) => {
if (materialRef.current) {
materialRef.current.uniforms.uTime.value = state.clock.getElapsedTime();
}
});
useLayoutEffect(() => {
if (meshRef.current) {
const geo = meshRef.current.geometry;
geo.attributes.position.needsUpdate = true;
geo.attributes.customColor.needsUpdate = true;
geo.attributes.size.needsUpdate = true;
geo.attributes.random.needsUpdate = true;
}
}, [positions, colors, sizes, randoms]);
return (
<group visible={visible}>
<points ref={meshRef}>
<bufferGeometry>
<bufferAttribute
attach="attributes-position"
count={positions.length / 3}
array={positions}
itemSize={3}
/>
<bufferAttribute
attach="attributes-customColor"
count={colors.length / 3}
array={colors}
itemSize={3}
/>
<bufferAttribute
attach="attributes-size"
count={sizes.length}
array={sizes}
itemSize={1}
/>
<bufferAttribute
attach="attributes-random"
count={randoms.length}
array={randoms}
itemSize={1}
/>
</bufferGeometry>
<shaderMaterial
ref={materialRef}
uniforms={{ uTime: { value: 0 } }}
vertexShader={vertexShader}
fragmentShader={fragmentShader}
transparent={true}
depthWrite={false}
blending={THREE.AdditiveBlending}
/>
</points>
</group>
);
}