Reputation: 179
So I found a neat code snippet to create volumetric lights inside threejs from Andrew Berg (can be seen here: https://codepen.io/abberg/pen/pbWkjg?editors=0010 ) who utilized post-processing and shaders to create a volumetric ball of light.
My question is how do I modify my code so that I can simultaneously have a static image being rendered in the background while using the composer renderer that will create the volumetric light, because currently I can do one or the other but not both.
If I remove the render();
call in my animate function and remove the comments from //renderer.render(backgroundScene,backgroundCamera);
//renderer.render(scene,camera);
I am able to see the static background image however the postprocessing effects that make the Volumetric Light won't be used, and vice-versa if I leave the render();
function and leave those lines commented out I can see the post processing effects and the light ball shader.
main.js
var scene, camera, renderer,
testcube, composer;
var backgroundMesh, backgroundScene, backgroundCamera,
backgroundComposer;
var occlusionComposer, occlusionRenderTarget,
occlusionBox, lightSphere, sphereUniforms;
var OCCLUSION_LAYER, DEFAULT_LAYER;
var incr;
function init(){
incr = 0;
OCCLUSION_LAYER = 1;
DEFAULT_LAYER = 0;
renderer = new THREE.WebGLRenderer({ antialias: false, alpha:true });
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.autoClear = false;
renderer.setClearColor(0x000000);
scene = new THREE.Scene();
var sphereGeo = new THREE.SphereGeometry(5,10,10);
var sphereMat = new THREE.MeshBasicMaterial({color:0xffffff});
lightSphere = new THREE.Mesh(sphereGeo,sphereMat);
lightSphere.layers.set(OCCLUSION_LAYER);
lightSphere.position.z = 0;
lightSphere.position.y = -150;
lightSphere.position.x = -150;
scene.add(lightSphere);
var boxGeo = new THREE.BoxGeometry(50,50,50);
var boxMat = new THREE.MeshBasicMaterial({color:0xffff13});
testcube = new THREE.Mesh(boxGeo,boxMat);
scene.add(testcube);
testcube.position.z = 0;
testcube.position.y = -100;
testcube.position.x = -90;
camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 0.1,7000);
camera.position.z = 200;
camera.position.x = -150;
camera.position.y = -150;
scene.add(camera);
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
var manager = new THREE.LoadingManager();
var loader = new THREE.TextureLoader( manager );
backgroundScene = new THREE.Scene();
backgroundCamera = new THREE.Camera();
backgroundScene.add( backgroundCamera );
loader.load( 'img/background.png', function( texture ) {
backgroundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2, 0),
new THREE.MeshBasicMaterial({
map: texture
})
);
backgroundMesh.material.depthTest = false;
backgroundMesh.material.depthWrite = false;
backgroundScene.add( backgroundMesh );
});
document.body.appendChild(renderer.domElement);
}
function preprocess(){
var pass;
occlusionRenderTarget = new THREE.WebGLRenderTarget( window.innerWidth * 0.5 , window.innerHeight * 0.5 );
occlusionComposer = new THREE.EffectComposer( renderer, occlusionRenderTarget);
occlusionComposer.addPass( new THREE.RenderPass( scene, camera ) );
pass = new THREE.ShaderPass( THREE.VolumetericLightShader );
pass.needsSwap = false;
occlusionComposer.addPass( pass );
sphereUniforms = pass.uniforms;
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );
pass = new THREE.ShaderPass( THREE.AdditiveBlendingShader );
pass.uniforms.tAdd.value = occlusionRenderTarget.texture;
composer.addPass( pass );
pass.renderToScreen = true;
}
function render(){
camera.layers.set(OCCLUSION_LAYER);
occlusionComposer.render();
//backgroundComposer.render();
camera.layers.set(DEFAULT_LAYER);
composer.render();
}
function animate(){
requestAnimationFrame(animate);
renderer.clear();
render();
//renderer.render(backgroundScene,backgroundCamera);
//renderer.render(scene,camera);
}
init();
preprocess();
animate();
shader.js
THREE.VolumetericLightShader = {
uniforms: {
tDiffuse: {value:null},
lightPosition: {value: new THREE.Vector2(0.5, 0.5)},
exposure: {value: 0.18},
decay: {value: 0.95},
density: {value: 0.8},
weight: {value: 0.62},
samples: {value: 100}
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"varying vec2 vUv;",
"uniform sampler2D tDiffuse;",
"uniform vec2 lightPosition;",
"uniform float exposure;",
"uniform float decay;",
"uniform float density;",
"uniform float weight;",
"uniform int samples;",
"const int MAX_SAMPLES = 100;",
"void main()",
"{",
"vec2 texCoord = vUv;",
"vec2 deltaTextCoord = texCoord - lightPosition;",
"deltaTextCoord *= 1.0 / float(samples) * density;",
"vec4 color = texture2D(tDiffuse, texCoord);",
"float illuminationDecay = 1.0;",
"for(int i=0; i < MAX_SAMPLES; i++)",
"{",
"if(i == samples){",
"break;",
"}",
"texCoord -= deltaTextCoord;",
"vec4 sample = texture2D(tDiffuse, texCoord);",
"sample *= illuminationDecay * weight;",
"color += sample;",
"illuminationDecay *= decay;",
"}",
"gl_FragColor = color * exposure;",
"}"
].join("\n")
};
THREE.AdditiveBlendingShader = {
uniforms: {
tDiffuse: { value:null },
tAdd: { value:null }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform sampler2D tAdd;",
"varying vec2 vUv;",
"void main() {",
"vec4 color = texture2D( tDiffuse, vUv );",
"vec4 add = texture2D( tAdd, vUv );",
"gl_FragColor = color + add;",
"}"
].join("\n")
};
THREE.PassThroughShader = {
uniforms: {
tDiffuse: { value: null }
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join( "\n" ),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"varying vec2 vUv;",
"void main() {",
"gl_FragColor = texture2D( tDiffuse, vec2( vUv.x, vUv.y ) );",
"}"
].join( "\n" )
};
Upvotes: 1
Views: 895
Reputation: 211166
First create a THREE.Scene
for the background and edit the background
property of the scene with the background texture:
backgroundScene = new THREE.Scene();
backgroundCamera = new THREE.Camera();
backgroundScene.add( backgroundCamera );
var manager = new THREE.LoadingManager();
loader = new THREE.TextureLoader( manager );
loader.setCrossOrigin("");
var backTexture = loader.load(img/background.png,
function ( texture ) {
var img = texture.image;
bgWidth= img.width;
bgHeight = img.height;
}
);
backgroundScene.background = backTexture;
Ensure that the transparent
property of the THREE.Material
(material
property) of the final THREE.ShaderPass
is set true
:
pass = new THREE.ShaderPass( THREE.AdditiveBlendingShader );
pass.uniforms.tAdd.value = occlusionRenderTarget.texture;
composer.addPass( pass );
pass.renderToScreen = true;
pass.material.transparent = true;
Finally you have to render the background scene before you render the THREE.EffectComposer
:
renderer.render(backgroundScene,backgroundCamera);
camera.layers.set(OCCLUSION_LAYER);
occlusionComposer.render();
camera.layers.set(DEFAULT_LAYER);
composer.render();
See the Example:
THREE.VolumetericLightShader = {
uniforms: {
tDiffuse: {value:null},
lightPosition: {value: new THREE.Vector2(0.5, 0.5)},
exposure: {value: 0.18},
decay: {value: 0.95},
density: {value: 0.8},
weight: {value: 0.62},
samples: {value: 100}
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"varying vec2 vUv;",
"uniform sampler2D tDiffuse;",
"uniform vec2 lightPosition;",
"uniform float exposure;",
"uniform float decay;",
"uniform float density;",
"uniform float weight;",
"uniform int samples;",
"const int MAX_SAMPLES = 100;",
"void main()",
"{",
"vec2 texCoord = vUv;",
"vec2 deltaTextCoord = texCoord - lightPosition;",
"deltaTextCoord *= 1.0 / float(samples) * density;",
"vec4 color = texture2D(tDiffuse, texCoord);",
"float illuminationDecay = 1.0;",
"for(int i=0; i < MAX_SAMPLES; i++)",
"{",
"if(i == samples){",
"break;",
"}",
"texCoord -= deltaTextCoord;",
"vec4 sample = texture2D(tDiffuse, texCoord);",
"sample *= illuminationDecay * weight;",
"color += sample;",
"illuminationDecay *= decay;",
"}",
"gl_FragColor = color * exposure;",
"}"
].join("\n")
};
THREE.AdditiveBlendingShader = {
uniforms: {
tDiffuse: { value:null },
tAdd: { value:null },
},
vertexShader: [
"varying vec2 vUv;",
"void main() {",
"vUv = uv;",
"gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
"}"
].join("\n"),
fragmentShader: [
"uniform sampler2D tDiffuse;",
"uniform sampler2D tAdd;",
"varying vec2 vUv;",
"void main() {",
"vec4 color = texture2D( tDiffuse, vUv );",
"vec4 add = texture2D( tAdd, vUv );",
"gl_FragColor = color + add;",
"}"
].join("\n")
};
var scene, camera, renderer, composer;
var backgroundScene, backgroundCamera;
var occlusionComposer, occlusionRenderTarget,
occlusionBox, lightSphere, sphereUniforms;
var OCCLUSION_LAYER, DEFAULT_LAYER;
function init(){
OCCLUSION_LAYER = 1;
DEFAULT_LAYER = 0;
renderer = new THREE.WebGLRenderer({ antialias: false, alpha:true });
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.autoClear = false;
renderer.setClearColor(0x000000, 0);
window.onresize = resize;
scene = new THREE.Scene();
var sphereGeo = new THREE.SphereGeometry(5,10,10);
var sphereMat = new THREE.MeshBasicMaterial({color:0xffffff});
lightSphere = new THREE.Mesh(sphereGeo,sphereMat);
lightSphere.layers.set(OCCLUSION_LAYER);
lightSphere.position.z = 0;
lightSphere.position.y = -150;
lightSphere.position.x = -150;
scene.add(lightSphere);
var boxGeo = new THREE.BoxGeometry(50,50,50);
var boxMat = new THREE.MeshBasicMaterial({color:0xffff13});
var testcube = new THREE.Mesh(boxGeo,boxMat);
scene.add(testcube);
testcube.position.z = 0;
testcube.position.y = -100;
testcube.position.x = -90;
camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 0.1,7000);
camera.position.z = 200;
camera.position.x = -150;
camera.position.y = -150;
scene.add(camera);
var light = new THREE.AmbientLight(0xffffff);
scene.add(light);
backgroundScene = new THREE.Scene();
backgroundCamera = new THREE.Camera();
backgroundScene.add( backgroundCamera );
var manager = new THREE.LoadingManager();
loader = new THREE.TextureLoader( manager );
loader.setCrossOrigin("");
var backTexture = loader.load("https://raw.githubusercontent.com/Rabbid76/graphics-snippets/master/resource/texture/background.jpg",
function ( texture ) {
var img = texture.image;
bgWidth= img.width;
bgHeight = img.height;
}
);
backgroundScene.background = backTexture;
document.body.appendChild(renderer.domElement);
}
function preprocess(){
occlusionRenderTarget = new THREE.WebGLRenderTarget( window.innerWidth * 0.5 , window.innerHeight * 0.5 );
occlusionComposer = new THREE.EffectComposer( renderer, occlusionRenderTarget);
occlusionComposer.addPass( new THREE.RenderPass( scene, camera ) );
var pass = new THREE.ShaderPass( THREE.VolumetericLightShader );
pass.needsSwap = false;
occlusionComposer.addPass( pass );
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );
pass = new THREE.ShaderPass( THREE.AdditiveBlendingShader );
pass.uniforms.tAdd.value = occlusionRenderTarget.texture;
composer.addPass( pass );
pass.renderToScreen = true;
pass.material.transparent = true;
}
function render(){
renderer.clear();
renderer.render(backgroundScene,backgroundCamera);
camera.layers.set(OCCLUSION_LAYER);
occlusionComposer.render();
camera.layers.set(DEFAULT_LAYER);
composer.render();
}
function resize() {
var aspect = window.innerWidth / window.innerHeight;
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = aspect;
camera.updateProjectionMatrix();
}
function animate(){
requestAnimationFrame(animate);
render();
}
init();
preprocess();
animate();
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://abberg.github.io/lib/shaders/CopyShader.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/EffectComposer.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/RenderPass.js"></script>
<script src="https://abberg.github.io/lib/postprocessing/ShaderPass.js"></script>
Upvotes: 1