mgokg commited on
Commit
3ddeb78
·
verified ·
1 Parent(s): 63bd228

Upload visual-3d.ts

Browse files
Files changed (1) hide show
  1. visual-3d.ts +258 -0
visual-3d.ts ADDED
@@ -0,0 +1,258 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ // tslint:disable:organize-imports
7
+ // tslint:disable:ban-malformed-import-paths
8
+ // tslint:disable:no-new-decorators
9
+
10
+ import {LitElement, css, html} from 'lit';
11
+ import {customElement, property} from 'lit/decorators.js';
12
+ import {Analyser} from './analyser';
13
+
14
+ import * as THREE from 'three';
15
+ import {EXRLoader} from 'three/addons/loaders/EXRLoader.js';
16
+ import {EffectComposer} from 'three/addons/postprocessing/EffectComposer.js';
17
+ import {RenderPass} from 'three/addons/postprocessing/RenderPass.js';
18
+ import {ShaderPass} from 'three/addons/postprocessing/ShaderPass.js';
19
+ import {UnrealBloomPass} from 'three/addons/postprocessing/UnrealBloomPass.js';
20
+ import {FXAAShader} from 'three/addons/shaders/FXAAShader.js';
21
+ import {fs as backdropFS, vs as backdropVS} from './backdrop-shader';
22
+ import {vs as sphereVS} from './sphere-shader';
23
+
24
+ /**
25
+ * 3D live audio visual.
26
+ */
27
+ @customElement('gdm-live-audio-visuals-3d')
28
+ export class GdmLiveAudioVisuals3D extends LitElement {
29
+ private inputAnalyser!: Analyser;
30
+ private outputAnalyser!: Analyser;
31
+ private camera!: THREE.PerspectiveCamera;
32
+ private backdrop!: THREE.Mesh;
33
+ private composer!: EffectComposer;
34
+ private sphere!: THREE.Mesh;
35
+ private prevTime = 0;
36
+ private rotation = new THREE.Vector3(0, 0, 0);
37
+
38
+ private _outputNode!: AudioNode;
39
+
40
+ @property()
41
+ set outputNode(node: AudioNode) {
42
+ this._outputNode = node;
43
+ this.outputAnalyser = new Analyser(this._outputNode);
44
+ }
45
+
46
+ get outputNode() {
47
+ return this._outputNode;
48
+ }
49
+
50
+ private _inputNode!: AudioNode;
51
+
52
+ @property()
53
+ set inputNode(node: AudioNode) {
54
+ this._inputNode = node;
55
+ this.inputAnalyser = new Analyser(this._inputNode);
56
+ }
57
+
58
+ get inputNode() {
59
+ return this._inputNode;
60
+ }
61
+
62
+ private canvas!: HTMLCanvasElement;
63
+
64
+ static styles = css`
65
+ canvas {
66
+ width: 100% !important;
67
+ height: 100% !important;
68
+ position: absolute;
69
+ inset: 0;
70
+ image-rendering: pixelated;
71
+ }
72
+ `;
73
+
74
+ connectedCallback() {
75
+ super.connectedCallback();
76
+ }
77
+
78
+ private init() {
79
+ const scene = new THREE.Scene();
80
+ scene.background = new THREE.Color(0x100c14);
81
+
82
+ const backdrop = new THREE.Mesh(
83
+ new THREE.IcosahedronGeometry(10, 5),
84
+ new THREE.RawShaderMaterial({
85
+ uniforms: {
86
+ resolution: {value: new THREE.Vector2(1, 1)},
87
+ rand: {value: 0},
88
+ },
89
+ vertexShader: backdropVS,
90
+ fragmentShader: backdropFS,
91
+ glslVersion: THREE.GLSL3,
92
+ }),
93
+ );
94
+ backdrop.material.side = THREE.BackSide;
95
+ scene.add(backdrop);
96
+ this.backdrop = backdrop;
97
+
98
+ const camera = new THREE.PerspectiveCamera(
99
+ 75,
100
+ window.innerWidth / window.innerHeight,
101
+ 0.1,
102
+ 1000,
103
+ );
104
+ camera.position.set(2, -2, 5);
105
+ this.camera = camera;
106
+
107
+ const renderer = new THREE.WebGLRenderer({
108
+ canvas: this.canvas,
109
+ antialias: !true,
110
+ });
111
+ renderer.setSize(window.innerWidth, window.innerHeight);
112
+ renderer.setPixelRatio(window.devicePixelRatio / 1);
113
+
114
+ const geometry = new THREE.IcosahedronGeometry(1, 10);
115
+
116
+ new EXRLoader().load('piz_compressed.exr', (texture: THREE.Texture) => {
117
+ texture.mapping = THREE.EquirectangularReflectionMapping;
118
+ const exrCubeRenderTarget = pmremGenerator.fromEquirectangular(texture);
119
+ sphereMaterial.envMap = exrCubeRenderTarget.texture;
120
+ sphere.visible = true;
121
+ });
122
+
123
+ const pmremGenerator = new THREE.PMREMGenerator(renderer);
124
+ pmremGenerator.compileEquirectangularShader();
125
+
126
+ const sphereMaterial = new THREE.MeshStandardMaterial({
127
+ color: 0x000010,
128
+ metalness: 0.5,
129
+ roughness: 0.1,
130
+ emissive: 0x000010,
131
+ emissiveIntensity: 1.5,
132
+ });
133
+
134
+ sphereMaterial.onBeforeCompile = (shader) => {
135
+ shader.uniforms.time = {value: 0};
136
+ shader.uniforms.inputData = {value: new THREE.Vector4()};
137
+ shader.uniforms.outputData = {value: new THREE.Vector4()};
138
+
139
+ sphereMaterial.userData.shader = shader;
140
+
141
+ shader.vertexShader = sphereVS;
142
+ };
143
+
144
+ const sphere = new THREE.Mesh(geometry, sphereMaterial);
145
+ scene.add(sphere);
146
+ sphere.visible = false;
147
+
148
+ this.sphere = sphere;
149
+
150
+ const renderPass = new RenderPass(scene, camera);
151
+
152
+ const bloomPass = new UnrealBloomPass(
153
+ new THREE.Vector2(window.innerWidth, window.innerHeight),
154
+ 5,
155
+ 0.5,
156
+ 0,
157
+ );
158
+
159
+ const fxaaPass = new ShaderPass(FXAAShader);
160
+
161
+ const composer = new EffectComposer(renderer);
162
+ composer.addPass(renderPass);
163
+ // composer.addPass(fxaaPass);
164
+ composer.addPass(bloomPass);
165
+
166
+ this.composer = composer;
167
+
168
+ function onWindowResize() {
169
+ camera.aspect = window.innerWidth / window.innerHeight;
170
+ camera.updateProjectionMatrix();
171
+ const dPR = renderer.getPixelRatio();
172
+ const w = window.innerWidth;
173
+ const h = window.innerHeight;
174
+ backdrop.material.uniforms.resolution.value.set(w * dPR, h * dPR);
175
+ renderer.setSize(w, h);
176
+ composer.setSize(w, h);
177
+ fxaaPass.material.uniforms['resolution'].value.set(
178
+ 1 / (w * dPR),
179
+ 1 / (h * dPR),
180
+ );
181
+ }
182
+
183
+ window.addEventListener('resize', onWindowResize);
184
+ onWindowResize();
185
+
186
+ this.animation();
187
+ }
188
+
189
+ private animation() {
190
+ requestAnimationFrame(() => this.animation());
191
+
192
+ this.inputAnalyser.update();
193
+ this.outputAnalyser.update();
194
+
195
+ const t = performance.now();
196
+ const dt = (t - this.prevTime) / (1000 / 60);
197
+ this.prevTime = t;
198
+ const backdropMaterial = this.backdrop.material as THREE.RawShaderMaterial;
199
+ const sphereMaterial = this.sphere.material as THREE.MeshStandardMaterial;
200
+
201
+ backdropMaterial.uniforms.rand.value = Math.random() * 10000;
202
+
203
+ if (sphereMaterial.userData.shader) {
204
+ this.sphere.scale.setScalar(
205
+ 1 + (0.2 * this.outputAnalyser.data[1]) / 255,
206
+ );
207
+
208
+ const f = 0.001;
209
+ this.rotation.x += (dt * f * 0.5 * this.outputAnalyser.data[1]) / 255;
210
+ this.rotation.z += (dt * f * 0.5 * this.inputAnalyser.data[1]) / 255;
211
+ this.rotation.y += (dt * f * 0.25 * this.inputAnalyser.data[2]) / 255;
212
+ this.rotation.y += (dt * f * 0.25 * this.outputAnalyser.data[2]) / 255;
213
+
214
+ const euler = new THREE.Euler(
215
+ this.rotation.x,
216
+ this.rotation.y,
217
+ this.rotation.z,
218
+ );
219
+ const quaternion = new THREE.Quaternion().setFromEuler(euler);
220
+ const vector = new THREE.Vector3(0, 0, 5);
221
+ vector.applyQuaternion(quaternion);
222
+ this.camera.position.copy(vector);
223
+ this.camera.lookAt(this.sphere.position);
224
+
225
+ sphereMaterial.userData.shader.uniforms.time.value +=
226
+ (dt * 0.1 * this.outputAnalyser.data[0]) / 255;
227
+ sphereMaterial.userData.shader.uniforms.inputData.value.set(
228
+ (1 * this.inputAnalyser.data[0]) / 255,
229
+ (0.1 * this.inputAnalyser.data[1]) / 255,
230
+ (10 * this.inputAnalyser.data[2]) / 255,
231
+ 0,
232
+ );
233
+ sphereMaterial.userData.shader.uniforms.outputData.value.set(
234
+ (2 * this.outputAnalyser.data[0]) / 255,
235
+ (0.1 * this.outputAnalyser.data[1]) / 255,
236
+ (10 * this.outputAnalyser.data[2]) / 255,
237
+ 0,
238
+ );
239
+ }
240
+
241
+ this.composer.render();
242
+ }
243
+
244
+ protected firstUpdated() {
245
+ this.canvas = this.shadowRoot!.querySelector('canvas') as HTMLCanvasElement;
246
+ this.init();
247
+ }
248
+
249
+ protected render() {
250
+ return html`<canvas></canvas>`;
251
+ }
252
+ }
253
+
254
+ declare global {
255
+ interface HTMLElementTagNameMap {
256
+ 'gdm-live-audio-visuals-3d': GdmLiveAudioVisuals3D;
257
+ }
258
+ }