ThreeJS object outlines and masking - three.js

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

Related

Three.js - Stencil only on certain objects

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.

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.

How to make Irrlicht's collision animator two-way street

Using the default built-in collision animator of Irrlicht I have found out that it works only on one side of the polygons of my geometry.
I have used the following code:
selector = smgr->createOctreeTriangleSelector(
q3node->getMesh(), q3node, 128);
q3node->setTriangleSelector(selector);
ICameraSceneNode* camera =
smgr->addCameraSceneNodeFPS(0, 100.0f, .3f, ID_IsNotPickable, 0, 0, true, 3.f);
ISceneNodeAnimator* anim = smgr->createCollisionResponseAnimator(
selector, camera, core::vector3df(30,50,30),
core::vector3df(0,-10,0), core::vector3df(0,30,0));
selector->drop();
camera->addAnimator(anim);
anim->drop();
Moreover I have noticed that the geometry is not textured on the other side.
Any suggestions?
Thanks in advance.
The concept of disabling the other side of the polygons is called Backface Culling
To disable it on your geometry do the following before performing any other actions:
q3node->setMaterialFlag(EMF_BACKFACE_CULLING, false);
Alternatively you may make your geometry with normals on both sides. This would even make irrlicht do less computations.
No need to disable the blackface culling then.

android OpenGLES 1.x CameraPreview to Surfacetexture

I am trying to send the camera preview to a surfacetexture object and render it on a square. I have running code for GLES20 but didnt find anything for 1.x.
Basically it should work like this, right?
// setup texture
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glGenTextures(1, textures, 0);
gl.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
gl.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, ...);
...
// setup surfacetexture object
surface = new SurfaceTexture(textures[0]);
surface.setOnFrameAvailableListener(this);
// setup camera
mCamera = Camera.open(0);
Camera.Parameters param = mCamera.getParameters();
List<Size> psize = param.getSupportedPreviewSizes();
//find previewsize to match glsurface from renderer
param.setPreviewSize(psize.get(i).width, psize.get(i).height);
mCamera.setParameters(param);
// set the texture and start preview
mCamera.setPreviewTexture(surface);
mCamera.startPreview();
// in the "onFrameAvailable" handler, i switch a flag to mark a new frame
updateSurface = true;
// and in the renderloop i update and redraw
if (updateSurface) {
surface.updateTexImage();
updateSurface = false;
}
gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textures[0]);
// Draw square
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBufferFloor);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer);
gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3);
The square gets drawn but is completely white. I dont receive glErrors or other exceptions. The "onFrameAvailable" handler gets called too.
If i use glTeximage with a loaded bitmap, it is correctly drawn on the square.
ANY ideas? Thank you!
I'm facing the same problem. Maybe I'm wrong, but it seems that SurfaceTexture is not compatible with GLES10. Surface texture uses GL_TEXTURE_EXTERNAL_OES, thereby it a custom fragment shader that is able to use this texture ("#extension GL_OES_EGL_image_external : require ").
As glUseProgram(...), etc are not avaible in GLES10, we cannot use custom shaders.
As I said, maybe I'm wrong... Good luck
EDIT : I finaly get it to work. You should use "gl.glEnable(GLES11Ext.GL_TEXTURE_EXTERNAL_OES);"

Three.js custom objLoader geometry lighting

I have this object I'm loading with THREE.objLoader and then create a mesh with it like so:
mesh = new THREE.SceneUtils.createMultiMaterialObject(
geometry,
[
new THREE.MeshBasicMaterial({color: 0xFEC1EA}),
new THREE.MeshBasicMaterial({
color: 0x999999,
wireframe: true,
transparent: true,
opacity: 0.85
})
]
);
In my scene I then add a DirectionalLight, it works and I can see my object, however it's like the DirectionalLight was an ambient one. No face is getting darker or lighter as it should be.
The object is filled with the color, but no lighting is applied to it.
If someone can help me with that it would be much appreciated :)
What could I be missing ?
Jsfiddle here: http://jsfiddle.net/5hcDs/
Ok folks, thanks to Maƫl Nison and mr doob I was able to understand the few things I was missing, being the total 3d noob that I am... I believe people starting to get into the 3d may find useful a little recap:
Basic 3d concepts
A 3d Face is made of some points (Vertex), and a vector called a normal, indicating the direction of the face (which side is the front and which one is the backside).
Not having normals can be really bad, because lighting is applied on the frontside only by default. Hence the black model when trying to apply a LambertMaterial or PhongMaterial.
An OBJ file is a way to describe 3D information. Want more info on this? Read this wikipedia article (en). Also, the french page provides a cube example which can be useful for testing.
Three.js tips and tricks
When normals are not present, the lighting can't be applied, hence the black model render. Three.js can actually compute vertex and face normals with geometry.computeVertexNormals() and/or geometry.computeFaceNormals() depending on what's missing
When you do so, there's a chance Three.js' normal calculation will be wrong and your normals will be flipped, to fix this you can simply loop through your geometry's faces array like so:
/* Compute normals */
geometry.computeFaceNormals();
geometry.computeVertexNormals();
/* Next 3 lines seems not to be mandatory */
mesh.geometry.dynamic = true
mesh.geometry.__dirtyVertices = true;
mesh.geometry.__dirtyNormals = true;
mesh.flipSided = true;
mesh.doubleSided = true;
/* Flip normals*/
for(var i = 0; i<mesh.geometry.faces.length; i++) {
mesh.geometry.faces[i].normal.x = -1*mesh.geometry.faces[i].normal.x;
mesh.geometry.faces[i].normal.y = -1*mesh.geometry.faces[i].normal.y;
mesh.geometry.faces[i].normal.z = -1*mesh.geometry.faces[i].normal.z;
}
You have to use a MeshPhongMaterial. MeshBasicMaterial does not take light in account when computing fragment color.
However, when using a MeshPhongMaterial, your mesh becomes black. I've never used the OBJ loader, but are you sure your model normales are right ?
Btw : you probably want to use a PointLight instead. And its position should probably be set to the camera position (light.position = camera.position should do the trick, as it will allow the light to be moved when the camera position will be edited by the Controls).

Resources