├── .gitignore ├── .npmignore ├── ConvolutedCubeMap.js ├── ConvolutionCubeShader.js ├── LICENSE.md ├── README.md ├── bower.json ├── glslRotationMatrix.js ├── index.js ├── lib ├── stats.min.js ├── three.js └── threex.rendererstats.js ├── package.json ├── test.js └── upgradeFragmentShaderToHaveFakeHdriOutput.js /.gitignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | bower_components 2 | node_modules 3 | *.log 4 | .DS_Store 5 | bundle.js 6 | -------------------------------------------------------------------------------- /ConvolutedCubeMap.js: -------------------------------------------------------------------------------- 1 | var upgradeFragmentShaderToHaveFakeHDRIOutput = require('./upgradeFragmentShaderToHaveFakeHdriOutput'); 2 | var cloneDeep = require('lodash.clonedeep'); 3 | 4 | function ConvolutedCubeMap(sourceCubMap, resolution, blurStrength, brightness, iterations, flipX, type, prerenderCallback, postrenderCallback) { 5 | resolution = resolution || 64; 6 | this.iterations = iterations || 2; 7 | this.blurStrength = blurStrength || .5; 8 | this.brightness = brightness || 1; 9 | type = type || THREE.FloatType; 10 | var format, useFakeHDRI; 11 | var useFakeHDRIThrough = false; 12 | if(type === ConvolutedCubeMap.FakeHDRI || type === ConvolutedCubeMap.FakeHDRIThrough) { 13 | useFakeHDRI = true; 14 | useFakeHDRIThrough = type === ConvolutedCubeMap.FakeHDRIThrough; 15 | 16 | type = THREE.UnsignedByteType; 17 | format = THREE.RGBAFormat; 18 | } 19 | this.useFakeHDRI = useFakeHDRI; 20 | flipX = (flipX === false) ? 1 : -1; 21 | this.sourceCubMap = sourceCubMap; 22 | this.prerenderCallback = prerenderCallback; 23 | this.postrenderCallback = postrenderCallback; 24 | 25 | this.scene = new THREE.Scene(); 26 | 27 | this.shader = require('./ConvolutionCubeShader'); 28 | var fragmentShader = upgradeFragmentShaderToHaveFakeHDRIOutput(this.shader.fragmentShader); 29 | var uniforms = this.uniforms = cloneDeep(this.shader.uniforms); //clone uniforms for this instance 30 | 31 | this.cubeMapToRerender = uniforms["tCube"]; 32 | uniforms["tFlip"].value = flipX; 33 | uniforms["brightness"].value = .16666 * this.brightness; 34 | uniforms["blurStrength"].value = this.blurStrength; 35 | this.cubeMapToRerender.value = sourceCubMap; 36 | 37 | var material = this.material = new THREE.ShaderMaterial( { 38 | fragmentShader: fragmentShader, 39 | vertexShader: this.shader.vertexShader, 40 | uniforms: uniforms, 41 | depthWrite: false, 42 | side: THREE.BackSide, 43 | defines: useFakeHDRI ? { 44 | USE_FAKE_HDRI: true 45 | } : {}, 46 | // transparent: true 47 | } ); 48 | var material2; 49 | if(useFakeHDRIThrough) { 50 | material2 = this.material2 = material; 51 | } else { 52 | material2 = this.material2 = new THREE.ShaderMaterial( { 53 | fragmentShader: this.shader.fragmentShader, 54 | vertexShader: this.shader.vertexShader, 55 | uniforms: uniforms, 56 | depthWrite: false, 57 | side: THREE.BackSide, 58 | defines: useFakeHDRI ? { 59 | USE_FAKE_HDRI: true 60 | } : {}, 61 | // transparent: true 62 | }); 63 | } 64 | 65 | this.mesh = new THREE.Mesh( new THREE.BoxGeometry( 1, 1, 1 ), material ); 66 | this.scene.add( this.mesh ); 67 | 68 | this.cameraA = new THREE.CubeCamera(.01, 2, resolution, type, format); 69 | this.cameraB = new THREE.CubeCamera(.01, 2, resolution, type, format); 70 | this.scene.add(this.cameraA); 71 | this.scene.add(this.cameraB); 72 | this.cubeMap = this.cameraB.renderTarget; 73 | } 74 | 75 | ConvolutedCubeMap.prototype = { 76 | update: function(renderer) { 77 | if(this.prerenderCallback) { 78 | this.prerenderCallback(); 79 | } 80 | this.uniforms["blurStrength"].value = this.blurStrength; 81 | this.cubeMapToRerender.value = this.sourceCubMap; 82 | this.mesh.material = this.material; 83 | for (var i = this.iterations; i >= 0; i--) { 84 | this.cameraA.updateCubeMap(renderer, this.scene); 85 | this.cubeMapToRerender.value = this.cameraA.renderTarget; 86 | this.uniforms["blurStrength"].value *= .6666; 87 | if(this.useFakeHDRI && i == 0) this.mesh.material = this.material2; 88 | this.cameraB.updateCubeMap(renderer, this.scene); 89 | this.cubeMapToRerender.value = this.cameraB.renderTarget; 90 | this.uniforms["blurStrength"].value *= .6666; 91 | }; 92 | if(this.postrenderCallback) { 93 | this.postrenderCallback(); 94 | } 95 | }, 96 | setBrightness: function(val) { 97 | this.brightness = val; 98 | this.uniforms.brightness.value = .16666 * this.brightness; 99 | } 100 | } 101 | 102 | ConvolutedCubeMap.FakeHDRI = 10090; 103 | ConvolutedCubeMap.FakeHDRIThrough = 10091; 104 | 105 | module.exports = ConvolutedCubeMap; -------------------------------------------------------------------------------- /ConvolutionCubeShader.js: -------------------------------------------------------------------------------- 1 | var ConvolutionCubeShader = { 2 | 3 | uniforms: { "tCube": { type: "t", value: null }, 4 | "blurStrength": { type: "f", value: 0.1 }, 5 | "brightness": { type: "f", value: 0.16666 }, 6 | "tFlip": { type: "f", value: - 1 } }, 7 | 8 | vertexShader: [ 9 | require('./glslRotationMatrix'), 10 | "varying vec3 vWorldPosition;", 11 | "varying vec3 vWorldPosition2;", 12 | "varying vec3 vWorldPosition3;", 13 | "varying vec3 vWorldPosition4;", 14 | "varying vec3 vWorldPosition5;", 15 | "varying vec3 vWorldPosition6;", 16 | "uniform float blurStrength;", 17 | 18 | THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ], 19 | 20 | "void main() {", 21 | 22 | " vec4 worldPosition = modelMatrix * vec4( position, 1.0 );", 23 | " vWorldPosition = (worldPosition * rotationMatrix( vec3(1.0, 0.0, 0.0), blurStrength)).xyz;", 24 | " vWorldPosition2 = (worldPosition * rotationMatrix( vec3(1.0, 0.0, 0.0), -blurStrength)).xyz;", 25 | " vWorldPosition3 = (worldPosition * rotationMatrix( vec3(0.0, 1.0, 0.0), blurStrength)).xyz;", 26 | " vWorldPosition4 = (worldPosition * rotationMatrix( vec3(0.0, 1.0, 0.0), -blurStrength)).xyz;", 27 | " vWorldPosition5 = (worldPosition * rotationMatrix( vec3(0.0, 0.0, 1.0), blurStrength)).xyz;", 28 | " vWorldPosition6 = (worldPosition * rotationMatrix( vec3(0.0, 0.0, 1.0), -blurStrength)).xyz;", 29 | 30 | // " vWorldPosition.x += blurStrength;", 31 | // " vWorldPosition2.x -= blurStrength;", 32 | // " vWorldPosition3.y += blurStrength;", 33 | // " vWorldPosition4.y -= blurStrength;", 34 | // " vWorldPosition5.z += blurStrength;", 35 | // " vWorldPosition6.z -= blurStrength;", 36 | 37 | " gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );", 38 | 39 | THREE.ShaderChunk[ "logdepthbuf_vertex" ], 40 | 41 | "}" 42 | 43 | ].join("\n"), 44 | 45 | fragmentShader: [ 46 | 47 | "uniform samplerCube tCube;", 48 | "uniform float tFlip;", 49 | "uniform float brightness;", 50 | 51 | "varying vec3 vWorldPosition;", 52 | "varying vec3 vWorldPosition2;", 53 | "varying vec3 vWorldPosition3;", 54 | "varying vec3 vWorldPosition4;", 55 | "varying vec3 vWorldPosition5;", 56 | "varying vec3 vWorldPosition6;", 57 | 58 | THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ], 59 | 60 | "void main() {", 61 | 62 | 63 | " #ifdef USE_FAKE_HDRI", 64 | " vec4 sample1 = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", 65 | " vec3 tempColor = sample1.rgb * ((1.0 - sample1.a) * 255.0 + 1.0);", 66 | // " gl_FragColor.rgb = sample1.rgb;", 67 | " vec4 sample2 = textureCube( tCube, vec3( tFlip * vWorldPosition2.x, vWorldPosition2.yz ) );", 68 | " tempColor += sample2.rgb * ((1.0 - sample2.a) * 255.0 + 1.0);", 69 | " vec4 sample3 = textureCube( tCube, vec3( tFlip * vWorldPosition3.x, vWorldPosition3.yz ) );", 70 | " tempColor += sample3.rgb * ((1.0 - sample3.a) * 255.0 + 1.0);", 71 | " vec4 sample4 = textureCube( tCube, vec3( tFlip * vWorldPosition4.x, vWorldPosition4.yz ) );", 72 | " tempColor += sample4.rgb * ((1.0 - sample4.a) * 255.0 + 1.0);", 73 | " vec4 sample5 = textureCube( tCube, vec3( tFlip * vWorldPosition6.x, vWorldPosition5.yz ) );", 74 | " tempColor += sample5.rgb * ((1.0 - sample5.a) * 255.0 + 1.0);", 75 | " vec4 sample6 = textureCube( tCube, vec3( tFlip * vWorldPosition6.x, vWorldPosition6.yz ) );", 76 | " tempColor += sample6.rgb * ((1.0 - sample6.a) * 255.0 + 1.0);", 77 | " gl_FragColor.rgb = tempColor;", 78 | " #else", 79 | " gl_FragColor = textureCube( tCube, vec3( tFlip * vWorldPosition.x, vWorldPosition.yz ) );", 80 | " gl_FragColor += textureCube( tCube, vec3( tFlip * vWorldPosition2.x, vWorldPosition2.yz ) );", 81 | " gl_FragColor += textureCube( tCube, vec3( tFlip * vWorldPosition3.x, vWorldPosition3.yz ) );", 82 | " gl_FragColor += textureCube( tCube, vec3( tFlip * vWorldPosition4.x, vWorldPosition4.yz ) );", 83 | " gl_FragColor += textureCube( tCube, vec3( tFlip * vWorldPosition5.x, vWorldPosition5.yz ) );", 84 | " gl_FragColor += textureCube( tCube, vec3( tFlip * vWorldPosition6.x, vWorldPosition6.yz ) );", 85 | " #endif", 86 | " gl_FragColor *= brightness;", 87 | 88 | THREE.ShaderChunk[ "logdepthbuf_fragment" ], 89 | 90 | // " gl_FragColor.a = 0.0;", 91 | 92 | 93 | "}" 94 | 95 | ].join("\n") 96 | 97 | } 98 | 99 | module.exports = ConvolutionCubeShader; -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2014 Tomasz Dysinski 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy 5 | of this software and associated documentation files (the "Software"), to deal 6 | in the Software without restriction, including without limitation the rights 7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | copies of the Software, and to permit persons to whom the Software is 9 | furnished to do so, subject to the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be included in all 12 | copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 17 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 18 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 19 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE 20 | OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # threejs-light-probe 2 | 3 | [![experimental](http://badges.github.io/stability-badges/dist/experimental.svg)](http://github.com/badges/stability-badges) 4 | 5 | A convenient probe to generate cubemaps for reflections during runtime. 6 | 7 | ## Usage 8 | 9 | [![NPM](https://nodei.co/npm/threejs-light-probe.png)](https://nodei.co/npm/threejs-light-probe/) 10 | 11 | ## License 12 | 13 | MIT, see [LICENSE.md](http://github.com/bunnybones1/threejs-light-probe/blob/master/LICENSE.md) for details. 14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threejs-light-probe", 3 | "version": "0.0.1", 4 | "devDependencies": { 5 | "three.js": "*" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /glslRotationMatrix.js: -------------------------------------------------------------------------------- 1 | var glslRotationMatrix = [ 2 | 'mat4 rotationMatrix(vec3 axis, float angle)', 3 | 4 | '{', 5 | 6 | ' axis = normalize(axis);', 7 | 8 | ' float s = sin(angle);', 9 | 10 | ' float c = cos(angle);', 11 | 12 | ' float oc = 1.0 - c;', 13 | 14 | ' ', 15 | 16 | ' return mat4(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 0.0,', 17 | 18 | ' oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 0.0,', 19 | 20 | ' oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c, 0.0,', 21 | 22 | ' 0.0, 0.0, 0.0, 1.0);', 23 | 24 | '}' 25 | ].join("\n"); 26 | 27 | module.exports = glslRotationMatrix; -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var ConvolutedCubeMap = require('./ConvolutedCubeMap'); 2 | var upgradeFragmentShaderToHaveFakeHDRIOutput = require('./upgradeFragmentShaderToHaveFakeHDRIOutput'); 3 | //awesome shim to empower native materials with fake 8bit hdri hack for mobile 4 | var materialsToUpgrage = [ 5 | 'basic', 6 | 'phong' 7 | ]; 8 | materialsToUpgrage.forEach(function(mat) { 9 | var shader = THREE.ShaderLib[mat]; 10 | shader.fragmentShader = upgradeFragmentShaderToHaveFakeHDRIOutput(shader.fragmentShader); 11 | }); 12 | 13 | console.warn('warning: THREEjs has been shimmed with extra shader routines to provide HDRI emulation via the alpha channel. This may break certain expected behaviours.'); 14 | 15 | //end shim 16 | 17 | function LightProbe(near, far, resolution, type) { 18 | near = near || .01; 19 | far = far || 1000; 20 | resolution = resolution || 128; 21 | this.bufferType = type; 22 | var format; 23 | if(type === ConvolutedCubeMap.FakeHDRI || type === ConvolutedCubeMap.FakeHDRIThrough) { 24 | type = THREE.UnsignedByteType; 25 | format = THREE.RGBAFormat; 26 | } 27 | THREE.CubeCamera.call(this, near, far, resolution, type, format); 28 | 29 | if(this.renderTarget.type !== this.bufferType) console.log("LightProbe: WARNING: This version of three.js does not support Float Type CubeCamera. This is OK, but if you use a patched version of threejs, cubemaps can be HDRI!"); 30 | 31 | this.convolutedCubeMaps = []; 32 | } 33 | 34 | LightProbe.prototype = Object.create(THREE.CubeCamera.prototype); 35 | 36 | LightProbe.prototype.update = function(renderer, scene, updateAllconvolutionCubeMaps) { 37 | this.updateCubeMap(renderer, scene); 38 | if(updateAllconvolutionCubeMaps !== false) { 39 | for (var i = this.convolutedCubeMaps.length - 1; i >= 0; i--) { 40 | this.convolutedCubeMaps[i].update(renderer); 41 | }; 42 | } 43 | } 44 | 45 | LightProbe.prototype.updateManual = function(renderer, renderTarget, updateAllconvolutionCubeMaps) { 46 | this.renderTarget = renderTarget; 47 | if(updateAllconvolutionCubeMaps !== false) { 48 | for (var i = this.convolutedCubeMaps.length - 1; i >= 0; i--) { 49 | this.convolutedCubeMaps[i].sourceCubMap = renderTarget; 50 | this.convolutedCubeMaps[i].update(renderer); 51 | }; 52 | } 53 | } 54 | 55 | LightProbe.prototype.getCubeMapGenerator = function(resolution, blurStrength, brightness, iterations, flipX, prerenderCallback, postrenderCallback) { 56 | var convolutedCubeMap = new ConvolutedCubeMap(this.renderTarget, resolution, blurStrength, brightness, iterations, flipX, this.bufferType, prerenderCallback, postrenderCallback); 57 | this.convolutedCubeMaps.push(convolutedCubeMap); 58 | return convolutedCubeMap; 59 | } 60 | 61 | LightProbe.FakeHDRI = ConvolutedCubeMap.FakeHDRI; 62 | LightProbe.FakeHDRIThrough = ConvolutedCubeMap.FakeHDRIThrough; 63 | 64 | module.exports = LightProbe; -------------------------------------------------------------------------------- /lib/stats.min.js: -------------------------------------------------------------------------------- 1 | // stats.js - http://github.com/mrdoob/stats.js 2 | var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; 3 | i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:11,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}}; 7 | -------------------------------------------------------------------------------- /lib/threex.rendererstats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | * @author jetienne / http://jetienne.com/ 4 | */ 5 | /** @namespace */ 6 | var THREEx = THREEx || {} 7 | 8 | /** 9 | * provide info on THREE.WebGLRenderer 10 | * 11 | * @param {Object} renderer the renderer to update 12 | * @param {Object} Camera the camera to update 13 | */ 14 | THREEx.RendererStats = function (){ 15 | 16 | var msMin = 100; 17 | var msMax = 0; 18 | 19 | var container = document.createElement( 'div' ); 20 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 21 | 22 | var msDiv = document.createElement( 'div' ); 23 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#200;'; 24 | container.appendChild( msDiv ); 25 | 26 | var msText = document.createElement( 'div' ); 27 | msText.style.cssText = 'color:#f00;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 28 | msText.innerHTML= 'WebGLRenderer'; 29 | msDiv.appendChild( msText ); 30 | 31 | var msTexts = []; 32 | var nLines = 9; 33 | for(var i = 0; i < nLines; i++){ 34 | msTexts[i] = document.createElement( 'div' ); 35 | msTexts[i].style.cssText = 'color:#f00;background-color:#311;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 36 | msDiv.appendChild( msTexts[i] ); 37 | msTexts[i].innerHTML= '-'; 38 | } 39 | 40 | 41 | var lastTime = Date.now(); 42 | return { 43 | domElement: container, 44 | 45 | update: function(webGLRenderer){ 46 | // sanity check 47 | console.assert(webGLRenderer instanceof THREE.WebGLRenderer) 48 | 49 | // refresh only 30time per second 50 | if( Date.now() - lastTime < 1000/30 ) return; 51 | lastTime = Date.now() 52 | 53 | var i = 0; 54 | msTexts[i++].textContent = "== Memory ====="; 55 | msTexts[i++].textContent = "Programs: " + webGLRenderer.info.memory.programs; 56 | msTexts[i++].textContent = "Geometries: "+webGLRenderer.info.memory.geometries; 57 | msTexts[i++].textContent = "Textures: " + webGLRenderer.info.memory.textures; 58 | 59 | msTexts[i++].textContent = "== Render ====="; 60 | msTexts[i++].textContent = "Calls: " + webGLRenderer.info.render.calls; 61 | msTexts[i++].textContent = "Vertices: " + webGLRenderer.info.render.vertices; 62 | msTexts[i++].textContent = "Faces: " + webGLRenderer.info.render.faces; 63 | msTexts[i++].textContent = "Points: " + webGLRenderer.info.render.points; 64 | } 65 | } 66 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "threejs-light-probe", 3 | "version": "4.2.0", 4 | "description": "A convenient camera probe to generate cubemaps for reflections during runtime.", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "author": { 8 | "name": "Tomasz Dysinski", 9 | "email": "oz@bunnybones.com", 10 | "url": "https://github.com/bunnybones1" 11 | }, 12 | "dependencies": { 13 | "lodash.clonedeep": "^3.0.0" 14 | }, 15 | "devDependencies": { 16 | "loadandrunscripts": "^1.0.0", 17 | "threejs-checkerroom": "^1.0.0", 18 | "threejs-managed-view": "^2.4.0", 19 | "threejs-orbitingballs": "^1.0.1", 20 | "urlparams": "^1.0.0" 21 | }, 22 | "scripts": { 23 | "test": "beefy test.js --live --open" 24 | }, 25 | "keywords": [ 26 | "threejs", 27 | "reflection", 28 | "cubemap", 29 | "camera" 30 | ], 31 | "repository": { 32 | "type": "git", 33 | "url": "git://github.com/bunnybones1/threejs-light-probe.git" 34 | }, 35 | "homepage": "https://github.com/bunnybones1/threejs-light-probe", 36 | "bugs": { 37 | "url": "https://github.com/bunnybones1/threejs-light-probe/issues" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | var onReady = function() { 2 | var View = require('threejs-managed-view').View, 3 | LightProbe = require('./'), 4 | CheckerRoom = require('threejs-checkerroom'); 5 | 6 | var view = new View({ 7 | stats: true 8 | }); 9 | var getUrlParam = require('urlparams').getParam; 10 | 11 | //dolly 12 | var dolly = new THREE.Object3D(); 13 | view.scene.add(dolly); 14 | // view.camera.position.z = 10; 15 | dolly.add(view.camera); 16 | 17 | //lights 18 | var light = new THREE.PointLight(0xffffff, .5); 19 | light.position.x = 5; 20 | light.position.y = 30; 21 | view.scene.add(light); 22 | var hemisphereLight = new THREE.HemisphereLight(0x7f6f5f, 0x7f0000); 23 | view.scene.add(hemisphereLight); 24 | 25 | var useFakeHDRI = true; 26 | //something to reflect 27 | var lightMesh = new THREE.Mesh( 28 | new THREE.SphereGeometry(2, 16, 8), 29 | new THREE.MeshBasicMaterial({ 30 | color: new THREE.Color(140, 90, 20), 31 | emissive: new THREE.Color(1, 1, 1), 32 | useFakeHDRI: useFakeHDRI 33 | }) 34 | ) 35 | 36 | lightMesh.position.copy(light.position); 37 | view.scene.add(lightMesh); 38 | 39 | var checkerRoom = new CheckerRoom(20, 20, 4); 40 | view.scene.add(checkerRoom); 41 | 42 | //test mirror ball 43 | 44 | var distance = 2; 45 | var total = 6; 46 | var lightProbes = []; 47 | var colors = [ 48 | 0xef7f7f, 49 | 0xefef7f, 50 | 0x7fef7f, 51 | 0x7fefef, 52 | 0x7f7fef, 53 | 0xef7fef 54 | ] 55 | for (var i = total; i > 0; i--) { 56 | var ratio = i / total; 57 | var angle = ratio * Math.PI * 2; 58 | 59 | var pos = new THREE.Vector3(Math.cos(angle) * distance, 1, Math.sin(angle) * distance); 60 | 61 | var lightProbe = new LightProbe(undefined, undefined, undefined, useFakeHDRI ? LightProbe.FakeHDRI : THREE.FloatType); 62 | view.scene.add(lightProbe); 63 | // lightProbe.update(view.renderer, view.scene); 64 | 65 | var mirrorBall = new THREE.Mesh( 66 | new THREE.SphereGeometry(1, 32, 16), 67 | new THREE.MeshPhongMaterial({ 68 | // color: colors[i-1], 69 | color: 0x000000, 70 | // color: new THREE.Color(-.15, -.35, -4), 71 | // emissive: colors[i-1], 72 | // wireframe: true, 73 | // useFakeHDRI: true, 74 | combine: THREE.AddOperation, 75 | envMap : lightProbe.getCubeMapGenerator(64, 1 * ratio * ratio * ratio, 1, 3, false).cubeMap 76 | }) 77 | ) 78 | 79 | view.scene.add(mirrorBall); 80 | mirrorBall.position.copy(pos); 81 | lightProbe.position.copy(pos); 82 | lightProbe.update(view.renderer, view.scene); 83 | lightProbes.push(lightProbe); 84 | }; 85 | 86 | 87 | for (var i = total-1; i >= 0; i--) { 88 | lightProbes[i].update(view.renderer, view.scene); 89 | } 90 | 91 | 92 | view.renderManager.onEnterFrame.add(function() { 93 | dolly.rotation.y += .005; 94 | for (var i = lightProbes.length - 1; i >= 0; i--) { 95 | lightProbes[i].update(view.renderer, view.scene); 96 | }; 97 | lightMesh.position.y = 10 * Math.sin((new Date()).getTime() * .001) + 11; 98 | }) 99 | 100 | } 101 | 102 | var loadAndRunScripts = require('loadandrunscripts'); 103 | loadAndRunScripts( 104 | [ 105 | 'lib/three.js', 106 | 'lib/stats.min.js', 107 | 'lib/threex.rendererstats.js', 108 | ], 109 | onReady 110 | ); 111 | -------------------------------------------------------------------------------- /upgradeFragmentShaderToHaveFakeHdriOutput.js: -------------------------------------------------------------------------------- 1 | function upgradeFragmentShaderToHaveFakeHDRIOutput(fragmentShader) { 2 | var indexOfEndOfFragmentShader = fragmentShader.lastIndexOf('}'); 3 | var upgradeEnd = [ 4 | '#ifdef USE_FAKE_HDRI', 5 | ' float fhdri_brightness = ceil(min(256.0, max(gl_FragColor.r, max(gl_FragColor.g, max(gl_FragColor.b, 1.0)))));', 6 | ' if(fhdri_brightness > 1.0) {', 7 | ' gl_FragColor.rgb /= fhdri_brightness;', 8 | ' }', 9 | ' gl_FragColor.a = 1.0 - ((fhdri_brightness - 1.0) / 255.0);', 10 | '#endif', 11 | ].join('\n'); 12 | fragmentShader = fragmentShader.substring(0, indexOfEndOfFragmentShader) + upgradeEnd + '\n}' 13 | 14 | return fragmentShader; 15 | } 16 | module.exports = upgradeFragmentShaderToHaveFakeHDRIOutput; --------------------------------------------------------------------------------