main.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855
  1. import * as THREE from 'https://cdn.skypack.dev/[email protected]';
  2. import {EffectComposer} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/postprocessing/EffectComposer.js';
  3. import {ShaderPass} from 'https://cdn.skypack.dev/[email protected]/examples//jsm/postprocessing/ShaderPass.js';
  4. import {GammaCorrectionShader} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/shaders/GammaCorrectionShader.js';
  5. import {RenderPass} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/postprocessing/RenderPass.js';
  6. import {FXAAShader} from 'https://cdn.skypack.dev/[email protected]/examples/jsm/shaders/FXAAShader.js';
  7. import {math} from './math.js';
  8. import {noise} from './noise.js';
  9. const FS_DECLARATIONS = `
  10. uniform sampler2D audioDataTexture;
  11. uniform vec2 iResolution;
  12. uniform float iTime;
  13. #define M_PI 3.14159
  14. #define NUM_BARS 64.0
  15. #define CIRCLE_RADIUS 0.15
  16. #define BAR_HEIGHT 0.125
  17. // All code snippets taken from Inigo Quilez's site
  18. // Make sure to check out his site!
  19. // https://iquilezles.org/
  20. //
  21. vec3 pal( in float t, in vec3 a, in vec3 b, in vec3 c, in vec3 d) {
  22. return a + b*cos( 6.28318*(c*t+d) );
  23. }
  24. float dot2(in vec2 v ) { return dot(v,v); }
  25. float sdfTrapezoid(in vec2 p, in float r1, float r2, float he) {
  26. vec2 k1 = vec2(r2,he);
  27. vec2 k2 = vec2(r2-r1,2.0*he);
  28. p.x = abs(p.x);
  29. vec2 ca = vec2(p.x-min(p.x,(p.y<0.0)?r1:r2), abs(p.y)-he);
  30. vec2 cb = p - k1 + k2*clamp( dot(k1-p,k2)/dot2(k2), 0.0, 1.0 );
  31. float s = (cb.x<0.0 && ca.y<0.0) ? -1.0 : 1.0;
  32. return s*sqrt( min(dot2(ca),dot2(cb)) );
  33. }
  34. float sdUnevenCapsule( vec2 p, float r1, float r2, float h ) {
  35. p.x = abs(p.x);
  36. float b = (r1-r2)/h;
  37. float a = sqrt(1.0-b*b);
  38. float k = dot(p,vec2(-b,a));
  39. if( k < 0.0 ) return length(p) - r1;
  40. if( k > a*h ) return length(p-vec2(0.0,h)) - r2;
  41. return dot(p, vec2(a,b) ) - r1;
  42. }
  43. float sdTriangleIsosceles( in vec2 p, in vec2 q ) {
  44. p.x = abs(p.x);
  45. vec2 a = p - q*clamp( dot(p,q)/dot(q,q), 0.0, 1.0 );
  46. vec2 b = p - q*vec2( clamp( p.x/q.x, 0.0, 1.0 ), 1.0 );
  47. float s = -sign( q.y );
  48. vec2 d = min( vec2( dot(a,a), s*(p.x*q.y-p.y*q.x) ),
  49. vec2( dot(b,b), s*(p.y-q.y) ));
  50. return -sqrt(d.x)*sign(d.y);
  51. }
  52. float opSmoothUnion( float d1, float d2, float k ) {
  53. float h = clamp( 0.5 + 0.5*(d2-d1)/k, 0.0, 1.0 );
  54. return mix( d2, d1, h ) - k*h*(1.0-h);
  55. }
  56. float opUnion( float d1, float d2 ) { return min(d1,d2); }
  57. float opIntersection( float d1, float d2 ) { return max(d1,d2); }
  58. float opSubtraction( float d1, float d2 ) { return max(-d1,d2); }
  59. float sdfBar(vec2 position, vec2 dimensions, vec2 uv, float frequencySample) {
  60. float w = mix(dimensions.x * 0.5, dimensions.x, smoothstep(0.0, 1.0, frequencySample));
  61. vec2 basePosition = uv - position + vec2(0.0, -dimensions.y * 0.5 - frequencySample * 0.05);
  62. float d = sdfTrapezoid(
  63. basePosition,
  64. dimensions.x * 0.5,
  65. w, dimensions.y * 0.5);
  66. return (d > 0.0 ? 0.0 : 1.0);
  67. }
  68. vec2 rotate2D(vec2 pt, float a) {
  69. float c = cos(a);
  70. float s = sin(a);
  71. mat2 r = mat2(c, s, -s, c);
  72. return r * pt;
  73. }
  74. vec4 DrawBars(vec2 center, vec2 uv) {
  75. float barWidth = 2.0 * M_PI * CIRCLE_RADIUS / (NUM_BARS * 1.25);
  76. vec4 resultColour = vec4(1.0, 1.0, 1.0, 0.0);
  77. vec2 position = vec2(center.x, center.y + CIRCLE_RADIUS);
  78. for(int i = 0; i < int(NUM_BARS); i++) {
  79. float frequencyUV = 0.0;
  80. if (float(i) >= NUM_BARS * 0.5) {
  81. frequencyUV = 1.0 - ((float(i) - (NUM_BARS * 0.5)) / (NUM_BARS * 0.5));
  82. } else {
  83. frequencyUV = float(i) / (NUM_BARS * 0.5);
  84. }
  85. float frequencyData = texture(audioDataTexture, vec2(frequencyUV, 0.0)).x;
  86. float barFinalHeight = BAR_HEIGHT * (0.1 + 0.9 * frequencyData);
  87. vec2 barDimensions = vec2(barWidth, barFinalHeight);
  88. vec2 barUvs = rotate2D(uv - center, (2.0 * M_PI * float(i)) / NUM_BARS) + center;
  89. resultColour.w += sdfBar(position, barDimensions, barUvs, frequencyData);
  90. }
  91. float d = saturate(1.1 * ((distance(uv, center) - CIRCLE_RADIUS) / BAR_HEIGHT));
  92. d = smoothstep(0.0, 1.0, d);
  93. d = 0.45 + 0.55 * d;
  94. 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) );
  95. resultColour.xyz *= resultColour.w;
  96. return saturate(resultColour);
  97. }
  98. vec4 AudioVisualizer() {
  99. float aspect = iResolution.x / iResolution.y;
  100. vec2 uv = vUv * vec2(aspect, 1.0);
  101. vec2 circleCenter = vec2(aspect * 0.5, 0.5);
  102. return DrawBars(circleCenter, uv);
  103. }
  104. `;
  105. function clamp(x, a, b) {
  106. return Math.min(Math.max(x, a), b);
  107. }
  108. const KEYS = {
  109. 'a': 65,
  110. 's': 83,
  111. 'w': 87,
  112. 'd': 68,
  113. };
  114. class InputController {
  115. constructor(target) {
  116. this.target_ = target || document;
  117. this.initialize_();
  118. }
  119. initialize_() {
  120. this.current_ = {
  121. leftButton: false,
  122. rightButton: false,
  123. mouseXDelta: 0,
  124. mouseYDelta: 0,
  125. mouseX: 0,
  126. mouseY: 0,
  127. };
  128. this.previous_ = null;
  129. this.keys_ = {};
  130. this.previousKeys_ = {};
  131. this.target_.addEventListener('mousedown', (e) => this.onMouseDown_(e), false);
  132. this.target_.addEventListener('mousemove', (e) => this.onMouseMove_(e), false);
  133. this.target_.addEventListener('mouseup', (e) => this.onMouseUp_(e), false);
  134. this.target_.addEventListener('keydown', (e) => this.onKeyDown_(e), false);
  135. this.target_.addEventListener('keyup', (e) => this.onKeyUp_(e), false);
  136. }
  137. onMouseMove_(e) {
  138. this.current_.mouseX = e.pageX - window.innerWidth / 2;
  139. this.current_.mouseY = e.pageY - window.innerHeight / 2;
  140. if (this.previous_ === null) {
  141. this.previous_ = {...this.current_};
  142. }
  143. this.current_.mouseXDelta = this.current_.mouseX - this.previous_.mouseX;
  144. this.current_.mouseYDelta = this.current_.mouseY - this.previous_.mouseY;
  145. }
  146. onMouseDown_(e) {
  147. this.onMouseMove_(e);
  148. switch (e.button) {
  149. case 0: {
  150. this.current_.leftButton = true;
  151. break;
  152. }
  153. case 2: {
  154. this.current_.rightButton = true;
  155. break;
  156. }
  157. }
  158. }
  159. onMouseUp_(e) {
  160. this.onMouseMove_(e);
  161. switch (e.button) {
  162. case 0: {
  163. this.current_.leftButton = false;
  164. break;
  165. }
  166. case 2: {
  167. this.current_.rightButton = false;
  168. break;
  169. }
  170. }
  171. }
  172. onKeyDown_(e) {
  173. this.keys_[e.keyCode] = true;
  174. }
  175. onKeyUp_(e) {
  176. this.keys_[e.keyCode] = false;
  177. }
  178. key(keyCode) {
  179. return !!this.keys_[keyCode];
  180. }
  181. isReady() {
  182. return this.previous_ !== null;
  183. }
  184. update(_) {
  185. if (this.previous_ !== null) {
  186. this.current_.mouseXDelta = this.current_.mouseX - this.previous_.mouseX;
  187. this.current_.mouseYDelta = this.current_.mouseY - this.previous_.mouseY;
  188. this.previous_ = {...this.current_};
  189. }
  190. }
  191. };
  192. class FirstPersonCamera {
  193. constructor(camera, objects) {
  194. this.camera_ = camera;
  195. this.input_ = new InputController();
  196. this.phi_ = 0;
  197. this.phiSpeed_ = 8;
  198. this.theta_ = 0;
  199. this.thetaSpeed_ = 5;
  200. this.movementSpeed_ = 10;
  201. this.rotation_ = new THREE.Quaternion();
  202. this.translation_ = new THREE.Vector3(30, 2, 0);
  203. this.bobTimer_ = 0;
  204. this.bobMagnitude_ = 0.175;
  205. this.bobFrequency_ = 10;
  206. this.objects_ = objects;
  207. }
  208. update(timeElapsedS) {
  209. if (this.input_.isReady()) {
  210. this.updateRotation_(timeElapsedS);
  211. this.updateTranslation_(timeElapsedS);
  212. this.updateBob_(timeElapsedS);
  213. this.updateCamera_(timeElapsedS);
  214. }
  215. this.input_.update(timeElapsedS);
  216. }
  217. updateBob_(timeElapsedS) {
  218. if (this.bobActive_) {
  219. const waveLength = Math.PI;
  220. const nextStep = 1 + Math.floor(((this.bobTimer_ + 0.000001) * this.bobFrequency_) / waveLength);
  221. const nextStepTime = nextStep * waveLength / this.bobFrequency_;
  222. this.bobTimer_ = Math.min(this.bobTimer_ + timeElapsedS, nextStepTime);
  223. if (this.bobTimer_ == nextStepTime) {
  224. this.bobActive_ = false;
  225. this.bobTimer_ = 0;
  226. }
  227. }
  228. }
  229. updateCamera_(timeElapsedS) {
  230. this.camera_.quaternion.copy(this.rotation_);
  231. this.camera_.position.copy(this.translation_);
  232. this.camera_.position.y += Math.sin(this.bobTimer_ * this.bobFrequency_) * this.bobMagnitude_;
  233. const forward = new THREE.Vector3(0, 0, -1);
  234. forward.applyQuaternion(this.rotation_);
  235. const dir = forward.clone();
  236. forward.multiplyScalar(100);
  237. forward.add(this.translation_);
  238. let closest = forward;
  239. const result = new THREE.Vector3();
  240. const ray = new THREE.Ray(this.translation_, dir);
  241. for (let i = 0; i < this.objects_.length; ++i) {
  242. if (ray.intersectBox(this.objects_[i], result)) {
  243. if (result.distanceTo(ray.origin) < closest.distanceTo(ray.origin)) {
  244. closest = result.clone();
  245. }
  246. }
  247. }
  248. this.camera_.lookAt(closest);
  249. }
  250. updateTranslation_(timeElapsedS) {
  251. const forwardVelocity = ((this.input_.key(KEYS.w) ? 1 : 0) + (this.input_.key(KEYS.s) ? -1 : 0));
  252. const strafeVelocity = ((this.input_.key(KEYS.a) ? 1 : 0) + (this.input_.key(KEYS.d) ? -1 : 0));
  253. const qx = new THREE.Quaternion();
  254. qx.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.phi_);
  255. const forward = new THREE.Vector3(0, 0, -1);
  256. forward.applyQuaternion(qx);
  257. forward.multiplyScalar(forwardVelocity * this.movementSpeed_ * timeElapsedS);
  258. const left = new THREE.Vector3(-1, 0, 0);
  259. left.applyQuaternion(qx);
  260. left.multiplyScalar(strafeVelocity * this.movementSpeed_ * timeElapsedS);
  261. this.translation_.add(forward);
  262. this.translation_.add(left);
  263. if(forwardVelocity != 0 || strafeVelocity != 0) {
  264. this.bobActive_ = true;
  265. }
  266. }
  267. updateRotation_(timeElapsedS) {
  268. const xh = this.input_.current_.mouseXDelta / window.innerWidth;
  269. const yh = this.input_.current_.mouseYDelta / window.innerHeight;
  270. this.phi_ += -xh * this.phiSpeed_;
  271. this.theta_ = clamp(this.theta_ + -yh * this.thetaSpeed_, -Math.PI / 3, Math.PI / 3);
  272. // console.log(this.input_.current_.mouseYDelta);
  273. const qx = new THREE.Quaternion();
  274. qx.setFromAxisAngle(new THREE.Vector3(0, 1, 0), this.phi_);
  275. const qz = new THREE.Quaternion();
  276. qz.setFromAxisAngle(new THREE.Vector3(1, 0, 0), this.theta_);
  277. const q = new THREE.Quaternion();
  278. q.multiply(qx);
  279. q.multiply(qz);
  280. const t = 1.0 - Math.pow(0.001, 5 * timeElapsedS);
  281. this.rotation_.slerp(q, t);
  282. }
  283. };
  284. class LinearSpline {
  285. constructor(lerp) {
  286. this.points_ = [];
  287. this._lerp = lerp;
  288. }
  289. AddPoint(t, d) {
  290. this.points_.push([t, d]);
  291. }
  292. Get(t) {
  293. let p1 = 0;
  294. for (let i = 0; i < this.points_.length; i++) {
  295. if (this.points_[i][0] >= t) {
  296. break;
  297. }
  298. p1 = i;
  299. }
  300. const p2 = Math.min(this.points_.length - 1, p1 + 1);
  301. if (p1 == p2) {
  302. return this.points_[p1][1];
  303. }
  304. return this._lerp(
  305. (t - this.points_[p1][0]) / (
  306. this.points_[p2][0] - this.points_[p1][0]),
  307. this.points_[p1][1], this.points_[p2][1]);
  308. }
  309. }
  310. class FirstPersonCameraDemo {
  311. constructor() {
  312. this.initialize_();
  313. }
  314. initialize_() {
  315. this.initializeRenderer_();
  316. this.initializeScene_();
  317. this.initializePostFX_();
  318. this.initializeAudio_();
  319. this.previousRAF_ = null;
  320. this.raf_();
  321. this.onWindowResize_();
  322. }
  323. initializeAudio_() {
  324. this.listener_ = new THREE.AudioListener();
  325. this.camera_.add(this.listener_);
  326. const sound1 = new THREE.PositionalAudio(this.listener_);
  327. const sound2 = new THREE.PositionalAudio(this.listener_);
  328. this.speakerMesh1_.add(sound1);
  329. this.speakerMesh2_.add(sound2);
  330. const loader = new THREE.AudioLoader();
  331. loader.load('resources/music/Ectoplasm.mp3', (buffer) => {
  332. setTimeout(() => {
  333. sound1.setBuffer(buffer);
  334. sound1.setLoop(true);
  335. sound1.setVolume(1.0);
  336. sound1.setRefDistance(1);
  337. sound1.play();
  338. this.analyzer1_ = new THREE.AudioAnalyser(sound1, 32);
  339. this.analyzer1Data_ = [];
  340. }, 5000);
  341. });
  342. loader.load('resources/music/AcousticRock.mp3', (buffer) => {
  343. setTimeout(() => {
  344. sound2.setBuffer(buffer);
  345. sound2.setLoop(true);
  346. sound2.setVolume(1.0);
  347. sound2.setRefDistance(1);
  348. sound2.play();
  349. this.analyzer2_ = new THREE.AudioAnalyser(sound2, 128);
  350. this.analyzer2Texture_ = new THREE.DataTexture(
  351. this.analyzer2_.data, 64, 1, THREE.RedFormat);
  352. this.analyzer2Texture_.magFilter = THREE.LinearFilter;
  353. }, 5000);
  354. });
  355. this.indexTimer_ = 0;
  356. this.noise1_ = new noise.Noise({
  357. octaves: 3,
  358. persistence: 0.5,
  359. lacunarity: 1.6,
  360. exponentiation: 1.0,
  361. height: 1.0,
  362. scale: 0.1,
  363. seed: 1
  364. });
  365. }
  366. initializeScene_() {
  367. const distance = 50.0;
  368. const angle = Math.PI / 4.0;
  369. const penumbra = 0.5;
  370. const decay = 1.0;
  371. let light = null;
  372. light = new THREE.SpotLight(
  373. 0xFFFFFF, 100.0, distance, angle, penumbra, decay);
  374. light.castShadow = true;
  375. light.shadow.bias = -0.00001;
  376. light.shadow.mapSize.width = 4096;
  377. light.shadow.mapSize.height = 4096;
  378. light.shadow.camera.near = 1;
  379. light.shadow.camera.far = 100;
  380. light.position.set(-35, 25, 0);
  381. light.target.position.set(-40, 4, 0);
  382. this.scene_.add(light);
  383. this.scene_.add(light.target);
  384. light = new THREE.SpotLight(
  385. 0xFFFFFF, 100.0, distance, angle, penumbra, decay);
  386. light.castShadow = true;
  387. light.shadow.bias = -0.00001;
  388. light.shadow.mapSize.width = 4096;
  389. light.shadow.mapSize.height = 4096;
  390. light.shadow.camera.near = 1;
  391. light.shadow.camera.far = 100;
  392. light.position.set(35, 25, 0);
  393. light.target.position.set(40, 4, 0);
  394. this.scene_.add(light);
  395. this.scene_.add(light.target);
  396. const upColour = 0xFFFF80;
  397. const downColour = 0x808080;
  398. light = new THREE.HemisphereLight(upColour, downColour, 0.5);
  399. light.color.setHSL( 0.6, 1, 0.6 );
  400. light.groundColor.setHSL( 0.095, 1, 0.75 );
  401. light.position.set(0, 4, 0);
  402. this.scene_.add(light);
  403. const loader = new THREE.CubeTextureLoader();
  404. const texture = loader.load([
  405. './resources/skybox/posx.jpg',
  406. './resources/skybox/negx.jpg',
  407. './resources/skybox/posy.jpg',
  408. './resources/skybox/negy.jpg',
  409. './resources/skybox/posz.jpg',
  410. './resources/skybox/negz.jpg',
  411. ]);
  412. texture.encoding = THREE.sRGBEncoding;
  413. this.scene_.background = texture;
  414. const mapLoader = new THREE.TextureLoader();
  415. const maxAnisotropy = this.threejs_.capabilities.getMaxAnisotropy();
  416. const plane = new THREE.Mesh(
  417. new THREE.PlaneGeometry(100, 100, 10, 10),
  418. this.loadMaterial_('rustediron2_', 4));
  419. plane.castShadow = false;
  420. plane.receiveShadow = true;
  421. plane.rotation.x = -Math.PI / 2;
  422. this.scene_.add(plane);
  423. const concreteMaterial = this.loadMaterial_('concrete3-', 4);
  424. const wall1 = new THREE.Mesh(
  425. new THREE.BoxGeometry(100, 100, 4),
  426. concreteMaterial);
  427. wall1.position.set(0, -40, -50);
  428. wall1.castShadow = true;
  429. wall1.receiveShadow = true;
  430. this.scene_.add(wall1);
  431. const wall2 = new THREE.Mesh(
  432. new THREE.BoxGeometry(100, 100, 4),
  433. concreteMaterial);
  434. wall2.position.set(0, -40, 50);
  435. wall2.castShadow = true;
  436. wall2.receiveShadow = true;
  437. this.scene_.add(wall2);
  438. const wall3 = new THREE.Mesh(
  439. new THREE.BoxGeometry(4, 100, 100),
  440. concreteMaterial);
  441. wall3.position.set(50, -40, 0);
  442. wall3.castShadow = true;
  443. wall3.receiveShadow = true;
  444. this.scene_.add(wall3);
  445. const wall4 = new THREE.Mesh(
  446. new THREE.BoxGeometry(4, 100, 100),
  447. concreteMaterial);
  448. wall4.position.set(-50, -40, 0);
  449. wall4.castShadow = true;
  450. wall4.receiveShadow = true;
  451. this.scene_.add(wall4);
  452. const speaker1Material = this.loadMaterial_('worn_metal4_', 1);
  453. const speaker1 = new THREE.Mesh(
  454. new THREE.BoxGeometry(1, 8, 4),
  455. speaker1Material);
  456. speaker1.position.set(-40, 4, 0);
  457. speaker1.castShadow = true;
  458. speaker1.receiveShadow = true;
  459. this.scene_.add(speaker1);
  460. const speaker1Geo = new THREE.BoxGeometry(0.25, 0.25, 0.25);
  461. const speaker1BoxMaterial = this.loadMaterial_('broken_down_concrete2_', 1);
  462. this.speakerMeshes1_ = [];
  463. const speaker1Group = new THREE.Group();
  464. speaker1Group.position.x = 0.5 + 0.125;
  465. for (let x = -5; x <= 5; ++x) {
  466. const row = [];
  467. for (let y = 0; y < 16; ++y) {
  468. const speaker1_1 = new THREE.Mesh(
  469. speaker1Geo,
  470. speaker1BoxMaterial.clone());
  471. speaker1_1.position.set(0, y*0.35 - 3, x * 0.35);
  472. speaker1_1.castShadow = true;
  473. speaker1_1.receiveShadow = true;
  474. speaker1Group.add(speaker1_1);
  475. row.push(speaker1_1);
  476. }
  477. this.speakerMeshes1_.push(row);
  478. }
  479. speaker1.add(speaker1Group);
  480. this.speakerMesh1_ = speaker1;
  481. const speaker2 = new THREE.Mesh(
  482. new THREE.BoxGeometry(1, 8, 4),
  483. new THREE.MeshStandardMaterial({color: 0x404040, roughness: 0.1, metalness: 0 }));
  484. speaker2.position.set(40, 4, 0);
  485. speaker2.castShadow = true;
  486. speaker2.receiveShadow = true;
  487. this.scene_.add(speaker2);
  488. this.speakerMesh2_ = speaker2;
  489. const diffuseMap = mapLoader.load('resources/background-grey-dots.png');
  490. diffuseMap.anisotropy = maxAnisotropy;
  491. const visualizerMaterial = new THREE.MeshStandardMaterial({
  492. map: diffuseMap,
  493. normalMap: mapLoader.load('resources/freepbr/flaking-plaster_normal-ogl.png'),
  494. roughnessMap: mapLoader.load('resources/freepbr/flaking-plaster_roughness.png'),
  495. metalnessMap: mapLoader.load('resources/freepbr/flaking-plaster_metallic.png'),
  496. });
  497. visualizerMaterial.onBeforeCompile = (shader) => {
  498. shader.uniforms.iTime = { value: 0.0 };
  499. shader.uniforms.iResolution = {value: new THREE.Vector2(128, 256)};
  500. shader.uniforms.audioDataTexture = {value: null};
  501. shader.fragmentShader = shader.fragmentShader.replace('void main()', FS_DECLARATIONS + 'void main()');
  502. shader.fragmentShader = shader.fragmentShader.replace('totalEmissiveRadiance = emissive;', `
  503. totalEmissiveRadiance = emissive + AudioVisualizer().xyz;
  504. `);
  505. visualizerMaterial.userData.shader = shader;
  506. };
  507. visualizerMaterial.customProgramCacheKey = () => {
  508. return 'visualizerMaterial';
  509. };
  510. this.speaker2Material_ = visualizerMaterial;
  511. const speaker2Screen = new THREE.Mesh(
  512. new THREE.PlaneGeometry(4, 8),
  513. this.speaker2Material_);
  514. speaker2Screen.castShadow = false;
  515. speaker2Screen.receiveShadow = true;
  516. speaker2Screen.rotation.y = -Math.PI / 2;
  517. speaker2Screen.position.x -= 0.51;
  518. this.speakerMesh2_.add(speaker2Screen);
  519. // Create Box3 for each mesh in the scene so that we can
  520. // do some easy intersection tests.
  521. const meshes = [
  522. plane, wall1, wall2, wall3, wall4];
  523. this.objects_ = [];
  524. for (let i = 0; i < meshes.length; ++i) {
  525. const b = new THREE.Box3();
  526. b.setFromObject(meshes[i]);
  527. this.objects_.push(b);
  528. }
  529. this.fpsCamera_ = new FirstPersonCamera(this.camera_, this.objects_);
  530. // Crosshair
  531. const crosshair = mapLoader.load('resources/crosshair.png');
  532. crosshair.anisotropy = maxAnisotropy;
  533. this.sprite_ = new THREE.Sprite(
  534. new THREE.SpriteMaterial({map: crosshair, color: 0xffffff, fog: false, depthTest: false, depthWrite: false}));
  535. this.sprite_.scale.set(0.15, 0.15 * this.camera_.aspect, 1)
  536. this.sprite_.position.set(0, 0, -10);
  537. // this.uiScene_.add(this.sprite_);
  538. }
  539. loadMaterial_(name, tiling) {
  540. const mapLoader = new THREE.TextureLoader();
  541. const maxAnisotropy = this.threejs_.capabilities.getMaxAnisotropy();
  542. const metalMap = mapLoader.load('resources/freepbr/' + name + 'metallic.png');
  543. metalMap.anisotropy = maxAnisotropy;
  544. metalMap.wrapS = THREE.RepeatWrapping;
  545. metalMap.wrapT = THREE.RepeatWrapping;
  546. metalMap.repeat.set(tiling, tiling);
  547. const albedo = mapLoader.load('resources/freepbr/' + name + 'albedo.png');
  548. albedo.anisotropy = maxAnisotropy;
  549. albedo.wrapS = THREE.RepeatWrapping;
  550. albedo.wrapT = THREE.RepeatWrapping;
  551. albedo.repeat.set(tiling, tiling);
  552. const normalMap = mapLoader.load('resources/freepbr/' + name + 'normal.png');
  553. normalMap.anisotropy = maxAnisotropy;
  554. normalMap.wrapS = THREE.RepeatWrapping;
  555. normalMap.wrapT = THREE.RepeatWrapping;
  556. normalMap.repeat.set(tiling, tiling);
  557. const roughnessMap = mapLoader.load('resources/freepbr/' + name + 'roughness.png');
  558. roughnessMap.anisotropy = maxAnisotropy;
  559. roughnessMap.wrapS = THREE.RepeatWrapping;
  560. roughnessMap.wrapT = THREE.RepeatWrapping;
  561. roughnessMap.repeat.set(tiling, tiling);
  562. const material = new THREE.MeshStandardMaterial({
  563. metalnessMap: metalMap,
  564. map: albedo,
  565. normalMap: normalMap,
  566. roughnessMap: roughnessMap,
  567. });
  568. return material;
  569. }
  570. initializeRenderer_() {
  571. this.threejs_ = new THREE.WebGLRenderer({
  572. antialias: false,
  573. });
  574. this.threejs_.shadowMap.enabled = true;
  575. this.threejs_.shadowMap.type = THREE.PCFSoftShadowMap;
  576. this.threejs_.setPixelRatio(window.devicePixelRatio);
  577. this.threejs_.setSize(window.innerWidth, window.innerHeight);
  578. this.threejs_.physicallyCorrectLights = true;
  579. this.threejs_.autoClear = false;
  580. document.body.appendChild(this.threejs_.domElement);
  581. window.addEventListener('resize', () => {
  582. this.onWindowResize_();
  583. }, false);
  584. const fov = 60;
  585. const aspect = 1920 / 1080;
  586. const near = 1.0;
  587. const far = 1000.0;
  588. this.camera_ = new THREE.PerspectiveCamera(fov, aspect, near, far);
  589. this.camera_.position.set(-30, 2, 0);
  590. this.scene_ = new THREE.Scene();
  591. this.uiCamera_ = new THREE.OrthographicCamera(
  592. -1, 1, 1 * aspect, -1 * aspect, 1, 1000);
  593. this.uiScene_ = new THREE.Scene();
  594. }
  595. initializePostFX_() {
  596. const parameters = {
  597. minFilter: THREE.LinearFilter,
  598. magFilter: THREE.LinearFilter,
  599. format: THREE.RGBAFormat,
  600. type: THREE.FloatType,
  601. stencilBuffer: true,
  602. };
  603. const renderTarget = new THREE.WebGLRenderTarget(
  604. window.innerWidth, window.innerHeight, parameters);
  605. this.composer_ = new EffectComposer(this.threejs_, renderTarget);
  606. this.composer_.setPixelRatio(window.devicePixelRatio);
  607. this.composer_.setSize(window.innerWidth, window.innerHeight);
  608. this.fxaaPass_ = new ShaderPass(FXAAShader);
  609. const uiPass = new RenderPass(this.uiScene_, this.uiCamera_);
  610. uiPass.clear = false;
  611. this.composer_.addPass(new RenderPass(this.scene_, this.camera_));
  612. this.composer_.addPass(uiPass);
  613. this.composer_.addPass(new ShaderPass(GammaCorrectionShader));
  614. this.composer_.addPass(this.fxaaPass_);
  615. }
  616. onWindowResize_() {
  617. this.camera_.aspect = window.innerWidth / window.innerHeight;
  618. this.camera_.updateProjectionMatrix();
  619. this.uiCamera_.left = -this.camera_.aspect;
  620. this.uiCamera_.right = this.camera_.aspect;
  621. this.uiCamera_.updateProjectionMatrix();
  622. this.threejs_.setSize(window.innerWidth, window.innerHeight);
  623. this.composer_.setSize(window.innerWidth, window.innerHeight);
  624. const pixelRatio = this.threejs_.getPixelRatio();
  625. this.fxaaPass_.material.uniforms['resolution'].value.x = 1 / (
  626. window.innerWidth * pixelRatio);
  627. this.fxaaPass_.material.uniforms['resolution'].value.y = 1 / (
  628. window.innerHeight * pixelRatio);
  629. }
  630. raf_() {
  631. requestAnimationFrame((t) => {
  632. if (this.previousRAF_ === null) {
  633. this.previousRAF_ = t;
  634. }
  635. this.step_(t - this.previousRAF_);
  636. this.composer_.render();
  637. this.previousRAF_ = t;
  638. this.raf_();
  639. });
  640. }
  641. step_(timeElapsed) {
  642. const timeElapsedS = timeElapsed * 0.001;
  643. this.fpsCamera_.update(timeElapsedS);
  644. if (this.analyzer1_) {
  645. this.indexTimer_ += timeElapsedS * 0.1;
  646. this.analyzer1Data_.push([...this.analyzer1_.getFrequencyData()]);
  647. const rows = this.speakerMeshes1_.length;
  648. if (this.analyzer1Data_.length > rows) {
  649. this.analyzer1Data_.shift();
  650. }
  651. const colourSpline = new LinearSpline((t, a, b) => {
  652. const c = a.clone();
  653. return c.lerp(b, t);
  654. });
  655. colourSpline.AddPoint(0.0, new THREE.Color(0x4040FF));
  656. colourSpline.AddPoint(0.25, new THREE.Color(0xFF4040));
  657. colourSpline.AddPoint(1.0, new THREE.Color(0xFFFF80));
  658. const remap = [15, 13, 11, 9, 7, 5, 3, 1, 0, 2, 4, 6, 8, 10, 12, 14];
  659. for (let r = 0; r < this.analyzer1Data_.length; ++r) {
  660. const data = this.analyzer1Data_[r];
  661. const speakerRow = this.speakerMeshes1_[r];
  662. for (let i = 0; i < data.length; ++i) {
  663. const freqScale = math.smootherstep((data[remap[i]]/255) ** 0.5, 0, 1);
  664. const sc = 1 + 6 * freqScale + this.noise1_.Get(this.indexTimer_, r * 0.42142, i * 0.3455);
  665. speakerRow[i].scale.set(sc, 1, 1);
  666. speakerRow[i].material.color.copy(colourSpline.Get(freqScale));
  667. speakerRow[i].material.emissive.copy(colourSpline.Get(freqScale));
  668. speakerRow[i].material.emissive.multiplyScalar(freqScale ** 2);
  669. }
  670. }
  671. }
  672. if (this.analyzer2_ && this.speaker2Material_ && this.speaker2Material_.userData.shader) {
  673. this.analyzer2_.getFrequencyData();
  674. this.speaker2Material_.userData.shader.uniforms.audioDataTexture.value = this.analyzer2Texture_;
  675. this.speaker2Material_.userData.shader.uniforms.iTime.value += timeElapsedS;
  676. this.speaker2Material_.userData.shader.uniforms.audioDataTexture.value.needsUpdate = true;
  677. }
  678. }
  679. }
  680. let _APP = null;
  681. window.addEventListener('DOMContentLoaded', () => {
  682. const _Setup = () => {
  683. _APP = new FirstPersonCameraDemo();
  684. document.body.removeEventListener('click', _Setup);
  685. };
  686. document.body.addEventListener('click', _Setup);
  687. });