Three.js - Stencil only on certain objects - three.js

I'm looking into making kind of a portal effect using Three.js.
The main idea is to be able to see through multiple windows another Scene.
Exactly like in this example :
https://www.ronja-tutorials.com/post/022-stencil-buffers/ (See gif in article, too big to upload here)
I found an exact example of what i'm trying to do using three.js.
Here is the link :
https://sites.google.com/site/cgwith3js/home/masking-with-stencil
The fiddle was not working but I changed it to make it work :
http://jsfiddle.net/yzhreu6p/23/
scene = new THREE.Scene();
sceneStencil = new THREE.Scene();
...
scene.add(box); // red one
...
function animate() {
requestAnimationFrame(animate);
renderer.clear();
// animate the box
box.position.x = Math.cos(clock.getElapsedTime()) * 10;
var gl = renderer.getContext();
// enable stencil test
gl.enable(gl.STENCIL_TEST);
//renderer.state.setStencilTest( true );
// config the stencil buffer to collect data for testing
gl.stencilFunc(gl.ALWAYS, 1, 0xff);
gl.stencilOp(gl.REPLACE, gl.REPLACE, gl.REPLACE);
// render shape for stencil test
renderer.render(sceneStencil, camera);
// set stencil buffer for testing
gl.stencilFunc(gl.EQUAL, 1, 0xff);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);
// render actual scene
renderer.render(scene, camera);
// disable stencil test
gl.disable(gl.STENCIL_TEST);
}
Then I reuse the code in my project where I have kind of a town with buildings, and the hidden scene has a nyan cat textured box.
The problem I'm having is that the planes are disappearing when a building is behind and more serious problem, when there are buildings back the nyan cat texture, we see the texture in the building.
To better explain here is an image
I'm looking for a solution where the images are not visible inside the building, I found people that are talking about stencilMask but it's new for me.
Do I need to create another scene to make it work independently ?
If someone can help me, thank you for reading.

Related

threejs raycaster cannot intersect in stereoscopic mode

I am trying to make use of Raycaster in a ThreeJS scene to create a sort of VR interaction.
Everything works fine in normal mode, but not when I enable stereo effect.
I am using the following snippet of code.
// "camera" is a ThreeJS camera, "objectContainer" contains objects (Object3D) that I want to interact with
var raycaster = new THREE.Raycaster(),
origin = new THREE.Vector2();
origin.x = 0; origin.y = 0;
raycaster.setFromCamera(origin, camera);
var intersects = raycaster.intersectObjects(objectContainer.children, true);
if (intersects.length > 0 && intersects[0].object.visible === true) {
// trigger some function myFunc()
}
So basically when I try the above snippet of code in normal mode, myFunc gets triggered whenever I am looking at any of the concerned 3d objects.
However as soon as I switch to stereo mode, it stops working; i.e., myFunc never gets triggered.
I tried updating the value of origin.x to -0.5. I did that because in VR mode, the screen gets split into two halves. However that didn't work either.
What should I do to make the raycaster intersect the 3D objects in VR mode (when stereo effect is turned on)?
Could you please provide a jsfiddle with the code?
Basically, if you are using stereo in your app, it means you are using 2 cameras, therefore you need to check your intersects on both cameras views, this could become an expensive process.
var cameras =
{ 'camera1': new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000),
'camera2': new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 10000)
};
for (var cam in cameras) {
raycaster.setFromCamera(origin, cameras[cam]);
//continue your logic
}
You could use a vector object that simulates the camera intersection to avoid checking twice, but this depends on what you are trying to achieve.
I encountered a similar problem, I eventually found the reason. Actually in StereoEffect THREE.js displays the meshes on the two eyes, but in reality is actually adds only one mesh to the scene, exactly in the middle of the line left-eye-mesh <-> right-eye-mesh, hidden to the viewer.
So when you use the raycaster, you need to use it on the real mesh on the middle, not the illusion displayed on each eye !
I detailled here how to do it
Three.js StereoEffect displays meshes across 2 eyes
Hopes it solves your problem !
You can use my StereoEffect.js file in your project for resolving problem. See example of using. See my Raycaster stereo pull request also.

Reusing EffectsComposer result

I have an effectsComposer creating a result that is heavy in white.
var composer = new THREE.EffectComposer(renderer);
var renderPass = new THREE.RenderPass(shaderHeavyScene, camera);
composer.render(delta)
In the same project I have a material and scene with with a second image loaded into it.
When I replace: composer.render(delta);
with: renderer.render( secondImageScene, camera );
I can see my secondary image loaded into the three.js canvas.
My plan was to multiply the white heavy image of effectsComposer over the secondImageScene. (Revealing secondImageScene through the white)
My question is this: How would I go about multiplying the end result of the effectsComposer overtop of secondImageScene?
Use another pass that has input of two textures that were rendered in the previous passes, that multiplies them.

ThreeJS object outlines and masking

