├── LICENSE
├── base.css
├── index.html
├── main.js
├── math.js
├── noise.js
├── resources
├── background-grey-dots.png
├── crosshair.png
├── fire.png
├── freepbr
│ ├── broken_down_concrete2_Height.png
│ ├── broken_down_concrete2_albedo.png
│ ├── broken_down_concrete2_ao.png
│ ├── broken_down_concrete2_metallic.png
│ ├── broken_down_concrete2_normal.png
│ ├── broken_down_concrete2_roughness.png
│ ├── concrete3-albedo.png
│ ├── concrete3-metallic.png
│ ├── concrete3-normal.png
│ ├── concrete3-roughness.png
│ ├── flaking-plaster_albedo.png
│ ├── flaking-plaster_ao.png
│ ├── flaking-plaster_metallic.png
│ ├── flaking-plaster_normal-ogl.png
│ ├── flaking-plaster_roughness.png
│ ├── readme.txt
│ ├── rustediron2_albedo.png
│ ├── rustediron2_metallic.png
│ ├── rustediron2_normal.png
│ ├── rustediron2_roughness.png
│ ├── worn_metal4_Height.png
│ ├── worn_metal4_albedo.png
│ ├── worn_metal4_ao.png
│ ├── worn_metal4_metallic.png
│ ├── worn_metal4_normal.png
│ ├── worn_metal4_preview.jpg
│ └── worn_metal4_roughness.png
├── music
│ ├── AcousticRock.mp3
│ ├── Ectoplasm.mp3
│ └── readme.txt
├── skybox
│ ├── negx.jpg
│ ├── negy.jpg
│ ├── negz.jpg
│ ├── posx.jpg
│ ├── posy.jpg
│ ├── posz.jpg
│ └── readme.txt
└── speaker.png
└── simplex-noise.js
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 simondevyoutube
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/base.css:
--------------------------------------------------------------------------------
1 | body {
2 | width: 100%;
3 | height: 100%;
4 | position: absolute;
5 | background: #000000;
6 | margin: 0;
7 | padding: 0;
8 | overscroll-behavior: none;
9 | }
10 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Three.JS Tutorial: Audio Visualizer
5 |
6 |
7 |
8 |
9 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/main.js:
--------------------------------------------------------------------------------
1 | import * as THREE from 'https://cdn.skypack.dev/three@0.136';
2 |
3 | import {EffectComposer} from 'https://cdn.skypack.dev/three@0.136/examples/jsm/postprocessing/EffectComposer.js';
4 | import {ShaderPass} from 'https://cdn.skypack.dev/three@0.136/examples//jsm/postprocessing/ShaderPass.js';
5 | import {GammaCorrectionShader} from 'https://cdn.skypack.dev/three@0.136/examples/jsm/shaders/GammaCorrectionShader.js';
6 | import {RenderPass} from 'https://cdn.skypack.dev/three@0.136/examples/jsm/postprocessing/RenderPass.js';
7 | import {FXAAShader} from 'https://cdn.skypack.dev/three@0.136/examples/jsm/shaders/FXAAShader.js';
8 |
9 | import {math} from './math.js';
10 | import {noise} from './noise.js';
11 |
12 |
13 |
14 | const FS_DECLARATIONS = `
15 |
16 | uniform sampler2D audioDataTexture;
17 | uniform vec2 iResolution;
18 | uniform float iTime;
19 |
20 | #define M_PI 3.14159
21 | #define NUM_BARS 64.0
22 | #define CIRCLE_RADIUS 0.15
23 | #define BAR_HEIGHT 0.125
24 |
25 |
26 | // All code snippets taken from Inigo Quilez's site
27 | // Make sure to check out his site!
28 | // https://iquilezles.org/
29 | //
30 | vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
31 | return a + b*cos( 6.28318*(c*t+d) );
32 | }
33 |
34 | float dot2(in vec2 v ) { return dot(v,v); }
35 |
36 | float sdfTrapezoid(in vec2 p, in float r1, float r2, float he) {
37 | vec2 k1 = vec2(r2,he);
38 | vec2 k2 = vec2(r2-r1,2.0*he);
39 | p.x = abs(p.x);
40 | vec2 ca = vec2(p.x-min(p.x,(p.y<0.0)?r1:r2), abs(p.y)-he);
41 | vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot2(k2), 0.0, 1.0 );
42 | float s = (cb.x<0.0 && ca.y<0.0) ? -1.0 : 1.0;
43 | return s*sqrt( min(dot2(ca),dot2(cb)) );
44 | }
45 |
46 | float sdUnevenCapsule( vec2 p, float r1, float r2, float h ) {
47 | p.x = abs(p.x);
48 | float b = (r1-r2)/h;
49 | float a = sqrt(1.0-b*b);
50 | float k = dot(p,vec2(-b,a));
51 | if( k < 0.0 ) return length(p) - r1;
52 | if( k > a*h ) return length(p-vec2(0.0,h)) - r2;
53 | return dot(p, vec2(a,b) ) - r1;
54 | }
55 |
56 | float sdTriangleIsosceles( in vec2 p, in vec2 q ) {
57 | p.x = abs(p.x);
58 | vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );
59 | vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );
60 | float s = -sign( q.y );
61 | vec2 d = min( vec2( dot(a,a), s*(p.x*q.y-p.y*q.x) ),
62 | vec2( dot(b,b), s*(p.y-q.y) ));
63 | return -sqrt(d.x)*sign(d.y);
64 | }
65 |
66 | float opSmoothUnion( float d1, float d2, float k ) {
67 | float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
68 | return mix( d2, d1, h ) - k*h*(1.0-h);
69 | }
70 |
71 | float opUnion( float d1, float d2 ) { return min(d1,d2); }
72 | float opIntersection( float d1, float d2 ) { return max(d1,d2); }
73 | float opSubtraction( float d1, float d2 ) { return max(-d1,d2); }
74 |
75 | float sdfBar(vec2 position, vec2 dimensions, vec2 uv, float frequencySample) {
76 | float w = mix(dimensions.x * 0.5, dimensions.x, smoothstep(0.0, 1.0, frequencySample));
77 | vec2 basePosition = uv - position + vec2(0.0, -dimensions.y * 0.5 - frequencySample * 0.05);
78 |
79 | float d = sdfTrapezoid(
80 | basePosition,
81 | dimensions.x * 0.5,
82 | w, dimensions.y * 0.5);
83 |
84 | return (d > 0.0 ? 0.0 : 1.0);
85 | }
86 |
87 | vec2 rotate2D(vec2 pt, float a) {
88 | float c = cos(a);
89 | float s = sin(a);
90 |
91 | mat2 r = mat2(c, s, -s, c);
92 |
93 | return r * pt;
94 | }
95 |
96 | vec4 DrawBars(vec2 center, vec2 uv) {
97 | float barWidth = 2.0 * M_PI * CIRCLE_RADIUS / (NUM_BARS * 1.25);
98 |
99 | vec4 resultColour = vec4(1.0, 1.0, 1.0, 0.0);
100 | vec2 position = vec2(center.x, center.y + CIRCLE_RADIUS);
101 |
102 | for(int i = 0; i < int(NUM_BARS); i++) {
103 | float frequencyUV = 0.0;
104 |
105 | if (float(i) >= NUM_BARS * 0.5) {
106 | frequencyUV = 1.0 - ((float(i) - (NUM_BARS * 0.5)) / (NUM_BARS * 0.5));
107 | } else {
108 | frequencyUV = float(i) / (NUM_BARS * 0.5);
109 | }
110 |
111 | float frequencyData = texture(audioDataTexture, vec2(frequencyUV, 0.0)).x;
112 |
113 | float barFinalHeight = BAR_HEIGHT * (0.1 + 0.9 * frequencyData);
114 | vec2 barDimensions = vec2(barWidth, barFinalHeight);
115 | vec2 barUvs = rotate2D(uv - center, (2.0 * M_PI * float(i)) / NUM_BARS) + center;
116 |
117 | resultColour.w += sdfBar(position, barDimensions, barUvs, frequencyData);
118 | }
119 |
120 | float d = saturate(1.1 * ((distance(uv, center) - CIRCLE_RADIUS) / BAR_HEIGHT));
121 | d = smoothstep(0.0, 1.0, d);
122 | d = 0.45 + 0.55 * d;
123 | resultColour.xyz *= pal(d, vec3(0.5,0.5,0.5),vec3(0.5,0.5,0.5),vec3(1.0,1.0,1.0),vec3(0.0,0.20,0.30) );
124 | resultColour.xyz *= resultColour.w;
125 |
126 | return saturate(resultColour);
127 | }
128 |
129 |
130 | vec4 AudioVisualizer() {
131 | float aspect = iResolution.x / iResolution.y;
132 | vec2 uv = vUv * vec2(aspect, 1.0);
133 |
134 | vec2 circleCenter = vec2(aspect * 0.5, 0.5);
135 |
136 | return DrawBars(circleCenter, uv);
137 | }
138 | `;
139 |
140 |
141 | function clamp(x, a, b) {
142 | return Math.min(Math.max(x, a), b);
143 | }
144 |
145 | const KEYS = {
146 | 'a': 65,
147 | 's': 83,
148 | 'w': 87,
149 | 'd': 68,
150 | };
151 |
152 | class InputController {
153 | constructor(target) {
154 | this.target_ = target || document;
155 | this.initialize_();
156 | }
157 |
158 | initialize_() {
159 | this.current_ = {
160 | leftButton: false,
161 | rightButton: false,
162 | mouseXDelta: 0,
163 | mouseYDelta: 0,
164 | mouseX: 0,
165 | mouseY: 0,
166 | };
167 | this.previous_ = null;
168 | this.keys_ = {};
169 | this.previousKeys_ = {};
170 | this.target_.addEventListener('mousedown', (e) => this.onMouseDown_(e), false);
171 | this.target_.addEventListener('mousemove', (e) => this.onMouseMove_(e), false);
172 | this.target_.addEventListener('mouseup', (e) => this.onMouseUp_(e), false);
173 | this.target_.addEventListener('keydown', (e) => this.onKeyDown_(e), false);
174 | this.target_.addEventListener('keyup', (e) => this.onKeyUp_(e), false);
175 | }
176 |
177 | onMouseMove_(e) {
178 | this.current_.mouseX = e.pageX - window.innerWidth / 2;
179 | this.current_.mouseY = e.pageY - window.innerHeight / 2;
180 |
181 | if (this.previous_ === null) {
182 | this.previous_ = {...this.current_};
183 | }
184 |
185 | this.current_.mouseXDelta = this.current_.mouseX - this.previous_.mouseX;
186 | this.current_.mouseYDelta = this.current_.mouseY - this.previous_.mouseY;
187 | }
188 |
189 | onMouseDown_(e) {
190 | this.onMouseMove_(e);
191 |
192 | switch (e.button) {
193 | case 0: {
194 | this.current_.leftButton = true;
195 | break;
196 | }
197 | case 2: {
198 | this.current_.rightButton = true;
199 | break;
200 | }
201 | }
202 | }
203 |
204 | onMouseUp_(e) {
205 | this.onMouseMove_(e);
206 |
207 | switch (e.button) {
208 | case 0: {
209 | this.current_.leftButton = false;
210 | break;
211 | }
212 | case 2: {
213 | this.current_.rightButton = false;
214 | break;
215 | }
216 | }
217 | }
218 |
219 | onKeyDown_(e) {
220 | this.keys_[e.keyCode] = true;
221 | }
222 |
223 | onKeyUp_(e) {
224 | this.keys_[e.keyCode] = false;
225 | }
226 |
227 | key(keyCode) {
228 | return !!this.keys_[keyCode];
229 | }
230 |
231 | isReady() {
232 | return this.previous_ !== null;
233 | }
234 |
235 | update(_) {
236 | if (this.previous_ !== null) {
237 | this.current_.mouseXDelta = this.current_.mouseX - this.previous_.mouseX;
238 | this.current_.mouseYDelta = this.current_.mouseY - this.previous_.mouseY;
239 |
240 | this.previous_ = {...this.current_};
241 | }
242 | }
243 | };
244 |
245 |
246 |
247 | class FirstPersonCamera {
248 | constructor(camera, objects) {
249 | this.camera_ = camera;
250 | this.input_ = new InputController();
251 | this.phi_ = 0;
252 | this.phiSpeed_ = 8;
253 | this.theta_ = 0;
254 | this.thetaSpeed_ = 5;
255 | this.movementSpeed_ = 10;
256 | this.rotation_ = new THREE.Quaternion();
257 | this.translation_ = new THREE.Vector3(30, 2, 0);
258 | this.bobTimer_ = 0;
259 | this.bobMagnitude_ = 0.175;
260 | this.bobFrequency_ = 10;
261 | this.objects_ = objects;
262 | }
263 |
264 | update(timeElapsedS) {
265 | if (this.input_.isReady()) {
266 | this.updateRotation_(timeElapsedS);
267 | this.updateTranslation_(timeElapsedS);
268 | this.updateBob_(timeElapsedS);
269 | this.updateCamera_(timeElapsedS);
270 | }
271 |
272 | this.input_.update(timeElapsedS);
273 | }
274 |
275 | updateBob_(timeElapsedS) {
276 | if (this.bobActive_) {
277 | const waveLength = Math.PI;
278 | const nextStep = 1 + Math.floor(((this.bobTimer_ + 0.000001) * this.bobFrequency_) / waveLength);
279 | const nextStepTime = nextStep * waveLength / this.bobFrequency_;
280 | this.bobTimer_ = Math.min(this.bobTimer_ + timeElapsedS, nextStepTime);
281 |
282 | if (this.bobTimer_ == nextStepTime) {
283 | this.bobActive_ = false;
284 | this.bobTimer_ = 0;
285 | }
286 | }
287 | }
288 |
289 | updateCamera_(timeElapsedS) {
290 | this.camera_.quaternion.copy(this.rotation_);
291 | this.camera_.position.copy(this.translation_);
292 | this.camera_.position.y += Math.sin(this.bobTimer_ * this.bobFrequency_) * this.bobMagnitude_;
293 |
294 | const forward = new THREE.Vector3(0, 0, -1);
295 | forward.applyQuaternion(this.rotation_);
296 |
297 | const dir = forward.clone();
298 |
299 | forward.multiplyScalar(100);
300 | forward.add(this.translation_);
301 |
302 | let closest = forward;
303 | const result = new THREE.Vector3();
304 | const ray = new THREE.Ray(this.translation_, dir);
305 | for (let i = 0; i < this.objects_.length; ++i) {
306 | if (ray.intersectBox(this.objects_[i], result)) {
307 | if (result.distanceTo(ray.origin) < closest.distanceTo(ray.origin)) {
308 | closest = result.clone();
309 | }
310 | }
311 | }
312 |
313 | this.camera_.lookAt(closest);
314 | }
315 |
316 | updateTranslation_(timeElapsedS) {
317 | const forwardVelocity = ((this.input_.key(KEYS.w) ? 1 : 0) + (this.input_.key(KEYS.s) ? -1 : 0));
318 | const strafeVelocity = ((this.input_.key(KEYS.a) ? 1 : 0) + (this.input_.key(KEYS.d) ? -1 : 0));
319 |
320 | const qx = new THREE.Quaternion();
321 | qx.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.phi_);
322 |
323 | const forward = new THREE.Vector3(0, 0, -1);
324 | forward.applyQuaternion(qx);
325 | forward.multiplyScalar(forwardVelocity * this.movementSpeed_ * timeElapsedS);
326 |
327 | const left = new THREE.Vector3(-1, 0, 0);
328 | left.applyQuaternion(qx);
329 | left.multiplyScalar(strafeVelocity * this.movementSpeed_ * timeElapsedS);
330 |
331 | this.translation_.add(forward);
332 | this.translation_.add(left);
333 |
334 | if(forwardVelocity != 0 || strafeVelocity != 0) {
335 | this.bobActive_ = true;
336 | }
337 | }
338 |
339 | updateRotation_(timeElapsedS) {
340 | const xh = this.input_.current_.mouseXDelta / window.innerWidth;
341 | const yh = this.input_.current_.mouseYDelta / window.innerHeight;
342 |
343 | this.phi_ += -xh * this.phiSpeed_;
344 | this.theta_ = clamp(this.theta_ + -yh * this.thetaSpeed_, -Math.PI / 3, Math.PI / 3);
345 |
346 | // console.log(this.input_.current_.mouseYDelta);
347 |
348 | const qx = new THREE.Quaternion();
349 | qx.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.phi_);
350 | const qz = new THREE.Quaternion();
351 | qz.setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.theta_);
352 |
353 | const q = new THREE.Quaternion();
354 | q.multiply(qx);
355 | q.multiply(qz);
356 |
357 | const t = 1.0 - Math.pow(0.001, 5 * timeElapsedS);
358 | this.rotation_.slerp(q, t);
359 | }
360 | };
361 |
362 |
363 |
364 | class LinearSpline {
365 | constructor(lerp) {
366 | this.points_ = [];
367 | this._lerp = lerp;
368 | }
369 |
370 | AddPoint(t, d) {
371 | this.points_.push([t, d]);
372 | }
373 |
374 | Get(t) {
375 | let p1 = 0;
376 |
377 | for (let i = 0; i < this.points_.length; i++) {
378 | if (this.points_[i][0] >= t) {
379 | break;
380 | }
381 | p1 = i;
382 | }
383 |
384 | const p2 = Math.min(this.points_.length - 1, p1 + 1);
385 |
386 | if (p1 == p2) {
387 | return this.points_[p1][1];
388 | }
389 |
390 | return this._lerp(
391 | (t - this.points_[p1][0]) / (
392 | this.points_[p2][0] - this.points_[p1][0]),
393 | this.points_[p1][1], this.points_[p2][1]);
394 | }
395 | }
396 |
397 |
398 | class FirstPersonCameraDemo {
399 | constructor() {
400 | this.initialize_();
401 | }
402 |
403 | initialize_() {
404 | this.initializeRenderer_();
405 | this.initializeScene_();
406 | this.initializePostFX_();
407 | this.initializeAudio_();
408 |
409 | this.previousRAF_ = null;
410 | this.raf_();
411 | this.onWindowResize_();
412 | }
413 |
414 | initializeAudio_() {
415 | this.listener_ = new THREE.AudioListener();
416 | this.camera_.add(this.listener_);
417 |
418 | const sound1 = new THREE.PositionalAudio(this.listener_);
419 | const sound2 = new THREE.PositionalAudio(this.listener_);
420 |
421 | this.speakerMesh1_.add(sound1);
422 | this.speakerMesh2_.add(sound2);
423 |
424 | const loader = new THREE.AudioLoader();
425 | loader.load('resources/music/Ectoplasm.mp3', (buffer) => {
426 | setTimeout(() => {
427 | sound1.setBuffer(buffer);
428 | sound1.setLoop(true);
429 | sound1.setVolume(1.0);
430 | sound1.setRefDistance(1);
431 | sound1.play();
432 | this.analyzer1_ = new THREE.AudioAnalyser(sound1, 32);
433 | this.analyzer1Data_ = [];
434 | }, 5000);
435 | });
436 |
437 | loader.load('resources/music/AcousticRock.mp3', (buffer) => {
438 | setTimeout(() => {
439 | sound2.setBuffer(buffer);
440 | sound2.setLoop(true);
441 | sound2.setVolume(1.0);
442 | sound2.setRefDistance(1);
443 | sound2.play();
444 | this.analyzer2_ = new THREE.AudioAnalyser(sound2, 128);
445 | this.analyzer2Texture_ = new THREE.DataTexture(
446 | this.analyzer2_.data, 64, 1, THREE.RedFormat);
447 | this.analyzer2Texture_.magFilter = THREE.LinearFilter;
448 | }, 5000);
449 | });
450 |
451 | this.indexTimer_ = 0;
452 | this.noise1_ = new noise.Noise({
453 | octaves: 3,
454 | persistence: 0.5,
455 | lacunarity: 1.6,
456 | exponentiation: 1.0,
457 | height: 1.0,
458 | scale: 0.1,
459 | seed: 1
460 | });
461 | }
462 |
463 | initializeScene_() {
464 | const distance = 50.0;
465 | const angle = Math.PI / 4.0;
466 | const penumbra = 0.5;
467 | const decay = 1.0;
468 |
469 | let light = null;
470 |
471 | light = new THREE.SpotLight(
472 | 0xFFFFFF, 100.0, distance, angle, penumbra, decay);
473 | light.castShadow = true;
474 | light.shadow.bias = -0.00001;
475 | light.shadow.mapSize.width = 4096;
476 | light.shadow.mapSize.height = 4096;
477 | light.shadow.camera.near = 1;
478 | light.shadow.camera.far = 100;
479 | light.position.set(-35, 25, 0);
480 | light.target.position.set(-40, 4, 0);
481 | this.scene_.add(light);
482 | this.scene_.add(light.target);
483 |
484 | light = new THREE.SpotLight(
485 | 0xFFFFFF, 100.0, distance, angle, penumbra, decay);
486 | light.castShadow = true;
487 | light.shadow.bias = -0.00001;
488 | light.shadow.mapSize.width = 4096;
489 | light.shadow.mapSize.height = 4096;
490 | light.shadow.camera.near = 1;
491 | light.shadow.camera.far = 100;
492 | light.position.set(35, 25, 0);
493 | light.target.position.set(40, 4, 0);
494 | this.scene_.add(light);
495 | this.scene_.add(light.target);
496 |
497 | const upColour = 0xFFFF80;
498 | const downColour = 0x808080;
499 | light = new THREE.HemisphereLight(upColour, downColour, 0.5);
500 | light.color.setHSL( 0.6, 1, 0.6 );
501 | light.groundColor.setHSL( 0.095, 1, 0.75 );
502 | light.position.set(0, 4, 0);
503 | this.scene_.add(light);
504 |
505 | const loader = new THREE.CubeTextureLoader();
506 | const texture = loader.load([
507 | './resources/skybox/posx.jpg',
508 | './resources/skybox/negx.jpg',
509 | './resources/skybox/posy.jpg',
510 | './resources/skybox/negy.jpg',
511 | './resources/skybox/posz.jpg',
512 | './resources/skybox/negz.jpg',
513 | ]);
514 |
515 | texture.encoding = THREE.sRGBEncoding;
516 | this.scene_.background = texture;
517 |
518 | const mapLoader = new THREE.TextureLoader();
519 | const maxAnisotropy = this.threejs_.capabilities.getMaxAnisotropy();
520 |
521 | const plane = new THREE.Mesh(
522 | new THREE.PlaneGeometry(100, 100, 10, 10),
523 | this.loadMaterial_('rustediron2_', 4));
524 | plane.castShadow = false;
525 | plane.receiveShadow = true;
526 | plane.rotation.x = -Math.PI / 2;
527 | this.scene_.add(plane);
528 |
529 | const concreteMaterial = this.loadMaterial_('concrete3-', 4);
530 |
531 | const wall1 = new THREE.Mesh(
532 | new THREE.BoxGeometry(100, 100, 4),
533 | concreteMaterial);
534 | wall1.position.set(0, -40, -50);
535 | wall1.castShadow = true;
536 | wall1.receiveShadow = true;
537 | this.scene_.add(wall1);
538 |
539 | const wall2 = new THREE.Mesh(
540 | new THREE.BoxGeometry(100, 100, 4),
541 | concreteMaterial);
542 | wall2.position.set(0, -40, 50);
543 | wall2.castShadow = true;
544 | wall2.receiveShadow = true;
545 | this.scene_.add(wall2);
546 |
547 | const wall3 = new THREE.Mesh(
548 | new THREE.BoxGeometry(4, 100, 100),
549 | concreteMaterial);
550 | wall3.position.set(50, -40, 0);
551 | wall3.castShadow = true;
552 | wall3.receiveShadow = true;
553 | this.scene_.add(wall3);
554 |
555 | const wall4 = new THREE.Mesh(
556 | new THREE.BoxGeometry(4, 100, 100),
557 | concreteMaterial);
558 | wall4.position.set(-50, -40, 0);
559 | wall4.castShadow = true;
560 | wall4.receiveShadow = true;
561 | this.scene_.add(wall4);
562 |
563 | const speaker1Material = this.loadMaterial_('worn_metal4_', 1);
564 | const speaker1 = new THREE.Mesh(
565 | new THREE.BoxGeometry(1, 8, 4),
566 | speaker1Material);
567 | speaker1.position.set(-40, 4, 0);
568 | speaker1.castShadow = true;
569 | speaker1.receiveShadow = true;
570 | this.scene_.add(speaker1);
571 |
572 | const speaker1Geo = new THREE.BoxGeometry(0.25, 0.25, 0.25);
573 | const speaker1BoxMaterial = this.loadMaterial_('broken_down_concrete2_', 1);
574 | this.speakerMeshes1_ = [];
575 | const speaker1Group = new THREE.Group();
576 | speaker1Group.position.x = 0.5 + 0.125;
577 |
578 | for (let x = -5; x <= 5; ++x) {
579 | const row = [];
580 | for (let y = 0; y < 16; ++y) {
581 | const speaker1_1 = new THREE.Mesh(
582 | speaker1Geo,
583 | speaker1BoxMaterial.clone());
584 | speaker1_1.position.set(0, y*0.35 - 3, x * 0.35);
585 | speaker1_1.castShadow = true;
586 | speaker1_1.receiveShadow = true;
587 | speaker1Group.add(speaker1_1);
588 | row.push(speaker1_1);
589 | }
590 | this.speakerMeshes1_.push(row);
591 | }
592 | speaker1.add(speaker1Group);
593 |
594 | this.speakerMesh1_ = speaker1;
595 |
596 | const speaker2 = new THREE.Mesh(
597 | new THREE.BoxGeometry(1, 8, 4),
598 | new THREE.MeshStandardMaterial({color: 0x404040, roughness: 0.1, metalness: 0 }));
599 | speaker2.position.set(40, 4, 0);
600 | speaker2.castShadow = true;
601 | speaker2.receiveShadow = true;
602 | this.scene_.add(speaker2);
603 |
604 | this.speakerMesh2_ = speaker2;
605 |
606 | const diffuseMap = mapLoader.load('resources/background-grey-dots.png');
607 | diffuseMap.anisotropy = maxAnisotropy;
608 |
609 | const visualizerMaterial = new THREE.MeshStandardMaterial({
610 | map: diffuseMap,
611 | normalMap: mapLoader.load('resources/freepbr/flaking-plaster_normal-ogl.png'),
612 | roughnessMap: mapLoader.load('resources/freepbr/flaking-plaster_roughness.png'),
613 | metalnessMap: mapLoader.load('resources/freepbr/flaking-plaster_metallic.png'),
614 | });
615 |
616 | visualizerMaterial.onBeforeCompile = (shader) => {
617 | shader.uniforms.iTime = { value: 0.0 };
618 | shader.uniforms.iResolution = {value: new THREE.Vector2(128, 256)};
619 | shader.uniforms.audioDataTexture = {value: null};
620 |
621 | shader.fragmentShader = shader.fragmentShader.replace('void main()', FS_DECLARATIONS + 'void main()');
622 | shader.fragmentShader = shader.fragmentShader.replace('totalEmissiveRadiance = emissive;', `
623 |
624 | totalEmissiveRadiance = emissive + AudioVisualizer().xyz;
625 |
626 | `);
627 | visualizerMaterial.userData.shader = shader;
628 | };
629 |
630 | visualizerMaterial.customProgramCacheKey = () => {
631 | return 'visualizerMaterial';
632 | };
633 |
634 | this.speaker2Material_ = visualizerMaterial;
635 |
636 | const speaker2Screen = new THREE.Mesh(
637 | new THREE.PlaneGeometry(4, 8),
638 | this.speaker2Material_);
639 | speaker2Screen.castShadow = false;
640 | speaker2Screen.receiveShadow = true;
641 | speaker2Screen.rotation.y = -Math.PI / 2;
642 | speaker2Screen.position.x -= 0.51;
643 | this.speakerMesh2_.add(speaker2Screen);
644 |
645 | // Create Box3 for each mesh in the scene so that we can
646 | // do some easy intersection tests.
647 | const meshes = [
648 | plane, wall1, wall2, wall3, wall4];
649 |
650 | this.objects_ = [];
651 |
652 | for (let i = 0; i < meshes.length; ++i) {
653 | const b = new THREE.Box3();
654 | b.setFromObject(meshes[i]);
655 | this.objects_.push(b);
656 | }
657 |
658 | this.fpsCamera_ = new FirstPersonCamera(this.camera_, this.objects_);
659 |
660 | // Crosshair
661 | const crosshair = mapLoader.load('resources/crosshair.png');
662 | crosshair.anisotropy = maxAnisotropy;
663 |
664 | this.sprite_ = new THREE.Sprite(
665 | new THREE.SpriteMaterial({map: crosshair, color: 0xffffff, fog: false, depthTest: false, depthWrite: false}));
666 | this.sprite_.scale.set(0.15, 0.15 * this.camera_.aspect, 1)
667 | this.sprite_.position.set(0, 0, -10);
668 |
669 | // this.uiScene_.add(this.sprite_);
670 | }
671 |
672 | loadMaterial_(name, tiling) {
673 | const mapLoader = new THREE.TextureLoader();
674 | const maxAnisotropy = this.threejs_.capabilities.getMaxAnisotropy();
675 |
676 | const metalMap = mapLoader.load('resources/freepbr/' + name + 'metallic.png');
677 | metalMap.anisotropy = maxAnisotropy;
678 | metalMap.wrapS = THREE.RepeatWrapping;
679 | metalMap.wrapT = THREE.RepeatWrapping;
680 | metalMap.repeat.set(tiling, tiling);
681 |
682 | const albedo = mapLoader.load('resources/freepbr/' + name + 'albedo.png');
683 | albedo.anisotropy = maxAnisotropy;
684 | albedo.wrapS = THREE.RepeatWrapping;
685 | albedo.wrapT = THREE.RepeatWrapping;
686 | albedo.repeat.set(tiling, tiling);
687 |
688 | const normalMap = mapLoader.load('resources/freepbr/' + name + 'normal.png');
689 | normalMap.anisotropy = maxAnisotropy;
690 | normalMap.wrapS = THREE.RepeatWrapping;
691 | normalMap.wrapT = THREE.RepeatWrapping;
692 | normalMap.repeat.set(tiling, tiling);
693 |
694 | const roughnessMap = mapLoader.load('resources/freepbr/' + name + 'roughness.png');
695 | roughnessMap.anisotropy = maxAnisotropy;
696 | roughnessMap.wrapS = THREE.RepeatWrapping;
697 | roughnessMap.wrapT = THREE.RepeatWrapping;
698 | roughnessMap.repeat.set(tiling, tiling);
699 |
700 | const material = new THREE.MeshStandardMaterial({
701 | metalnessMap: metalMap,
702 | map: albedo,
703 | normalMap: normalMap,
704 | roughnessMap: roughnessMap,
705 | });
706 |
707 | return material;
708 | }
709 |
710 | initializeRenderer_() {
711 | this.threejs_ = new THREE.WebGLRenderer({
712 | antialias: false,
713 | });
714 | this.threejs_.shadowMap.enabled = true;
715 | this.threejs_.shadowMap.type = THREE.PCFSoftShadowMap;
716 | this.threejs_.setPixelRatio(window.devicePixelRatio);
717 | this.threejs_.setSize(window.innerWidth, window.innerHeight);
718 | this.threejs_.physicallyCorrectLights = true;
719 | this.threejs_.autoClear = false;
720 |
721 | document.body.appendChild(this.threejs_.domElement);
722 |
723 | window.addEventListener('resize', () => {
724 | this.onWindowResize_();
725 | }, false);
726 |
727 | const fov = 60;
728 | const aspect = 1920 / 1080;
729 | const near = 1.0;
730 | const far = 1000.0;
731 | this.camera_ = new THREE.PerspectiveCamera(fov, aspect, near, far);
732 | this.camera_.position.set(-30, 2, 0);
733 |
734 | this.scene_ = new THREE.Scene();
735 |
736 | this.uiCamera_ = new THREE.OrthographicCamera(
737 | -1, 1, 1 * aspect, -1 * aspect, 1, 1000);
738 | this.uiScene_ = new THREE.Scene();
739 | }
740 |
741 | initializePostFX_() {
742 | const parameters = {
743 | minFilter: THREE.LinearFilter,
744 | magFilter: THREE.LinearFilter,
745 | format: THREE.RGBAFormat,
746 | type: THREE.FloatType,
747 | stencilBuffer: true,
748 | };
749 |
750 | const renderTarget = new THREE.WebGLRenderTarget(
751 | window.innerWidth, window.innerHeight, parameters);
752 |
753 | this.composer_ = new EffectComposer(this.threejs_, renderTarget);
754 | this.composer_.setPixelRatio(window.devicePixelRatio);
755 | this.composer_.setSize(window.innerWidth, window.innerHeight);
756 |
757 | this.fxaaPass_ = new ShaderPass(FXAAShader);
758 |
759 | const uiPass = new RenderPass(this.uiScene_, this.uiCamera_);
760 | uiPass.clear = false;
761 |
762 | this.composer_.addPass(new RenderPass(this.scene_, this.camera_));
763 | this.composer_.addPass(uiPass);
764 | this.composer_.addPass(new ShaderPass(GammaCorrectionShader));
765 | this.composer_.addPass(this.fxaaPass_);
766 | }
767 |
768 | onWindowResize_() {
769 | this.camera_.aspect = window.innerWidth / window.innerHeight;
770 | this.camera_.updateProjectionMatrix();
771 |
772 | this.uiCamera_.left = -this.camera_.aspect;
773 | this.uiCamera_.right = this.camera_.aspect;
774 | this.uiCamera_.updateProjectionMatrix();
775 |
776 | this.threejs_.setSize(window.innerWidth, window.innerHeight);
777 | this.composer_.setSize(window.innerWidth, window.innerHeight);
778 |
779 | const pixelRatio = this.threejs_.getPixelRatio();
780 | this.fxaaPass_.material.uniforms['resolution'].value.x = 1 / (
781 | window.innerWidth * pixelRatio);
782 | this.fxaaPass_.material.uniforms['resolution'].value.y = 1 / (
783 | window.innerHeight * pixelRatio);
784 | }
785 |
786 | raf_() {
787 | requestAnimationFrame((t) => {
788 | if (this.previousRAF_ === null) {
789 | this.previousRAF_ = t;
790 | }
791 |
792 | this.step_(t - this.previousRAF_);
793 | this.composer_.render();
794 |
795 | this.previousRAF_ = t;
796 | this.raf_();
797 | });
798 | }
799 |
800 | step_(timeElapsed) {
801 | const timeElapsedS = timeElapsed * 0.001;
802 |
803 | this.fpsCamera_.update(timeElapsedS);
804 |
805 | if (this.analyzer1_) {
806 | this.indexTimer_ += timeElapsedS * 0.1;
807 |
808 | this.analyzer1Data_.push([...this.analyzer1_.getFrequencyData()]);
809 | const rows = this.speakerMeshes1_.length;
810 | if (this.analyzer1Data_.length > rows) {
811 | this.analyzer1Data_.shift();
812 | }
813 |
814 | const colourSpline = new LinearSpline((t, a, b) => {
815 | const c = a.clone();
816 | return c.lerp(b, t);
817 | });
818 | colourSpline.AddPoint(0.0, new THREE.Color(0x4040FF));
819 | colourSpline.AddPoint(0.25, new THREE.Color(0xFF4040));
820 | colourSpline.AddPoint(1.0, new THREE.Color(0xFFFF80));
821 |
822 | const remap = [15, 13, 11, 9, 7, 5, 3, 1, 0, 2, 4, 6, 8, 10, 12, 14];
823 | for (let r = 0; r < this.analyzer1Data_.length; ++r) {
824 | const data = this.analyzer1Data_[r];
825 | const speakerRow = this.speakerMeshes1_[r];
826 | for (let i = 0; i < data.length; ++i) {
827 | const freqScale = math.smootherstep((data[remap[i]]/255) ** 0.5, 0, 1);
828 | const sc = 1 + 6 * freqScale + this.noise1_.Get(this.indexTimer_, r * 0.42142, i * 0.3455);
829 | speakerRow[i].scale.set(sc, 1, 1);
830 | speakerRow[i].material.color.copy(colourSpline.Get(freqScale));
831 | speakerRow[i].material.emissive.copy(colourSpline.Get(freqScale));
832 | speakerRow[i].material.emissive.multiplyScalar(freqScale ** 2);
833 | }
834 | }
835 | }
836 |
837 | if (this.analyzer2_ && this.speaker2Material_ && this.speaker2Material_.userData.shader) {
838 | this.analyzer2_.getFrequencyData();
839 | this.speaker2Material_.userData.shader.uniforms.audioDataTexture.value = this.analyzer2Texture_;
840 | this.speaker2Material_.userData.shader.uniforms.iTime.value += timeElapsedS;
841 | this.speaker2Material_.userData.shader.uniforms.audioDataTexture.value.needsUpdate = true;
842 | }
843 | }
844 | }
845 |
846 |
847 | let _APP = null;
848 |
849 | window.addEventListener('DOMContentLoaded', () => {
850 | const _Setup = () => {
851 | _APP = new FirstPersonCameraDemo();
852 | document.body.removeEventListener('click', _Setup);
853 | };
854 | document.body.addEventListener('click', _Setup);
855 | });
856 |
--------------------------------------------------------------------------------
/math.js:
--------------------------------------------------------------------------------
1 | export const math = (function() {
2 | return {
3 | rand_range: function(a, b) {
4 | return Math.random() * (b - a) + a;
5 | },
6 |
7 | rand_normalish: function() {
8 | const r = Math.random() + Math.random() + Math.random() + Math.random();
9 | return (r / 4.0) * 2.0 - 1;
10 | },
11 |
12 | rand_int: function(a, b) {
13 | return Math.round(Math.random() * (b - a) + a);
14 | },
15 |
16 | lerp: function(x, a, b) {
17 | return x * (b - a) + a;
18 | },
19 |
20 | smoothstep: function(x, a, b) {
21 | x = x * x * (3.0 - 2.0 * x);
22 | return x * (b - a) + a;
23 | },
24 |
25 | smootherstep: function(x, a, b) {
26 | x = x * x * x * (x * (x * 6 - 15) + 10);
27 | return x * (b - a) + a;
28 | },
29 |
30 | clamp: function(x, a, b) {
31 | return Math.min(Math.max(x, a), b);
32 | },
33 |
34 | sat: function(x) {
35 | return Math.min(Math.max(x, 0.0), 1.0);
36 | },
37 |
38 | in_range: (x, a, b) => {
39 | return x >= a && x <= b;
40 | },
41 | };
42 | })();
43 |
--------------------------------------------------------------------------------
/noise.js:
--------------------------------------------------------------------------------
1 | import {simplex} from './simplex-noise.js';
2 |
3 |
4 | export const noise = (function() {
5 |
6 | class _NoiseGenerator {
7 | constructor(params) {
8 | this._params = params;
9 | this._Init();
10 | }
11 |
12 | _Init() {
13 | this._noise = new simplex.SimplexNoise(this._params.seed);
14 | }
15 |
16 | Get(x, y, z) {
17 | const G = 2.0 ** (-this._params.persistence);
18 | const xs = x / this._params.scale;
19 | const ys = y / this._params.scale;
20 | const zs = z / this._params.scale;
21 | const noiseFunc = this._noise;
22 |
23 | let amplitude = 1.0;
24 | let frequency = 1.0;
25 | let normalization = 0;
26 | let total = 0;
27 | for (let o = 0; o < this._params.octaves; o++) {
28 | const noiseValue = noiseFunc.noise3D(
29 | xs * frequency, ys * frequency, zs * frequency) * 0.5 + 0.5;
30 |
31 | total += noiseValue * amplitude;
32 | normalization += amplitude;
33 | amplitude *= G;
34 | frequency *= this._params.lacunarity;
35 | }
36 | total /= normalization;
37 | return Math.pow(
38 | total, this._params.exponentiation) * this._params.height;
39 | }
40 | }
41 |
42 | return {
43 | Noise: _NoiseGenerator
44 | }
45 | })();
46 |
--------------------------------------------------------------------------------
/resources/background-grey-dots.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/background-grey-dots.png
--------------------------------------------------------------------------------
/resources/crosshair.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/crosshair.png
--------------------------------------------------------------------------------
/resources/fire.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/fire.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_Height.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_Height.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_albedo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_albedo.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_ao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_ao.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_metallic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_metallic.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_normal.png
--------------------------------------------------------------------------------
/resources/freepbr/broken_down_concrete2_roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/broken_down_concrete2_roughness.png
--------------------------------------------------------------------------------
/resources/freepbr/concrete3-albedo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/concrete3-albedo.png
--------------------------------------------------------------------------------
/resources/freepbr/concrete3-metallic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/concrete3-metallic.png
--------------------------------------------------------------------------------
/resources/freepbr/concrete3-normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/concrete3-normal.png
--------------------------------------------------------------------------------
/resources/freepbr/concrete3-roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/concrete3-roughness.png
--------------------------------------------------------------------------------
/resources/freepbr/flaking-plaster_albedo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/flaking-plaster_albedo.png
--------------------------------------------------------------------------------
/resources/freepbr/flaking-plaster_ao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/flaking-plaster_ao.png
--------------------------------------------------------------------------------
/resources/freepbr/flaking-plaster_metallic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/flaking-plaster_metallic.png
--------------------------------------------------------------------------------
/resources/freepbr/flaking-plaster_normal-ogl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/flaking-plaster_normal-ogl.png
--------------------------------------------------------------------------------
/resources/freepbr/flaking-plaster_roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/flaking-plaster_roughness.png
--------------------------------------------------------------------------------
/resources/freepbr/readme.txt:
--------------------------------------------------------------------------------
1 | These textures were all taken from https://freepbr.com/
2 |
3 |
--------------------------------------------------------------------------------
/resources/freepbr/rustediron2_albedo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/rustediron2_albedo.png
--------------------------------------------------------------------------------
/resources/freepbr/rustediron2_metallic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/rustediron2_metallic.png
--------------------------------------------------------------------------------
/resources/freepbr/rustediron2_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/rustediron2_normal.png
--------------------------------------------------------------------------------
/resources/freepbr/rustediron2_roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/rustediron2_roughness.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_Height.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_Height.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_albedo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_albedo.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_ao.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_ao.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_metallic.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_metallic.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_normal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_normal.png
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_preview.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_preview.jpg
--------------------------------------------------------------------------------
/resources/freepbr/worn_metal4_roughness.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/freepbr/worn_metal4_roughness.png
--------------------------------------------------------------------------------
/resources/music/AcousticRock.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/music/AcousticRock.mp3
--------------------------------------------------------------------------------
/resources/music/Ectoplasm.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/music/Ectoplasm.mp3
--------------------------------------------------------------------------------
/resources/music/readme.txt:
--------------------------------------------------------------------------------
1 | Music: https://audionautix.com/
2 |
--------------------------------------------------------------------------------
/resources/skybox/negx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/negx.jpg
--------------------------------------------------------------------------------
/resources/skybox/negy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/negy.jpg
--------------------------------------------------------------------------------
/resources/skybox/negz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/negz.jpg
--------------------------------------------------------------------------------
/resources/skybox/posx.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/posx.jpg
--------------------------------------------------------------------------------
/resources/skybox/posy.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/posy.jpg
--------------------------------------------------------------------------------
/resources/skybox/posz.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/skybox/posz.jpg
--------------------------------------------------------------------------------
/resources/skybox/readme.txt:
--------------------------------------------------------------------------------
1 | Author
2 | ======
3 |
4 | This is the work of Emil Persson, aka Humus.
5 | http://www.humus.name
6 |
7 |
8 |
9 | License
10 | =======
11 |
12 | This work is licensed under a Creative Commons Attribution 3.0 Unported License.
13 | http://creativecommons.org/licenses/by/3.0/
14 |
--------------------------------------------------------------------------------
/resources/speaker.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/simondevyoutube/ThreeJS_Tutorial_3DSound/80fc5ad21f72c6b4d54cc388ff00228aa4d2253a/resources/speaker.png
--------------------------------------------------------------------------------
/simplex-noise.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A fast javascript implementation of simplex noise by Jonas Wagner
3 |
4 | Based on a speed-improved simplex noise algorithm for 2D, 3D and 4D in Java.
5 | Which is based on example code by Stefan Gustavson (stegu@itn.liu.se).
6 | With Optimisations by Peter Eastman (peastman@drizzle.stanford.edu).
7 | Better rank ordering method by Stefan Gustavson in 2012.
8 |
9 |
10 | Copyright (c) 2018 Jonas Wagner
11 |
12 | Permission is hereby granted, free of charge, to any person obtaining a copy
13 | of this software and associated documentation files (the "Software"), to deal
14 | in the Software without restriction, including without limitation the rights
15 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
16 | copies of the Software, and to permit persons to whom the Software is
17 | furnished to do so, subject to the following conditions:
18 |
19 | The above copyright notice and this permission notice shall be included in all
20 | copies or substantial portions of the Software.
21 |
22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
28 | SOFTWARE.
29 | */
30 | // (function() {
31 |
32 | export const simplex = (function() {
33 |
34 | 'use strict';
35 |
36 | var F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
37 | var G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
38 | var F3 = 1.0 / 3.0;
39 | var G3 = 1.0 / 6.0;
40 | var F4 = (Math.sqrt(5.0) - 1.0) / 4.0;
41 | var G4 = (5.0 - Math.sqrt(5.0)) / 20.0;
42 |
43 | function SimplexNoise(randomOrSeed) {
44 | var random;
45 | if (typeof randomOrSeed == 'function') {
46 | random = randomOrSeed;
47 | }
48 | else if (randomOrSeed) {
49 | random = alea(randomOrSeed);
50 | } else {
51 | random = Math.random;
52 | }
53 | this.p = buildPermutationTable(random);
54 | this.perm = new Uint8Array(512);
55 | this.permMod12 = new Uint8Array(512);
56 | for (var i = 0; i < 512; i++) {
57 | this.perm[i] = this.p[i & 255];
58 | this.permMod12[i] = this.perm[i] % 12;
59 | }
60 |
61 | }
62 | SimplexNoise.prototype = {
63 | grad3: new Float32Array([1, 1, 0,
64 | -1, 1, 0,
65 | 1, -1, 0,
66 |
67 | -1, -1, 0,
68 | 1, 0, 1,
69 | -1, 0, 1,
70 |
71 | 1, 0, -1,
72 | -1, 0, -1,
73 | 0, 1, 1,
74 |
75 | 0, -1, 1,
76 | 0, 1, -1,
77 | 0, -1, -1]),
78 | grad4: new Float32Array([0, 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1,
79 | 0, -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1,
80 | 1, 0, 1, 1, 1, 0, 1, -1, 1, 0, -1, 1, 1, 0, -1, -1,
81 | -1, 0, 1, 1, -1, 0, 1, -1, -1, 0, -1, 1, -1, 0, -1, -1,
82 | 1, 1, 0, 1, 1, 1, 0, -1, 1, -1, 0, 1, 1, -1, 0, -1,
83 | -1, 1, 0, 1, -1, 1, 0, -1, -1, -1, 0, 1, -1, -1, 0, -1,
84 | 1, 1, 1, 0, 1, 1, -1, 0, 1, -1, 1, 0, 1, -1, -1, 0,
85 | -1, 1, 1, 0, -1, 1, -1, 0, -1, -1, 1, 0, -1, -1, -1, 0]),
86 | noise2D: function(xin, yin) {
87 | var permMod12 = this.permMod12;
88 | var perm = this.perm;
89 | var grad3 = this.grad3;
90 | var n0 = 0; // Noise contributions from the three corners
91 | var n1 = 0;
92 | var n2 = 0;
93 | // Skew the input space to determine which simplex cell we're in
94 | var s = (xin + yin) * F2; // Hairy factor for 2D
95 | var i = Math.floor(xin + s);
96 | var j = Math.floor(yin + s);
97 | var t = (i + j) * G2;
98 | var X0 = i - t; // Unskew the cell origin back to (x,y) space
99 | var Y0 = j - t;
100 | var x0 = xin - X0; // The x,y distances from the cell origin
101 | var y0 = yin - Y0;
102 | // For the 2D case, the simplex shape is an equilateral triangle.
103 | // Determine which simplex we are in.
104 | var i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords
105 | if (x0 > y0) {
106 | i1 = 1;
107 | j1 = 0;
108 | } // lower triangle, XY order: (0,0)->(1,0)->(1,1)
109 | else {
110 | i1 = 0;
111 | j1 = 1;
112 | } // upper triangle, YX order: (0,0)->(0,1)->(1,1)
113 | // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and
114 | // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where
115 | // c = (3-sqrt(3))/6
116 | var x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords
117 | var y1 = y0 - j1 + G2;
118 | var x2 = x0 - 1.0 + 2.0 * G2; // Offsets for last corner in (x,y) unskewed coords
119 | var y2 = y0 - 1.0 + 2.0 * G2;
120 | // Work out the hashed gradient indices of the three simplex corners
121 | var ii = i & 255;
122 | var jj = j & 255;
123 | // Calculate the contribution from the three corners
124 | var t0 = 0.5 - x0 * x0 - y0 * y0;
125 | if (t0 >= 0) {
126 | var gi0 = permMod12[ii + perm[jj]] * 3;
127 | t0 *= t0;
128 | n0 = t0 * t0 * (grad3[gi0] * x0 + grad3[gi0 + 1] * y0); // (x,y) of grad3 used for 2D gradient
129 | }
130 | var t1 = 0.5 - x1 * x1 - y1 * y1;
131 | if (t1 >= 0) {
132 | var gi1 = permMod12[ii + i1 + perm[jj + j1]] * 3;
133 | t1 *= t1;
134 | n1 = t1 * t1 * (grad3[gi1] * x1 + grad3[gi1 + 1] * y1);
135 | }
136 | var t2 = 0.5 - x2 * x2 - y2 * y2;
137 | if (t2 >= 0) {
138 | var gi2 = permMod12[ii + 1 + perm[jj + 1]] * 3;
139 | t2 *= t2;
140 | n2 = t2 * t2 * (grad3[gi2] * x2 + grad3[gi2 + 1] * y2);
141 | }
142 | // Add contributions from each corner to get the final noise value.
143 | // The result is scaled to return values in the interval [-1,1].
144 | return 70.0 * (n0 + n1 + n2);
145 | },
146 | // 3D simplex noise
147 | noise3D: function(xin, yin, zin) {
148 | var permMod12 = this.permMod12;
149 | var perm = this.perm;
150 | var grad3 = this.grad3;
151 | var n0, n1, n2, n3; // Noise contributions from the four corners
152 | // Skew the input space to determine which simplex cell we're in
153 | var s = (xin + yin + zin) * F3; // Very nice and simple skew factor for 3D
154 | var i = Math.floor(xin + s);
155 | var j = Math.floor(yin + s);
156 | var k = Math.floor(zin + s);
157 | var t = (i + j + k) * G3;
158 | var X0 = i - t; // Unskew the cell origin back to (x,y,z) space
159 | var Y0 = j - t;
160 | var Z0 = k - t;
161 | var x0 = xin - X0; // The x,y,z distances from the cell origin
162 | var y0 = yin - Y0;
163 | var z0 = zin - Z0;
164 | // For the 3D case, the simplex shape is a slightly irregular tetrahedron.
165 | // Determine which simplex we are in.
166 | var i1, j1, k1; // Offsets for second corner of simplex in (i,j,k) coords
167 | var i2, j2, k2; // Offsets for third corner of simplex in (i,j,k) coords
168 | if (x0 >= y0) {
169 | if (y0 >= z0) {
170 | i1 = 1;
171 | j1 = 0;
172 | k1 = 0;
173 | i2 = 1;
174 | j2 = 1;
175 | k2 = 0;
176 | } // X Y Z order
177 | else if (x0 >= z0) {
178 | i1 = 1;
179 | j1 = 0;
180 | k1 = 0;
181 | i2 = 1;
182 | j2 = 0;
183 | k2 = 1;
184 | } // X Z Y order
185 | else {
186 | i1 = 0;
187 | j1 = 0;
188 | k1 = 1;
189 | i2 = 1;
190 | j2 = 0;
191 | k2 = 1;
192 | } // Z X Y order
193 | }
194 | else { // x0 y0) rankx++;
301 | else ranky++;
302 | if (x0 > z0) rankx++;
303 | else rankz++;
304 | if (x0 > w0) rankx++;
305 | else rankw++;
306 | if (y0 > z0) ranky++;
307 | else rankz++;
308 | if (y0 > w0) ranky++;
309 | else rankw++;
310 | if (z0 > w0) rankz++;
311 | else rankw++;
312 | var i1, j1, k1, l1; // The integer offsets for the second simplex corner
313 | var i2, j2, k2, l2; // The integer offsets for the third simplex corner
314 | var i3, j3, k3, l3; // The integer offsets for the fourth simplex corner
315 | // simplex[c] is a 4-vector with the numbers 0, 1, 2 and 3 in some order.
316 | // Many values of c will never occur, since e.g. x>y>z>w makes x= 3 ? 1 : 0;
321 | j1 = ranky >= 3 ? 1 : 0;
322 | k1 = rankz >= 3 ? 1 : 0;
323 | l1 = rankw >= 3 ? 1 : 0;
324 | // Rank 2 denotes the second largest coordinate.
325 | i2 = rankx >= 2 ? 1 : 0;
326 | j2 = ranky >= 2 ? 1 : 0;
327 | k2 = rankz >= 2 ? 1 : 0;
328 | l2 = rankw >= 2 ? 1 : 0;
329 | // Rank 1 denotes the second smallest coordinate.
330 | i3 = rankx >= 1 ? 1 : 0;
331 | j3 = ranky >= 1 ? 1 : 0;
332 | k3 = rankz >= 1 ? 1 : 0;
333 | l3 = rankw >= 1 ? 1 : 0;
334 | // The fifth corner has all coordinate offsets = 1, so no need to compute that.
335 | var x1 = x0 - i1 + G4; // Offsets for second corner in (x,y,z,w) coords
336 | var y1 = y0 - j1 + G4;
337 | var z1 = z0 - k1 + G4;
338 | var w1 = w0 - l1 + G4;
339 | var x2 = x0 - i2 + 2.0 * G4; // Offsets for third corner in (x,y,z,w) coords
340 | var y2 = y0 - j2 + 2.0 * G4;
341 | var z2 = z0 - k2 + 2.0 * G4;
342 | var w2 = w0 - l2 + 2.0 * G4;
343 | var x3 = x0 - i3 + 3.0 * G4; // Offsets for fourth corner in (x,y,z,w) coords
344 | var y3 = y0 - j3 + 3.0 * G4;
345 | var z3 = z0 - k3 + 3.0 * G4;
346 | var w3 = w0 - l3 + 3.0 * G4;
347 | var x4 = x0 - 1.0 + 4.0 * G4; // Offsets for last corner in (x,y,z,w) coords
348 | var y4 = y0 - 1.0 + 4.0 * G4;
349 | var z4 = z0 - 1.0 + 4.0 * G4;
350 | var w4 = w0 - 1.0 + 4.0 * G4;
351 | // Work out the hashed gradient indices of the five simplex corners
352 | var ii = i & 255;
353 | var jj = j & 255;
354 | var kk = k & 255;
355 | var ll = l & 255;
356 | // Calculate the contribution from the five corners
357 | var t0 = 0.6 - x0 * x0 - y0 * y0 - z0 * z0 - w0 * w0;
358 | if (t0 < 0) n0 = 0.0;
359 | else {
360 | var gi0 = (perm[ii + perm[jj + perm[kk + perm[ll]]]] % 32) * 4;
361 | t0 *= t0;
362 | n0 = t0 * t0 * (grad4[gi0] * x0 + grad4[gi0 + 1] * y0 + grad4[gi0 + 2] * z0 + grad4[gi0 + 3] * w0);
363 | }
364 | var t1 = 0.6 - x1 * x1 - y1 * y1 - z1 * z1 - w1 * w1;
365 | if (t1 < 0) n1 = 0.0;
366 | else {
367 | var gi1 = (perm[ii + i1 + perm[jj + j1 + perm[kk + k1 + perm[ll + l1]]]] % 32) * 4;
368 | t1 *= t1;
369 | n1 = t1 * t1 * (grad4[gi1] * x1 + grad4[gi1 + 1] * y1 + grad4[gi1 + 2] * z1 + grad4[gi1 + 3] * w1);
370 | }
371 | var t2 = 0.6 - x2 * x2 - y2 * y2 - z2 * z2 - w2 * w2;
372 | if (t2 < 0) n2 = 0.0;
373 | else {
374 | var gi2 = (perm[ii + i2 + perm[jj + j2 + perm[kk + k2 + perm[ll + l2]]]] % 32) * 4;
375 | t2 *= t2;
376 | n2 = t2 * t2 * (grad4[gi2] * x2 + grad4[gi2 + 1] * y2 + grad4[gi2 + 2] * z2 + grad4[gi2 + 3] * w2);
377 | }
378 | var t3 = 0.6 - x3 * x3 - y3 * y3 - z3 * z3 - w3 * w3;
379 | if (t3 < 0) n3 = 0.0;
380 | else {
381 | var gi3 = (perm[ii + i3 + perm[jj + j3 + perm[kk + k3 + perm[ll + l3]]]] % 32) * 4;
382 | t3 *= t3;
383 | n3 = t3 * t3 * (grad4[gi3] * x3 + grad4[gi3 + 1] * y3 + grad4[gi3 + 2] * z3 + grad4[gi3 + 3] * w3);
384 | }
385 | var t4 = 0.6 - x4 * x4 - y4 * y4 - z4 * z4 - w4 * w4;
386 | if (t4 < 0) n4 = 0.0;
387 | else {
388 | var gi4 = (perm[ii + 1 + perm[jj + 1 + perm[kk + 1 + perm[ll + 1]]]] % 32) * 4;
389 | t4 *= t4;
390 | n4 = t4 * t4 * (grad4[gi4] * x4 + grad4[gi4 + 1] * y4 + grad4[gi4 + 2] * z4 + grad4[gi4 + 3] * w4);
391 | }
392 | // Sum up and scale the result to cover the range [-1,1]
393 | return 27.0 * (n0 + n1 + n2 + n3 + n4);
394 | }
395 | };
396 |
397 | function buildPermutationTable(random) {
398 | var i;
399 | var p = new Uint8Array(256);
400 | for (i = 0; i < 256; i++) {
401 | p[i] = i;
402 | }
403 | for (i = 0; i < 255; i++) {
404 | var r = i + ~~(random() * (256 - i));
405 | var aux = p[i];
406 | p[i] = p[r];
407 | p[r] = aux;
408 | }
409 | return p;
410 | }
411 | SimplexNoise._buildPermutationTable = buildPermutationTable;
412 |
413 | function alea() {
414 | // Johannes Baagøe , 2010
415 | var s0 = 0;
416 | var s1 = 0;
417 | var s2 = 0;
418 | var c = 1;
419 |
420 | var mash = masher();
421 | s0 = mash(' ');
422 | s1 = mash(' ');
423 | s2 = mash(' ');
424 |
425 | for (var i = 0; i < arguments.length; i++) {
426 | s0 -= mash(arguments[i]);
427 | if (s0 < 0) {
428 | s0 += 1;
429 | }
430 | s1 -= mash(arguments[i]);
431 | if (s1 < 0) {
432 | s1 += 1;
433 | }
434 | s2 -= mash(arguments[i]);
435 | if (s2 < 0) {
436 | s2 += 1;
437 | }
438 | }
439 | mash = null;
440 | return function() {
441 | var t = 2091639 * s0 + c * 2.3283064365386963e-10; // 2^-32
442 | s0 = s1;
443 | s1 = s2;
444 | return s2 = t - (c = t | 0);
445 | };
446 | }
447 | function masher() {
448 | var n = 0xefc8249d;
449 | return function(data) {
450 | data = data.toString();
451 | for (var i = 0; i < data.length; i++) {
452 | n += data.charCodeAt(i);
453 | var h = 0.02519603282416938 * n;
454 | n = h >>> 0;
455 | h -= n;
456 | h *= n;
457 | n = h >>> 0;
458 | h -= n;
459 | n += h * 0x100000000; // 2^32
460 | }
461 | return (n >>> 0) * 2.3283064365386963e-10; // 2^-32
462 | };
463 | }
464 |
465 | // // amd
466 | // if (typeof define !== 'undefined' && define.amd) define(function() {return SimplexNoise;});
467 | // // common js
468 | // if (typeof exports !== 'undefined') exports.SimplexNoise = SimplexNoise;
469 | // // browser
470 | // else if (typeof window !== 'undefined') window.SimplexNoise = SimplexNoise;
471 | // // nodejs
472 | // if (typeof module !== 'undefined') {
473 | // module.exports = SimplexNoise;
474 | // }
475 | return {
476 | SimplexNoise: SimplexNoise
477 | };
478 |
479 | })();
--------------------------------------------------------------------------------