Fire.js 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041
  1. /**
  2. * @author Mike Piecuch / https://github.com/mikepiecuch
  3. *
  4. * Based on research paper "Real-Time Fluid Dynamics for Games" by Jos Stam
  5. * http://www.dgp.toronto.edu/people/stam/reality/Research/pdf/GDC03.pdf
  6. *
  7. */
  8. THREE.Fire = function ( geometry, options ) {
  9. THREE.Mesh.call( this, geometry );
  10. this.type = 'Fire';
  11. var scope = this;
  12. this.clock = new THREE.Clock();
  13. options = options || {};
  14. var textureWidth = options.textureWidth || 512;
  15. var textureHeight = options.textureHeight || 512;
  16. var oneOverWidth = 1.0 / textureWidth;
  17. var oneOverHeight = 1.0 / textureHeight;
  18. var debug = (options.debug === undefined)? false : options.debug;
  19. this.color1 = options.color1 || new THREE.Color( 0xffffff );
  20. this.color2 = options.color2 || new THREE.Color( 0xffa000 );
  21. this.color3 = options.color3 || new THREE.Color( 0x000000 );
  22. this.colorBias = (options.colorBias === undefined)? 0.8 : options.colorBias;
  23. this.diffuse = (options.diffuse === undefined)? 1.33 : options.diffuse;
  24. this.viscosity = (options.viscosity === undefined)? 0.25 : options.viscosity;
  25. this.expansion = (options.expansion === undefined)? -0.25 : options.expansion;
  26. this.swirl = (options.swirl === undefined)? 50.0 : options.swirl;
  27. this.burnRate = (options.burnRate === undefined)? 0.3 : options.burnRate;
  28. this.drag = (options.drag === undefined)? 0.35 : options.drag;
  29. this.airSpeed = (options.airSpeed === undefined)? 6.0 : options.airSpeed;
  30. this.windVector = options.windVector || new THREE.Vector2( 0.0, 0.75 );
  31. this.speed = (options.speed === undefined)? 500.0 : options.speed;
  32. this.massConservation = (options.massConservation === undefined)? false : options.massConservation;
  33. var size = textureWidth * textureHeight;
  34. this.sourceData = new Uint8Array( 4 * size );
  35. this.clearSources = function() {
  36. for ( var y = 0; y < textureHeight; y++ ) {
  37. for ( var x = 0; x < textureWidth; x++ ) {
  38. i = y * textureWidth + x;
  39. var stride = i * 4;
  40. this.sourceData[ stride ] = 0;
  41. this.sourceData[ stride + 1 ] = 0;
  42. this.sourceData[ stride + 2 ] = 0;
  43. this.sourceData[ stride + 3 ] = 0;
  44. }
  45. }
  46. this.sourceMaterial.uniforms.sourceMap.value = this.internalSource;
  47. this.sourceMaterial.needsUpdate = true;
  48. return this.sourceData;
  49. }
  50. this.addSource = function(u, v, radius, density=null, windX=null, windY=null) {
  51. var startX = Math.max(Math.floor((u - radius) * textureWidth), 0);
  52. var startY = Math.max(Math.floor((v - radius) * textureHeight), 0);
  53. var endX = Math.min(Math.floor((u + radius) * textureWidth), textureWidth);
  54. var endY = Math.min(Math.floor((v + radius) * textureHeight), textureHeight);
  55. for (var y = startY; y < endY; y++) {
  56. for (var x = startX; x < endX; x++) {
  57. var diffX = x * oneOverWidth - u;
  58. var diffY = y * oneOverHeight - v;
  59. if (diffX * diffX + diffY * diffY < radius * radius) {
  60. i = y * textureWidth + x;
  61. var stride = i * 4;
  62. if (density != null) {
  63. this.sourceData[ stride ] = Math.min(Math.max(density, 0.0), 1.0) * 255;
  64. }
  65. if (windX != null) {
  66. var wind = Math.min(Math.max(windX, -1.0), 1.0);
  67. wind = (wind < 0.0)? Math.floor(wind * 127) + 255 : Math.floor(wind * 127);
  68. this.sourceData[ stride + 1 ] = wind;
  69. }
  70. if (windY != null) {
  71. var wind = Math.min(Math.max(windY, -1.0), 1.0);
  72. wind = (wind < 0.0)? Math.floor(wind * 127) + 255 : Math.floor(wind * 127);
  73. this.sourceData[ stride + 2 ] = wind;
  74. }
  75. }
  76. }
  77. }
  78. this.internalSource.needsUpdate = true;
  79. return this.sourceData;
  80. }
  81. // When setting source map, red channel is density. Green and blue channels
  82. // encode x and y velocity respectively as signed chars:
  83. // (0 -> 127 = 0.0 -> 1.0, 128 -> 255 = -1.0 -> 0.0 )
  84. this.setSourceMap = function(texture) {
  85. this.sourceMaterial.uniforms.sourceMap.value = texture;
  86. }
  87. var parameters = {
  88. minFilter: THREE.NearestFilter,
  89. magFilter: THREE.NearestFilter,
  90. format: THREE.RGBAFormat,
  91. depthBuffer: false,
  92. stencilBuffer: false
  93. };
  94. this.field0 = new THREE.WebGLRenderTarget( textureWidth,
  95. textureHeight,
  96. parameters );
  97. this.field0.background = new THREE.Color( 0x000000 );
  98. this.field1 = new THREE.WebGLRenderTarget( textureWidth,
  99. textureHeight,
  100. parameters );
  101. this.field0.background = new THREE.Color( 0x000000 );
  102. this.fieldProj = new THREE.WebGLRenderTarget( textureWidth,
  103. textureHeight,
  104. parameters );
  105. this.field0.background = new THREE.Color( 0x000000 );
  106. if ( ! THREE.Math.isPowerOfTwo( textureWidth ) ||
  107. ! THREE.Math.isPowerOfTwo( textureHeight ) ) {
  108. this.field0.texture.generateMipmaps = false;
  109. this.field1.texture.generateMipmaps = false;
  110. this.fieldProj.texture.generateMipmaps = false;
  111. }
  112. this.fieldScene = new THREE.Scene();
  113. this.fieldScene.background = new THREE.Color( 0x000000 );
  114. this.orthoCamera = new THREE.OrthographicCamera( textureWidth / -2,
  115. textureWidth / 2,
  116. textureHeight / 2,
  117. textureHeight / -2,
  118. 1, 2 );
  119. this.orthoCamera.position.z = 1;
  120. this.fieldGeometry = new THREE.PlaneGeometry(textureWidth, textureHeight);
  121. this.internalSource = new THREE.DataTexture( this.sourceData,
  122. textureWidth,
  123. textureHeight,
  124. THREE.RGBAFormat );
  125. // Source Shader
  126. var shader = THREE.Fire.SourceShader;
  127. this.sourceMaterial = new THREE.ShaderMaterial( {
  128. uniforms: shader.uniforms,
  129. vertexShader: shader.vertexShader,
  130. fragmentShader: shader.fragmentShader,
  131. transparent: false
  132. } );
  133. this.clearSources();
  134. this.sourceMesh = new THREE.Mesh( this.fieldGeometry, this.sourceMaterial );
  135. this.fieldScene.add(this.sourceMesh);
  136. // Diffuse Shader
  137. var shader = THREE.Fire.DiffuseShader;
  138. this.diffuseMaterial = new THREE.ShaderMaterial( {
  139. uniforms: shader.uniforms,
  140. vertexShader: shader.vertexShader,
  141. fragmentShader: shader.fragmentShader,
  142. transparent: false
  143. } );
  144. this.diffuseMaterial.uniforms.oneOverWidth.value = oneOverWidth;
  145. this.diffuseMaterial.uniforms.oneOverHeight.value = oneOverHeight;
  146. this.diffuseMesh = new THREE.Mesh( this.fieldGeometry, this.diffuseMaterial );
  147. this.fieldScene.add(this.diffuseMesh);
  148. // Drift Shader
  149. shader = THREE.Fire.DriftShader;
  150. this.driftMaterial = new THREE.ShaderMaterial( {
  151. uniforms: shader.uniforms,
  152. vertexShader: shader.vertexShader,
  153. fragmentShader: shader.fragmentShader,
  154. transparent: false
  155. } );
  156. this.driftMaterial.uniforms.oneOverWidth.value = oneOverWidth;
  157. this.driftMaterial.uniforms.oneOverHeight.value = oneOverHeight;
  158. this.driftMesh = new THREE.Mesh( this.fieldGeometry, this.driftMaterial );
  159. this.fieldScene.add(this.driftMesh);
  160. // Projection Shader 1
  161. shader = THREE.Fire.ProjectionShader1;
  162. this.projMaterial1 = new THREE.ShaderMaterial( {
  163. uniforms: shader.uniforms,
  164. vertexShader: shader.vertexShader,
  165. fragmentShader: shader.fragmentShader,
  166. transparent: false
  167. } );
  168. this.projMaterial1.uniforms.oneOverWidth.value = oneOverWidth ;
  169. this.projMaterial1.uniforms.oneOverHeight.value = oneOverHeight;
  170. this.projMesh1 = new THREE.Mesh( this.fieldGeometry, this.projMaterial1 );
  171. this.fieldScene.add(this.projMesh1);
  172. // Projection Shader 2
  173. shader = THREE.Fire.ProjectionShader2;
  174. this.projMaterial2 = new THREE.ShaderMaterial( {
  175. uniforms: shader.uniforms,
  176. vertexShader: shader.vertexShader,
  177. fragmentShader: shader.fragmentShader,
  178. transparent: false
  179. } );
  180. this.projMaterial2.uniforms.oneOverWidth.value = oneOverWidth ;
  181. this.projMaterial2.uniforms.oneOverHeight.value = oneOverHeight;
  182. this.projMesh2 = new THREE.Mesh( this.fieldGeometry, this.projMaterial2 );
  183. this.fieldScene.add(this.projMesh2);
  184. // Projection Shader 3
  185. shader = THREE.Fire.ProjectionShader3;
  186. this.projMaterial3 = new THREE.ShaderMaterial( {
  187. uniforms: shader.uniforms,
  188. vertexShader: shader.vertexShader,
  189. fragmentShader: shader.fragmentShader,
  190. transparent: false
  191. } );
  192. this.projMaterial3.uniforms.oneOverWidth.value = oneOverWidth ;
  193. this.projMaterial3.uniforms.oneOverHeight.value = oneOverHeight;
  194. this.projMesh3 = new THREE.Mesh( this.fieldGeometry, this.projMaterial3 );
  195. this.fieldScene.add(this.projMesh3);
  196. // Color Shader
  197. if (debug) {
  198. shader = THREE.Fire.DebugShader;
  199. } else {
  200. shader = THREE.Fire.ColorShader;
  201. }
  202. this.material = new THREE.ShaderMaterial( {
  203. uniforms: shader.uniforms,
  204. vertexShader: shader.vertexShader,
  205. fragmentShader: shader.fragmentShader,
  206. transparent: true
  207. } );
  208. this.material.uniforms.densityMap.value = this.field1.texture;
  209. this.configShaders = function(dt) {
  210. this.diffuseMaterial.uniforms.diffuse.value = dt * 0.05 * this.diffuse;
  211. this.diffuseMaterial.uniforms.viscosity.value = dt * 0.05 * this.viscosity;
  212. this.diffuseMaterial.uniforms.expansion.value = Math.exp(this.expansion * -1.0 );
  213. this.diffuseMaterial.uniforms.swirl.value = Math.exp(this.swirl * -0.1 );
  214. this.diffuseMaterial.uniforms.drag.value = Math.exp(this.drag * -0.1);
  215. this.diffuseMaterial.uniforms.burnRate.value = this.burnRate * dt * 0.01;
  216. this.driftMaterial.uniforms.windVector.value = this.windVector;
  217. this.driftMaterial.uniforms.airSpeed.value = dt * this.airSpeed * 0.001 * textureHeight;
  218. this.material.uniforms.color1.value = this.color1;
  219. this.material.uniforms.color2.value = this.color2;
  220. this.material.uniforms.color3.value = this.color3;
  221. this.material.uniforms.colorBias.value = this.colorBias;
  222. }
  223. this.clearDiffuse = function() {
  224. this.diffuseMaterial.uniforms.expansion.value = 1.0;
  225. this.diffuseMaterial.uniforms.swirl.value = 1.0;
  226. this.diffuseMaterial.uniforms.drag.value = 1.0;
  227. this.diffuseMaterial.uniforms.burnRate.value = 0.0;
  228. }
  229. this.swapTextures = function() {
  230. var swap = this.field0;
  231. this.field0 = this.field1;
  232. this.field1 = swap;
  233. }
  234. this.saveRenderState = function(renderer) {
  235. this.savedRenderTarget = renderer.getRenderTarget();
  236. this.savedVrEnabled = renderer.vr.enabled;
  237. this.savedShadowAutoUpdate = renderer.shadowMap.autoUpdate;
  238. this.savedAntialias = renderer.antialias;
  239. this.savedToneMapping = renderer.toneMapping;
  240. }
  241. this.restoreRenderState = function(renderer) {
  242. renderer.vr.enabled = this.savedVrEnabled;
  243. renderer.shadowMap.autoUpdate = this.savedShadowAutoUpdate;
  244. renderer.setRenderTarget(this.savedRenderTarget);
  245. renderer.antialias = this.savedAntialias;
  246. renderer.toneMapping = this.savedToneMapping;
  247. }
  248. this.renderSource = function(renderer) {
  249. this.sourceMesh.visible = true;
  250. this.sourceMaterial.uniforms.densityMap.value = this.field0.texture;
  251. renderer.render( this.fieldScene, this.orthoCamera, this.field1);
  252. this.sourceMesh.visible = false;
  253. this.swapTextures();
  254. }
  255. this.renderDiffuse = function(renderer) {
  256. this.diffuseMesh.visible = true;
  257. this.diffuseMaterial.uniforms.densityMap.value = this.field0.texture;
  258. renderer.render( this.fieldScene, this.orthoCamera, this.field1);
  259. this.diffuseMesh.visible = false;
  260. this.swapTextures();
  261. }
  262. this.renderDrift = function(renderer) {
  263. this.driftMesh.visible = true;
  264. this.driftMaterial.uniforms.densityMap.value = this.field0.texture;
  265. renderer.render( this.fieldScene, this.orthoCamera, this.field1);
  266. this.driftMesh.visible = false;
  267. this.swapTextures();
  268. }
  269. this.renderProject = function(renderer) {
  270. // Projection pass 1
  271. this.projMesh1.visible = true;
  272. this.projMaterial1.uniforms.densityMap.value = this.field0.texture;
  273. renderer.render( this.fieldScene, this.orthoCamera, this.fieldProj);
  274. this.projMesh1.visible = false;
  275. this.projMaterial2.uniforms.densityMap.value = this.fieldProj.texture;
  276. // Projection pass 2
  277. this.projMesh2.visible = true;
  278. for (var i = 0; i < 20; i++) {
  279. renderer.render( this.fieldScene, this.orthoCamera, this.field1);
  280. var temp = this.field1;
  281. this.field1 = this.fieldProj;
  282. this.fieldProj = temp;
  283. this.projMaterial2.uniforms.densityMap.value = this.fieldProj.texture;
  284. }
  285. this.projMesh2.visible = false;
  286. this.projMaterial3.uniforms.densityMap.value = this.field0.texture;
  287. this.projMaterial3.uniforms.projMap.value = this.fieldProj.texture;
  288. // Projection pass 3
  289. this.projMesh3.visible = true;
  290. renderer.render( this.fieldScene, this.orthoCamera, this.field1);
  291. this.projMesh3.visible = false;
  292. this.swapTextures();
  293. }
  294. this.onBeforeRender = function ( renderer, scene, camera ) {
  295. var delta = this.clock.getDelta();
  296. if (delta > 0.1) {
  297. delta = 0.1;
  298. }
  299. var dt = delta * (this.speed * 0.1);
  300. this.configShaders(dt);
  301. this.saveRenderState(renderer);
  302. renderer.vr.enabled = false; // Avoid camera modification and recursion
  303. renderer.shadowMap.autoUpdate = false; // Avoid re-computing shadows
  304. renderer.antialias = false;
  305. renderer.toneMapping = THREE.NoToneMapping;
  306. this.sourceMesh.visible = false;
  307. this.diffuseMesh.visible = false;
  308. this.driftMesh.visible = false;
  309. this.projMesh1.visible = false;
  310. this.projMesh2.visible = false;
  311. this.projMesh3.visible = false;
  312. this.renderSource(renderer);
  313. this.clearDiffuse();
  314. for (var i = 0; i < 21; i++) {
  315. this.renderDiffuse(renderer);
  316. }
  317. this.configShaders(dt);
  318. this.renderDiffuse(renderer);
  319. this.renderDrift(renderer);
  320. if (this.massConservation) {
  321. this.renderProject(renderer);
  322. this.renderProject(renderer);
  323. }
  324. // Final result out for coloring
  325. this.material.map = this.field1.texture;
  326. this.material.transparent = true;
  327. this.material.minFilter = THREE.LinearFilter,
  328. this.material.magFilter = THREE.LinearFilter,
  329. this.restoreRenderState(renderer);
  330. }
  331. }
  332. THREE.Fire.prototype = Object.create( THREE.Mesh.prototype );
  333. THREE.Fire.prototype.constructor = THREE.Fire;
  334. THREE.Fire.SourceShader = {
  335. uniforms: {
  336. 'sourceMap': {
  337. type: 't',
  338. value: null
  339. },
  340. 'densityMap': {
  341. type: 't',
  342. value: null
  343. }
  344. },
  345. vertexShader: [
  346. 'varying vec2 vUv;',
  347. 'void main() {',
  348. ' vUv = uv;',
  349. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  350. ' gl_Position = projectionMatrix * mvPosition;',
  351. '}'
  352. ].join("\n"),
  353. fragmentShader: [
  354. 'uniform sampler2D sourceMap;',
  355. 'uniform sampler2D densityMap;',
  356. 'varying vec2 vUv;',
  357. 'void main() {',
  358. ' vec4 source = texture2D( sourceMap, vUv );',
  359. ' vec4 current = texture2D( densityMap, vUv );',
  360. ' vec2 v0 = (current.gb - step(0.5, current.gb)) * 2.0;',
  361. ' vec2 v1 = (source.gb - step(0.5, source.gb)) * 2.0;',
  362. ' vec2 newVel = v0 + v1;',
  363. ' newVel = clamp(newVel, -0.99, 0.99);',
  364. ' newVel = newVel * 0.5 + step(0.0, -newVel);',
  365. ' float newDensity = source.r + current.a;',
  366. ' float newTemp = source.r + current.r;',
  367. ' newDensity = clamp(newDensity, 0.0, 1.0);',
  368. ' newTemp = clamp(newTemp, 0.0, 1.0);',
  369. ' gl_FragColor = vec4(newTemp, newVel.xy, newDensity);',
  370. '}'
  371. ].join("\n")
  372. }
  373. THREE.Fire.DiffuseShader = {
  374. uniforms: {
  375. 'oneOverWidth': {
  376. type: 'f',
  377. value: null
  378. },
  379. 'oneOverHeight': {
  380. type: 'f',
  381. value: null
  382. },
  383. 'diffuse': {
  384. type: 'f',
  385. value: null
  386. },
  387. 'viscosity': {
  388. type: 'f',
  389. value: null
  390. },
  391. 'expansion': {
  392. type: 'f',
  393. value: null
  394. },
  395. 'swirl': {
  396. type: 'f',
  397. value: null
  398. },
  399. 'drag': {
  400. type: 'f',
  401. value: null
  402. },
  403. 'burnRate': {
  404. type: 'f',
  405. value: null
  406. },
  407. 'densityMap': {
  408. type: 't',
  409. value: null
  410. }
  411. },
  412. vertexShader: [
  413. 'varying vec2 vUv;',
  414. 'void main() {',
  415. ' vUv = uv;',
  416. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  417. ' gl_Position = projectionMatrix * mvPosition;',
  418. '}'
  419. ].join("\n"),
  420. fragmentShader: [
  421. 'uniform float oneOverWidth;',
  422. 'uniform float oneOverHeight;',
  423. 'uniform float diffuse;',
  424. 'uniform float viscosity;',
  425. 'uniform float expansion;',
  426. 'uniform float swirl;',
  427. 'uniform float burnRate;',
  428. 'uniform float drag;',
  429. 'uniform sampler2D densityMap;',
  430. 'varying vec2 vUv;',
  431. 'void main() {',
  432. ' vec4 dC = texture2D( densityMap, vUv );',
  433. ' vec4 dL = texture2D( densityMap, vec2(vUv.x - oneOverWidth, vUv.y) );',
  434. ' vec4 dR = texture2D( densityMap, vec2(vUv.x + oneOverWidth, vUv.y) );',
  435. ' vec4 dU = texture2D( densityMap, vec2(vUv.x, vUv.y - oneOverHeight) );',
  436. ' vec4 dD = texture2D( densityMap, vec2(vUv.x, vUv.y + oneOverHeight) );',
  437. ' vec4 dUL = texture2D( densityMap, vec2(vUv.x - oneOverWidth, vUv.y - oneOverHeight) );',
  438. ' vec4 dUR = texture2D( densityMap, vec2(vUv.x + oneOverWidth, vUv.y - oneOverHeight) );',
  439. ' vec4 dDL = texture2D( densityMap, vec2(vUv.x - oneOverWidth, vUv.y + oneOverHeight) );',
  440. ' vec4 dDR = texture2D( densityMap, vec2(vUv.x + oneOverWidth, vUv.y + oneOverHeight) );',
  441. ' dC.yz = (dC.yz - step(0.5, dC.yz)) * 2.0;',
  442. ' dL.yz = (dL.yz - step(0.5, dL.yz)) * 2.0;',
  443. ' dR.yz = (dR.yz - step(0.5, dR.yz)) * 2.0;',
  444. ' dU.yz = (dU.yz - step(0.5, dU.yz)) * 2.0;',
  445. ' dD.yz = (dD.yz - step(0.5, dD.yz)) * 2.0;',
  446. ' dUL.yz = (dUL.yz - step(0.5, dUL.yz)) * 2.0;',
  447. ' dUR.yz = (dUR.yz - step(0.5, dUR.yz)) * 2.0;',
  448. ' dDL.yz = (dDL.yz - step(0.5, dDL.yz)) * 2.0;',
  449. ' dDR.yz = (dDR.yz - step(0.5, dDR.yz)) * 2.0;',
  450. ' vec4 result = (dC + vec4(diffuse, viscosity, viscosity, diffuse) * ( dL + dR + dU + dD + dUL + dUR + dDL + dDR )) / (1.0 + 8.0 * vec4(diffuse, viscosity, viscosity, diffuse)) - vec4(0.0, 0.0, 0.0, 0.001);',
  451. ' float temperature = result.r;',
  452. ' temperature = clamp(temperature - burnRate, 0.0, 1.0);',
  453. ' vec2 velocity = result.yz;',
  454. ' vec2 expansionVec = vec2(dL.w - dR.w, dU.w - dD.w);',
  455. ' vec2 swirlVec = vec2((dL.z - dR.z) * 0.5, (dU.y - dD.y) * 0.5);',
  456. ' velocity = velocity + (1.0 - expansion) * expansionVec + (1.0 - swirl) * swirlVec;',
  457. ' velocity = velocity - (1.0 - drag) * velocity;',
  458. ' gl_FragColor = vec4(temperature, velocity * 0.5 + step(0.0, -velocity), result.w);',
  459. ' gl_FragColor = gl_FragColor * step(oneOverWidth, vUv.x);',
  460. ' gl_FragColor = gl_FragColor * step(oneOverHeight, vUv.y);',
  461. ' gl_FragColor = gl_FragColor * step(vUv.x, 1.0 - oneOverWidth);',
  462. ' gl_FragColor = gl_FragColor * step(vUv.y, 1.0 - oneOverHeight);',
  463. '}'
  464. ].join("\n")
  465. }
  466. THREE.Fire.DriftShader = {
  467. uniforms: {
  468. 'oneOverWidth': {
  469. type: 'f',
  470. value: null
  471. },
  472. 'oneOverHeight': {
  473. type: 'f',
  474. value: null
  475. },
  476. 'windVector': {
  477. type: 'v2',
  478. value: new THREE.Vector2(0.0, 0.0)
  479. },
  480. 'airSpeed': {
  481. type: 'f',
  482. value: null
  483. },
  484. 'densityMap': {
  485. type: 't',
  486. value: null
  487. }
  488. },
  489. vertexShader: [
  490. 'varying vec2 vUv;',
  491. 'void main() {',
  492. ' vUv = uv;',
  493. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  494. ' gl_Position = projectionMatrix * mvPosition;',
  495. '}'
  496. ].join("\n"),
  497. fragmentShader: [
  498. 'uniform float oneOverWidth;',
  499. 'uniform float oneOverHeight;',
  500. 'uniform vec2 windVector;',
  501. 'uniform float airSpeed;',
  502. 'uniform sampler2D densityMap;',
  503. 'varying vec2 vUv;',
  504. 'void main() {',
  505. ' vec2 velocity = texture2D( densityMap, vUv ).gb;',
  506. ' velocity = (velocity - step(0.5, velocity)) * 2.0;',
  507. ' velocity = velocity + windVector;',
  508. ' vec2 sourcePos = vUv - airSpeed * vec2(oneOverWidth, oneOverHeight) * velocity;',
  509. ' vec2 units = sourcePos / vec2(oneOverWidth, oneOverHeight);',
  510. ' vec2 intPos = floor(units);',
  511. ' vec2 frac = units - intPos;',
  512. ' intPos = intPos * vec2(oneOverWidth, oneOverHeight);',
  513. ' vec4 dX0Y0 = texture2D( densityMap, intPos + vec2(0.0, -oneOverHeight) );',
  514. ' vec4 dX1Y0 = texture2D( densityMap, intPos + vec2(oneOverWidth, 0.0) );',
  515. ' vec4 dX0Y1 = texture2D( densityMap, intPos + vec2(0.0, oneOverHeight) );',
  516. ' vec4 dX1Y1 = texture2D( densityMap, intPos + vec2(oneOverWidth, oneOverHeight) );',
  517. ' dX0Y0.gb = (dX0Y0.gb - step(0.5, dX0Y0.gb)) * 2.0;',
  518. ' dX1Y0.gb = (dX1Y0.gb - step(0.5, dX1Y0.gb)) * 2.0;',
  519. ' dX0Y1.gb = (dX0Y1.gb - step(0.5, dX0Y1.gb)) * 2.0;',
  520. ' dX1Y1.gb = (dX1Y1.gb - step(0.5, dX1Y1.gb)) * 2.0;',
  521. ' vec4 source = mix(mix(dX0Y0, dX1Y0, frac.x), mix(dX0Y1, dX1Y1, frac.x), frac.y);',
  522. ' source.gb = source.gb * 0.5 + step(0.0, -source.gb);',
  523. ' gl_FragColor = source;',
  524. '}'
  525. ].join("\n")
  526. }
  527. THREE.Fire.ProjectionShader1 = {
  528. uniforms: {
  529. 'oneOverWidth': {
  530. type: 'f',
  531. value: null
  532. },
  533. 'oneOverHeight': {
  534. type: 'f',
  535. value: null
  536. },
  537. 'densityMap': {
  538. type: 't',
  539. value: null
  540. }
  541. },
  542. vertexShader: [
  543. 'varying vec2 vUv;',
  544. 'void main() {',
  545. ' vUv = uv;',
  546. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  547. ' gl_Position = projectionMatrix * mvPosition;',
  548. '}'
  549. ].join("\n"),
  550. fragmentShader: [
  551. 'uniform float oneOverWidth;',
  552. 'uniform float oneOverHeight;',
  553. 'uniform sampler2D densityMap;',
  554. 'varying vec2 vUv;',
  555. 'void main() {',
  556. ' float dL = texture2D( densityMap, vec2(vUv.x - oneOverWidth, vUv.y) ).g;',
  557. ' float dR = texture2D( densityMap, vec2(vUv.x + oneOverWidth, vUv.y) ).g;',
  558. ' float dU = texture2D( densityMap, vec2(vUv.x, vUv.y - oneOverHeight) ).b;',
  559. ' float dD = texture2D( densityMap, vec2(vUv.x, vUv.y + oneOverHeight) ).b;',
  560. ' dL = (dL - step(0.5, dL)) * 2.0;',
  561. ' dR = (dR - step(0.5, dR)) * 2.0;',
  562. ' dU = (dU - step(0.5, dU)) * 2.0;',
  563. ' dD = (dD - step(0.5, dD)) * 2.0;',
  564. ' float h = (oneOverWidth + oneOverHeight) * 0.5;',
  565. ' float div = -0.5 * h * (dR - dL + dD - dU);',
  566. ' gl_FragColor = vec4( 0.0, 0.0, div * 0.5 + step(0.0, -div), 0.0);',
  567. '}'
  568. ].join("\n")
  569. }
  570. THREE.Fire.ProjectionShader2 = {
  571. uniforms: {
  572. 'oneOverWidth': {
  573. type: 'f',
  574. value: null
  575. },
  576. 'oneOverHeight': {
  577. type: 'f',
  578. value: null
  579. },
  580. 'densityMap': {
  581. type: 't',
  582. value: null
  583. }
  584. },
  585. vertexShader: [
  586. 'varying vec2 vUv;',
  587. 'void main() {',
  588. ' vUv = uv;',
  589. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  590. ' gl_Position = projectionMatrix * mvPosition;',
  591. '}'
  592. ].join("\n"),
  593. fragmentShader: [
  594. 'uniform float oneOverWidth;',
  595. 'uniform float oneOverHeight;',
  596. 'uniform sampler2D densityMap;',
  597. 'varying vec2 vUv;',
  598. 'void main() {',
  599. ' float div = texture2D( densityMap, vUv ).b;',
  600. ' float pL = texture2D( densityMap, vec2(vUv.x - oneOverWidth, vUv.y) ).g;',
  601. ' float pR = texture2D( densityMap, vec2(vUv.x + oneOverWidth, vUv.y) ).g;',
  602. ' float pU = texture2D( densityMap, vec2(vUv.x, vUv.y - oneOverHeight) ).g;',
  603. ' float pD = texture2D( densityMap, vec2(vUv.x, vUv.y + oneOverHeight) ).g;',
  604. ' float divNorm = (div - step(0.5, div)) * 2.0;',
  605. ' pL = (pL - step(0.5, pL)) * 2.0;',
  606. ' pR = (pR - step(0.5, pR)) * 2.0;',
  607. ' pU = (pU - step(0.5, pU)) * 2.0;',
  608. ' pD = (pD - step(0.5, pD)) * 2.0;',
  609. ' float p = (divNorm + pR + pL + pD + pU) * 0.25;',
  610. ' gl_FragColor = vec4( 0.0, p * 0.5 + step(0.0, -p), div, 0.0);',
  611. '}'
  612. ].join("\n")
  613. }
  614. THREE.Fire.ProjectionShader3 = {
  615. uniforms: {
  616. 'oneOverWidth': {
  617. type: 'f',
  618. value: null
  619. },
  620. 'oneOverHeight': {
  621. type: 'f',
  622. value: null
  623. },
  624. 'densityMap': {
  625. type: 't',
  626. value: null
  627. },
  628. 'projMap': {
  629. type: 't',
  630. value: null
  631. }
  632. },
  633. vertexShader: [
  634. 'varying vec2 vUv;',
  635. 'void main() {',
  636. ' vUv = uv;',
  637. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  638. ' gl_Position = projectionMatrix * mvPosition;',
  639. '}'
  640. ].join("\n"),
  641. fragmentShader: [
  642. 'uniform float oneOverWidth;',
  643. 'uniform float oneOverHeight;',
  644. 'uniform sampler2D densityMap;',
  645. 'uniform sampler2D projMap;',
  646. 'varying vec2 vUv;',
  647. 'void main() {',
  648. ' vec4 orig = texture2D(densityMap, vUv);',
  649. ' float pL = texture2D( projMap, vec2(vUv.x - oneOverWidth, vUv.y) ).g;',
  650. ' float pR = texture2D( projMap, vec2(vUv.x + oneOverWidth, vUv.y) ).g;',
  651. ' float pU = texture2D( projMap, vec2(vUv.x, vUv.y - oneOverHeight) ).g;',
  652. ' float pD = texture2D( projMap, vec2(vUv.x, vUv.y + oneOverHeight) ).g;',
  653. ' float uNorm = (orig.g - step(0.5, orig.g)) * 2.0;',
  654. ' float vNorm = (orig.b - step(0.5, orig.b)) * 2.0;',
  655. ' pL = (pL - step(0.5, pL)) * 2.0;',
  656. ' pR = (pR - step(0.5, pR)) * 2.0;',
  657. ' pU = (pU - step(0.5, pU)) * 2.0;',
  658. ' pD = (pD - step(0.5, pD)) * 2.0;',
  659. ' float h = (oneOverWidth + oneOverHeight) * 0.5;',
  660. ' float u = uNorm - (0.5 * (pR - pL) / h);',
  661. ' float v = vNorm - (0.5 * (pD - pU) / h);',
  662. ' gl_FragColor = vec4( orig.r, u * 0.5 + step(0.0, -u), v * 0.5 + step(0.0, -v), orig.a);',
  663. '}'
  664. ].join("\n")
  665. }
  666. THREE.Fire.ColorShader = {
  667. uniforms: {
  668. 'color1': {
  669. type: 'c',
  670. value: null
  671. },
  672. 'color2': {
  673. type: 'c',
  674. value: null
  675. },
  676. 'color3': {
  677. type: 'c',
  678. value: null
  679. },
  680. 'colorBias': {
  681. type: 'f',
  682. value: null
  683. },
  684. 'densityMap': {
  685. type: 't',
  686. value: null
  687. }
  688. },
  689. vertexShader: [
  690. 'varying vec2 vUv;',
  691. 'void main() {',
  692. ' vUv = uv;',
  693. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  694. ' gl_Position = projectionMatrix * mvPosition;',
  695. '}'
  696. ].join("\n"),
  697. fragmentShader: [
  698. 'uniform vec3 color1;',
  699. 'uniform vec3 color2;',
  700. 'uniform vec3 color3;',
  701. 'uniform float colorBias;',
  702. 'uniform sampler2D densityMap;',
  703. 'varying vec2 vUv;',
  704. 'void main() {',
  705. ' float density = texture2D( densityMap, vUv ).a;',
  706. ' float temperature = texture2D( densityMap, vUv ).r;',
  707. ' float bias = clamp(colorBias, 0.0001, 0.9999);',
  708. ' vec3 blend1 = mix(color3, color2, temperature / bias) * (1.0 - step(bias, temperature));',
  709. ' vec3 blend2 = mix(color2, color1, (temperature - bias) / (1.0 - bias) ) * step(bias, temperature);',
  710. ' gl_FragColor = vec4(blend1 + blend2, density);',
  711. '}'
  712. ].join("\n")
  713. }
  714. THREE.Fire.DebugShader = {
  715. uniforms: {
  716. 'color1': {
  717. type: 'c',
  718. value: null
  719. },
  720. 'color2': {
  721. type: 'c',
  722. value: null
  723. },
  724. 'color3': {
  725. type: 'c',
  726. value: null
  727. },
  728. 'colorBias': {
  729. type: 'f',
  730. value: null
  731. },
  732. 'densityMap': {
  733. type: 't',
  734. value: null
  735. }
  736. },
  737. vertexShader: [
  738. 'varying vec2 vUv;',
  739. 'void main() {',
  740. ' vUv = uv;',
  741. ' vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );',
  742. ' gl_Position = projectionMatrix * mvPosition;',
  743. '}'
  744. ].join("\n"),
  745. fragmentShader: [
  746. 'uniform sampler2D densityMap;',
  747. 'varying vec2 vUv;',
  748. 'void main() {',
  749. ' float density;',
  750. ' density = texture2D( densityMap, vUv ).a;',
  751. ' vec2 vel = texture2D( densityMap, vUv ).gb;',
  752. ' vel = (vel - step(0.5, vel)) * 2.0;',
  753. ' float r = density;',
  754. ' float g = max(abs(vel.x), density * 0.5);',
  755. ' float b = max(abs(vel.y), density * 0.5);',
  756. ' float a = max(density * 0.5, max(abs(vel.x), abs(vel.y)));',
  757. ' gl_FragColor = vec4(r, g, b, a);',
  758. '}'
  759. ].join("\n")
  760. }