Skip to content

Commit b9e1cfb

Browse files
Chau TranChau Tran
Chau Tran
authored and
Chau Tran
committed
feat(soba): migrate stars
1 parent 307cd41 commit b9e1cfb

File tree

10 files changed

+262
-1
lines changed

10 files changed

+262
-1
lines changed

libs/soba/project.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@
5656
"libs/soba/performance/**/*.ts",
5757
"libs/soba/performance/**/*.html",
5858
"libs/soba/loaders/**/*.ts",
59-
"libs/soba/loaders/**/*.html"
59+
"libs/soba/loaders/**/*.html",
60+
"libs/soba/shaders/**/*.ts",
61+
"libs/soba/shaders/**/*.html"
6062
]
6163
}
6264
},

libs/soba/shaders/README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# angular-three-soba/shaders
2+
3+
Secondary entry point of `angular-three-soba`. It can be used by importing from `angular-three-soba/shaders`.

libs/soba/shaders/ng-package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"lib": {
3+
"entryFile": "src/index.ts"
4+
}
5+
}

libs/soba/shaders/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export * from './star-field-material/star-field-material';
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import * as THREE from 'three';
2+
3+
export function shaderMaterial(
4+
uniforms: {
5+
[name: string]:
6+
| THREE.CubeTexture
7+
| THREE.Texture
8+
| Int32Array
9+
| Float32Array
10+
| THREE.Matrix4
11+
| THREE.Matrix3
12+
| THREE.Quaternion
13+
| THREE.Vector4
14+
| THREE.Vector3
15+
| THREE.Vector2
16+
| THREE.Color
17+
| number
18+
| boolean
19+
| Array<any>
20+
| null;
21+
},
22+
vertexShader: string,
23+
fragmentShader: string,
24+
onInit?: (material?: THREE.ShaderMaterial) => void
25+
) {
26+
const material = class extends THREE.ShaderMaterial {
27+
public key: string = '';
28+
constructor(parameters = {}) {
29+
const entries = Object.entries(uniforms);
30+
// Create unforms and shaders
31+
super({
32+
uniforms: entries.reduce((acc, [name, value]) => {
33+
const uniform = THREE.UniformsUtils.clone({ [name]: { value } });
34+
return {
35+
...acc,
36+
...uniform,
37+
};
38+
}, {}),
39+
vertexShader,
40+
fragmentShader,
41+
});
42+
// Create getter/setters
43+
entries.forEach(([name]) =>
44+
Object.defineProperty(this, name, {
45+
get: () => this.uniforms[name].value,
46+
set: (v) => (this.uniforms[name].value = v),
47+
})
48+
);
49+
50+
// Assign parameters, this might include uniforms
51+
Object.assign(this, parameters);
52+
// Call onInit
53+
if (onInit) onInit(this);
54+
}
55+
} as unknown as typeof THREE.ShaderMaterial & { key: string };
56+
material.key = THREE.MathUtils.generateUUID();
57+
return material;
58+
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import { shaderMaterial } from '../shader-material/shader-material';
2+
3+
export const StarFieldMaterial = shaderMaterial(
4+
{ time: 0.0, fade: 0.0 },
5+
// language=GLSL
6+
`
7+
uniform float time;
8+
attribute float size;
9+
varying vec3 vColor;
10+
void main() {
11+
vColor = color;
12+
vec4 mvPosition = modelViewMatrix * vec4(position, 0.5);
13+
gl_PointSize = size * (30.0 / -mvPosition.z) * (3.0 + sin(time + 100.0));
14+
gl_Position = projectionMatrix * mvPosition;
15+
}
16+
`,
17+
// language=GLSL
18+
`
19+
uniform sampler2D pointTexture;
20+
uniform float fade;
21+
varying vec3 vColor;
22+
void main() {
23+
float opacity = 1.0;
24+
if (fade == 1.0) {
25+
float d = distance(gl_PointCoord, vec2(0.5, 0.5));
26+
opacity = 1.0 / (1.0 + exp(16.0 * (d - 0.25)));
27+
}
28+
gl_FragColor = vec4(vColor, opacity);
29+
30+
#include <tonemapping_fragment>
31+
#include <encodings_fragment>
32+
}
33+
`
34+
);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
2+
import { Meta, moduleMetadata } from '@storybook/angular';
3+
import { NgtArgs } from 'angular-three';
4+
import { NgtsStars } from 'angular-three-soba/staging';
5+
import { makeStoryFunction, StorybookSetup } from '../setup-canvas';
6+
7+
@Component({
8+
standalone: true,
9+
template: `
10+
<ngt-mesh [rotation]="[Math.PI / 2, 0, 0]">
11+
<ngt-plane-geometry *args="[100, 100, 4, 4]" />
12+
<ngt-mesh-basic-material color="white" [wireframe]="true" />
13+
</ngt-mesh>
14+
<ngt-axes-helper />
15+
<ngts-stars />
16+
`,
17+
imports: [NgtsStars, NgtArgs],
18+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
19+
})
20+
class DefaultStarsStory {
21+
readonly Math = Math;
22+
}
23+
24+
export default {
25+
title: 'Staging/Stars',
26+
decorators: [moduleMetadata({ imports: [StorybookSetup] })],
27+
} as Meta;
28+
29+
export const Default = makeStoryFunction(DefaultStarsStory);

libs/soba/staging/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@ export * from './cloud/cloud';
33
export * from './contact-shadows/contact-shadows';
44
export * from './float/float';
55
export * from './sky/sky';
6+
export * from './stars/stars';

libs/soba/staging/src/stars/stars.ts

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
import { Component, computed, CUSTOM_ELEMENTS_SCHEMA, Input } from '@angular/core';
2+
import { extend, injectBeforeRender, injectNgtRef, NgtArgs, NgtSignalStore, type NgtRenderState } from 'angular-three';
3+
import { StarFieldMaterial } from 'angular-three-soba/shaders';
4+
import * as THREE from 'three';
5+
import { BufferAttribute, BufferGeometry, Points } from 'three';
6+
7+
extend({ Points, BufferGeometry, BufferAttribute });
8+
9+
const genStar = (r: number) => {
10+
return new THREE.Vector3().setFromSpherical(
11+
new THREE.Spherical(r, Math.acos(1 - Math.random() * 2), Math.random() * 2 * Math.PI)
12+
);
13+
};
14+
15+
export interface NgtsStarsState {
16+
radius: number;
17+
depth: number;
18+
count: number;
19+
factor: number;
20+
saturation: number;
21+
fade: boolean;
22+
speed: number;
23+
}
24+
25+
@Component({
26+
selector: 'ngts-stars',
27+
standalone: true,
28+
template: `
29+
<ngt-points [ref]="starsRef">
30+
<ngt-buffer-geometry>
31+
<ngt-buffer-attribute attach="attributes.position" *args="[bufferAttributes().positions, 3]" />
32+
<ngt-buffer-attribute attach="attributes.color" *args="[bufferAttributes().colors, 3]" />
33+
<ngt-buffer-attribute attach="attributes.size" *args="[bufferAttributes().sizes, 1]" />
34+
</ngt-buffer-geometry>
35+
<ngt-primitive
36+
*args="[material]"
37+
attach="material"
38+
[blending]="AdditiveBlending"
39+
[depthWrite]="false"
40+
[transparent]="true"
41+
[vertexColors]="true"
42+
>
43+
<ngt-value attach="uniforms.fade.value" [rawValue]="fade()" />
44+
</ngt-primitive>
45+
</ngt-points>
46+
`,
47+
imports: [NgtArgs],
48+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
49+
})
50+
export class NgtsStars extends NgtSignalStore<NgtsStarsState> {
51+
readonly AdditiveBlending = THREE.AdditiveBlending;
52+
readonly material = new StarFieldMaterial();
53+
54+
@Input() starsRef = injectNgtRef<Points>();
55+
56+
@Input() set radius(radius: number) {
57+
this.set({ radius });
58+
}
59+
60+
@Input() set depth(depth: number) {
61+
this.set({ depth });
62+
}
63+
64+
@Input() set count(count: number) {
65+
this.set({ count });
66+
}
67+
68+
@Input() set factor(factor: number) {
69+
this.set({ factor });
70+
}
71+
72+
@Input() set saturation(saturation: number) {
73+
this.set({ saturation });
74+
}
75+
76+
@Input('fade') set starsFade(fade: boolean) {
77+
this.set({ fade });
78+
}
79+
80+
@Input() set speed(speed: number) {
81+
this.set({ speed });
82+
}
83+
84+
readonly #count = this.select('count');
85+
readonly #depth = this.select('depth');
86+
readonly #factor = this.select('factor');
87+
readonly #radius = this.select('radius');
88+
readonly #saturation = this.select('saturation');
89+
90+
readonly fade = this.select('fade');
91+
readonly bufferAttributes = computed(() => {
92+
const positions: number[] = [];
93+
const colors: number[] = [];
94+
const sizes = Array.from({ length: this.#count() }, () => (0.5 + 0.5 * Math.random()) * this.#factor());
95+
const color = new THREE.Color();
96+
let r = this.#radius() + this.#depth();
97+
const increment = this.#depth() / this.#count();
98+
for (let i = 0; i < this.#count(); i++) {
99+
r -= increment * Math.random();
100+
positions.push(...genStar(r).toArray());
101+
color.setHSL(i / this.#count(), this.#saturation(), 0.9);
102+
colors.push(color.r, color.g, color.b);
103+
}
104+
return {
105+
positions: new Float32Array(positions),
106+
colors: new Float32Array(colors),
107+
sizes: new Float32Array(sizes),
108+
};
109+
});
110+
111+
constructor() {
112+
super({
113+
radius: 100,
114+
depth: 50,
115+
count: 5000,
116+
saturation: 0,
117+
factor: 4,
118+
fade: false,
119+
speed: 1,
120+
});
121+
injectBeforeRender(this.#onBeforeRender.bind(this));
122+
}
123+
124+
#onBeforeRender({ clock }: NgtRenderState) {
125+
this.material.uniforms['time'].value = clock.getElapsedTime() * this.get('speed');
126+
}
127+
}

tsconfig.base.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"angular-three-soba/loaders": ["libs/soba/loaders/src/index.ts"],
2828
"angular-three-soba/misc": ["libs/soba/misc/src/index.ts"],
2929
"angular-three-soba/performance": ["libs/soba/performance/src/index.ts"],
30+
"angular-three-soba/shaders": ["libs/soba/shaders/src/index.ts"],
3031
"angular-three-soba/staging": ["libs/soba/staging/src/index.ts"],
3132
"angular-three-suspense": ["libs/suspense/src/index.ts"]
3233
}

0 commit comments

Comments
 (0)