Spaces:
Sleeping
Sleeping
File size: 4,308 Bytes
d2226ad | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | import * as THREE from "three";
import { useMemo, useRef, useLayoutEffect } from "react";
const vertexShader = `
attribute float size;
attribute vec3 customColor;
varying vec3 vColor;
void main() {
vColor = customColor;
vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);
// FIX: Use length() to prevent negative Z issues behind camera
gl_PointSize = size * (300.0 / length(mvPosition.xyz));
gl_Position = projectionMatrix * mvPosition;
}
`;
const fragmentShader = `
uniform sampler2D uTexture;
varying vec3 vColor;
void main() {
vec4 texColor = texture2D(uTexture, gl_PointCoord);
if (texColor.a < 0.05) discard;
gl_FragColor = vec4(vColor, 1.0) * texColor;
gl_FragColor.a *= 0.15; // Transparency
}
`;
export function MilkyWay({ data, visible }) {
const meshRef = useRef();
const texture = useMemo(() => {
try {
const canvas = document.createElement("canvas");
canvas.width = 128;
canvas.height = 128;
const ctx = canvas.getContext("2d");
const grd = ctx.createRadialGradient(64, 64, 0, 64, 64, 64);
grd.addColorStop(0, "rgba(255, 255, 255, 1)");
grd.addColorStop(0.4, "rgba(200, 200, 255, 0.3)");
grd.addColorStop(0.8, "rgba(100, 0, 150, 0.05)");
grd.addColorStop(1, "rgba(0, 0, 0, 0)");
ctx.fillStyle = grd;
ctx.fillRect(0, 0, 128, 128);
return new THREE.CanvasTexture(canvas);
} catch (e) {
console.error("Texture Gen Failed", e);
return null;
}
}, []);
const { positions, colors, sizes } = useMemo(() => {
if (!data || !data.altitude || data.altitude.length === 0) {
return {
positions: new Float32Array(0),
colors: new Float32Array(0),
sizes: new Float32Array(0),
};
}
const count = data.altitude.length;
const posArray = new Float32Array(count * 3);
const colArray = new Float32Array(count * 3);
const sizeArray = new Float32Array(count);
const color = new THREE.Color();
const radius = 120;
for (let i = 0; i < count; i++) {
const alt = data.altitude[i];
const az = data.azimuth[i];
const intensity = data.intensity[i];
if (alt < -100 || isNaN(alt)) {
sizeArray[i] = 0;
continue;
}
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);
if (intensity > 0.8) color.setHSL(0.1, 0.8, 0.8);
else if (intensity > 0.5) color.setHSL(0.6, 0.5, 0.6);
else color.setHSL(0.75, 0.6, 0.3);
colArray[i * 3] = color.r;
colArray[i * 3 + 1] = color.g;
colArray[i * 3 + 2] = color.b;
sizeArray[i] = (20 + Math.random() * 20) * intensity;
}
return { positions: posArray, colors: colArray, sizes: sizeArray };
}, [data]);
useLayoutEffect(() => {
if (meshRef.current && positions.length > 0) {
meshRef.current.geometry.attributes.position.needsUpdate = true;
meshRef.current.geometry.attributes.customColor.needsUpdate = true;
meshRef.current.geometry.attributes.size.needsUpdate = true;
}
}, [positions, colors, sizes]);
if (!texture || positions.length === 0) return null;
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}
/>
</bufferGeometry>
<shaderMaterial
uniforms={{ uTexture: { value: texture } }}
vertexShader={vertexShader}
fragmentShader={fragmentShader}
transparent={true}
depthWrite={false}
blending={THREE.AdditiveBlending}
/>
</points>
</group>
);
}
|