I have a problem with masking in Three.js.
I want to have outline around object and I did it using this tutorial
http://www.codeproject.com/Articles/8499/Generating-Outlines-in-OpenGL
I wrote this code;
renderer.autoClear = false;
...
renderer.render(scene, camera);
...
var gl = this.world.renderer.domElement.getContext('webgl') || this.world.renderer.domElement.getContext('experimental-webgl');
gl.clearStencil(0);
gl.clear(gl.STENCIL_BUFFER_BIT);
gl.enable(gl.STENCIL_TEST);
gl.stencilFunc(gl.ALWAYS, 1, 1);
gl.stencilOp(gl.KEEP, gl.REPLACE, gl.REPLACE);
gl.colorMask(0, 0, 0, 0);
renderer.render(sceneMask, camera);
gl.colorMask(1, 1, 1, 1);
gl.stencilFunc(gl.NOTEQUAL, 1, 1);
gl.stencilOp(gl.KEEP, gl.REPLACE, gl.REPLACE);
renderer.render(sceneOutlines, camera);
gl.disable(gl.STENCIL_TEST);
and it works like a charm.
But i want to have outline more thicker. On windows, web browsers using angle and DirectX so i can render thicker lines.
(I know that i can use scaled object by vertex normals, but in this way i will create outline thicker in some places and thiner in other)
Then i got the idea, to blur outline.
I found this tutorial
(this is not a http link)://stemkoski.blogspot.com/2013/03/using-shaders-and-selective-glow.html
and i add MaskPass before rendering scene with objects that will be blured.
What happend then? Nothing.
I inverting mask and disabling buffer clear for mask and render passes but in overall i dont know what im doing.
This is the jsFiddle with some example that i made.
http://jsfiddle.net/9MtGR/15/
It looks like outline works but im using additive shader and green cube (that should work as outline) is added to red cube (that should receive outline).
Is it possible to use Three.js masking in the way that red cube will have green blured outline?
Or mayby is there other way to get the same effect using not Three.js methods?
P.S. This is a matter of life and death so it's not a joke.
When I was working on some animation that required me to include star-wars-like lasers - that what helped in the end: http://bkcore.com/blog/3d/webgl-three-js-animated-selective-glow.html
Especially this example: http://demo.bkcore.com/threejs/webgl_tron_iso.html

Make environment map scale when moving from the object

I use CubeCamera to build a simple reflection model. The setup can be seen on the picture below.
If the camera is close enough to the cube - the reflection looks fine. However, if i move away from the objects - the reflection just gets bigger. See the picture below.
This is not the way i want it. I'd like the reflection to proportionally get smaller. I tried to play with different settings, then I thought this could be achieved using a proper shader program (just squish the cube texture, kind of), so i've tried to mess with the existing PhongShader, but no luck there, i'm too newbie to this.
Also, i've noticed that if i change the width and height of the cubeCamera.renderTarget, i.e.
cubeCamera.renderTarget.width = cubeCamera.renderTarget.height = 150;
i can get the proper dimensions of the reflection, but its position on the surface is wrong. It's visible from the angle presented on the picture below, but not visible if i place the camera straight. Looks like the texture needs to be centered.
The actual code is pretty straightforward:
var cubeCamera = new THREE.CubeCamera(1, 520, 512);
cubeCamera.position.set(0, 1, 0);
cubeCamera.renderTarget.format = THREE.RGBAFormat;
scene.add(cubeCamera);
var reflectorObj = new THREE.Mesh(
new THREE.CubeGeometry(20, 20, 20),
new THREE.MeshPhongMaterial({
envMap: cubeCamera.renderTarget,
reflectivity: 0.3
})
);
reflectorObj.position.set(0, 0, 0);
scene.add(reflectorObj);
var reflectionObj = new THREE.Mesh(
new THREE.SphereGeometry(5),
new THREE.MeshBasicMaterial({
color: 0x00ff00
})
);
reflectionObj.position.set(0, -5, 20);
scene.add(reflectionObj);
function animate () {
reflectorObj.visible = false;
cubeCamera.updateCubeMap(renderer, scene);
reflectorObj.visible = true;
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
Appreciate any help!
Environment mapping in three.js is based on the assumption that the object being reflected is "infinitely" far away from the reflective surface.
The reflected ray used in the environment map look-up does not emanate from the surface of the reflective material, but from the CubeCamera's center. This approximation is OK, as long as the reflected object is sufficiently far away. In your case it is not.
You can read more about this topic in this tutorial.
three.js r.58

Super sample antialiasing with threejs

I want to render my scene at twice the resolution of my canvas and then downscale it before displaying it. How would I do that using threejs?
for me the best way to have a perfect AA with not too much work (see the code below)
ps :if you increase more than 2 its start to be too sharpen
renderer = new THREE.WebGLRenderer({antialiasing:true});
renderer.setPixelRatio( window.devicePixelRatio * 1.5 );
This is my solution. The source comments should explain what's going on. Setup (init):
var renderer;
var composer;
var renderModel;
var effectCopy;
renderer = new THREE.WebGLRenderer({canvas: canvas});
// Disable autoclear, we do this manually in our animation loop.
renderer.autoClear = false;
// Double resolution (twice the size of the canvas)
var sampleRatio = 2;
// This render pass will render the big result.
renderModel = new THREE.RenderPass(scene, camera);
// Shader to copy result from renderModel to the canvas
effectCopy = new THREE.ShaderPass(THREE.CopyShader);
effectCopy.renderToScreen = true;
// The composer will compose a result for the actual drawing canvas.
composer = new THREE.EffectComposer(renderer);
composer.setSize(canvasWidth * sampleRatio, canvasHeight * sampleRatio);
// Add passes to the composer.
composer.addPass(renderModel);
composer.addPass(effectCopy);
Change your animation loop to:
// Manually clear you canvas.
renderer.clear();
// Tell the composer to produce an image for us. It will provide our renderer with the result.
composer.render();
Note: EffectComposer, RenderPass, ShaderPass and CopyShader are not part of the default three.js file. You have to include them in addition to three.js. At the time of writing they can be found in the threejs project under the examples folder:
/examples/js/postprocessing/EffectComposer.js
/examples/js/postprocessing/RenderPass.js
/examples/js/postprocessing/ShaderPass.js
/examples/js/shaders/CopyShader.js
Here's how you might be able to work it out: In your three.js initialization code, when you create your renderer, make it double the dimensions of your primary canvas, and set it to render to a secondary, hidden canvas element that is twice as large as your primary canvas. Perform the necessary image manipulation on the secondary canvas, and then display the result on the primary canvas.

Resources