-
Notifications
You must be signed in to change notification settings - Fork 646
/
Stars.tsx
108 lines (100 loc) · 3.58 KB
/
Stars.tsx
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
import * as THREE from 'three'
import * as React from 'react'
// eslint-disable-next-line
import { ReactThreeFiber, useFrame } from '@react-three/fiber'
import { Points, Vector3, Spherical, Color, AdditiveBlending, ShaderMaterial } from 'three'
import { ForwardRefComponent } from '../helpers/ts-utils'
import { version } from '../helpers/constants'
type Props = {
radius?: number
depth?: number
count?: number
factor?: number
saturation?: number
fade?: boolean
speed?: number
}
class StarfieldMaterial extends ShaderMaterial {
constructor() {
super({
uniforms: { time: { value: 0.0 }, fade: { value: 1.0 } },
vertexShader: /* glsl */ `
uniform float time;
attribute float size;
varying vec3 vColor;
void main() {
vColor = color;
vec4 mvPosition = modelViewMatrix * vec4(position, 0.5);
gl_PointSize = size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0));
gl_Position = projectionMatrix * mvPosition;
}`,
fragmentShader: /* glsl */ `
uniform sampler2D pointTexture;
uniform float fade;
varying vec3 vColor;
void main() {
float opacity = 1.0;
if (fade == 1.0) {
float d = distance(gl_PointCoord, vec2(0.5, 0.5));
opacity = 1.0 / (1.0 + exp(16.0 * (d - 0.25)));
}
gl_FragColor = vec4(vColor, opacity);
#include <tonemapping_fragment>
#include <${version >= 154 ? 'colorspace_fragment' : 'encodings_fragment'}>
}`,
})
}
}
declare global {
namespace JSX {
interface IntrinsicElements {
starfieldMaterial: ReactThreeFiber.MaterialNode<StarfieldMaterial, []>
}
}
}
const genStar = (r: number) => {
return new Vector3().setFromSpherical(new Spherical(r, Math.acos(1 - Math.random() * 2), Math.random() * 2 * Math.PI))
}
export const Stars: ForwardRefComponent<Props, Points> = /* @__PURE__ */ React.forwardRef(
({ radius = 100, depth = 50, count = 5000, saturation = 0, factor = 4, fade = false, speed = 1 }: Props, ref) => {
const material = React.useRef<StarfieldMaterial>()
const [position, color, size] = React.useMemo(() => {
const positions: any[] = []
const colors: any[] = []
const sizes = Array.from({ length: count }, () => (0.5 + 0.5 * Math.random()) * factor)
const color = new Color()
let r = radius + depth
const increment = depth / count
for (let i = 0; i < count; i++) {
r -= increment * Math.random()
positions.push(...genStar(r).toArray())
color.setHSL(i / count, saturation, 0.9)
colors.push(color.r, color.g, color.b)
}
return [new Float32Array(positions), new Float32Array(colors), new Float32Array(sizes)]
}, [count, depth, factor, radius, saturation])
useFrame(
(state) => material.current && (material.current.uniforms.time.value = state.clock.getElapsedTime() * speed)
)
const [starfieldMaterial] = React.useState(() => new StarfieldMaterial())
return (
<points ref={ref as React.MutableRefObject<Points>}>
<bufferGeometry>
<bufferAttribute attach="attributes-position" args={[position, 3]} />
<bufferAttribute attach="attributes-color" args={[color, 3]} />
<bufferAttribute attach="attributes-size" args={[size, 1]} />
</bufferGeometry>
<primitive
ref={material}
object={starfieldMaterial}
attach="material"
blending={AdditiveBlending}
uniforms-fade-value={fade}
depthWrite={false}
transparent
vertexColors
/>
</points>
)
}
)