I guess it's basic stuff, but can't find any solution. Say, I have 2 objects and want to display only their overlapping parts (i.e. their intersection). Any hints?
One way is to turn on the stencil. Draw the first object, updating the stencil buffer, then draw the second object with the stencil test set to only draw where the first object set the stencil buffer.
var camera, scene1, scene2, scene3, renderer, container;
var mesh1, mesh2, mesh3, mesh4;
init();
animate();
function init() {
container = document.body;
renderer = new THREE.WebGLRenderer();
renderer.setSize( container.clientWidth, container.clientHeight );
container.appendChild( renderer.domElement );
renderer.autoClear = false;
//
camera = new THREE.PerspectiveCamera( 70, container.clientWidth / container.clientHeight, 1, 1000 );
camera.position.z = 400;
// we have to make 2 scenes AFAICT because three has no way to render portions
// of scene. I suppose we could hide one object, render, the hide the other object and un hide
// the first. Or we could remove objects from the scene and add them back.
scene1 = new THREE.Scene();
scene2 = new THREE.Scene();
var geometry1 = new THREE.BoxGeometry( 200, 200, 200 );
var geometry2 = new THREE.IcosahedronGeometry ( 200, 0 );
var material1 = new THREE.MeshBasicMaterial( { color: 0xFF8040 } );
var material2 = new THREE.MeshBasicMaterial( { color: 0x8080FF, wireframe: true } );
// put the box in scene1
mesh1 = new THREE.Mesh( geometry1, material1 );
mesh1.position.x = -75;
scene1.add( mesh1 );
// put the icoahedron in scene2
mesh2 = new THREE.Mesh( geometry2, material1 );
mesh2.position.x = 75;
scene2.add( mesh2 );
// just for visualization lets draw a 3rd scene with both objects so we can
// where they would be if drawn
scene3 = new THREE.Scene();
mesh3 = new THREE.Mesh( geometry1, material2 );
scene3.add( mesh3 );
// put the icoahedron in scene2
mesh4 = new THREE.Mesh( geometry2, material2 );
scene3.add( mesh4 );
}
function animate() {
mesh1.rotation.x += 0.005;
mesh1.rotation.y += 0.01;
mesh2.rotation.x -= 0.003;
mesh2.rotation.y -= 0.007;
// Clear color, depth, and stencil
renderer.clear(true, true, true);
// Turn on stenciling, Set it up so that as we draw scene1 it will put
// a 1 in the stencil for ever pixel drawn
var gl = renderer.context;
gl.enable(gl.STENCIL_TEST);
gl.stencilOp(gl.KEEP, gl.KEEP, gl.REPLACE);
gl.stencilFunc(gl.ALWAYS, 1, 0xFF);
// renderer scene1
renderer.render( scene1, camera );
// Clear the color and depth but NOT the stencil
renderer.clear(true, true, false);
// Set the stencil up so we'll only draw if there's a 1 in the stencil buffer.
gl.stencilOp(gl.KEEP, gl.KEEP, gl.KEEP);// sfail, spassdfail, spassdepthpass);
gl.stencilFunc(gl.EQUAL, 1, 0xFF); //func, ref, mask)
// renderer scene2
renderer.render( scene2, camera );
// this part is just so we can see both shapes since it's hard to see otherwise
gl.disable(gl.STENCIL_TEST);
mesh3.position.x = mesh1.position.x;
mesh3.rotation.x = mesh1.rotation.x;
mesh3.rotation.y = mesh1.rotation.y;
mesh4.position.x = mesh2.position.x;
mesh4.rotation.x = mesh2.rotation.x;
mesh4.rotation.y = mesh2.rotation.y;
renderer.render( scene3, camera );
requestAnimationFrame( animate );
}
html, body {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
overflow: hidden;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.min.js"></script>
Hmmm, this actually only works in 2D not 3D (the intersection is in 2d). DOH!
Take a look at: Constructive Solid Geometries. Intro here:
http://learningthreejs.com/blog/2011/12/10/constructive-solid-geometry-with-csg-js/
As an alternative to using the stencil #gman mentioned you could achieve this by cleverly switching between depth functions or blend functions.
Related
I´m trying to make use of layers in Three.js.
I have this script with a sphere, a triangle and a glTF object (car).
I made a second layer enable: camera.layers.enable(1);
Set the sphere, the triangle and the glTF object to the layer 1:
car.layers.set( 1 );
sphere.layers.set( 1 );
triangle.layers.set( 1 );
But when i set the camera to the layer 1 ( camera.layers.set(1); ), the glTF object does not display, but other elements do. So, it seens like i can´t set the glTF object to a different layer then default layer.
Here is the code. What could be wrong?
Thanks for the attention!
<script>
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(50, window.innerWidth/window.innerHeight, 0.1, 3000);
camera.position.set( 0, 0.1, 1 );
camera.layers.enable(1);
camera.layers.set(1);
var renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setClearColor("#f5e5e5");
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// LIGHT ------------------------------------------------------------------------------>
var dLight = new THREE.DirectionalLight( 0x0000ff, 6.5 );
dLight.position.set(1500, -700, 500);
dLight.castShadow = true;
dLight.layers.set(1);
scene.add( dLight);
// Load a glTF resource -------------------------------------------------------->
var loader = new THREE.GLTFLoader();
var car;
loader.load('car.gltf', function ( gltf ) {
car = gltf.scene.children[0];
car.scale.set(0.5, 0.5, 0.5);
car.position.z = 0;
car.position.y = -0.095;
car.layers.set(1);
scene.add( gltf.scene );
render();
});
// SPHERE --------------------------------------------------------------->
var material = new THREE.MeshLambertMaterial();
var geometry = new THREE.SphereGeometry(0.05, 20, 20);
var sphere = new THREE.Mesh(geometry, material);
sphere.position.x = 0.25;
scene.add(sphere);
sphere.layers.set( 1 );
// TRIANGLE ------------------------------------------------------------->
var geometre = new THREE.Geometry();
geometre.vertices.push(new THREE.Vector3(-0.25, -0.1, 0));
geometre.vertices.push(new THREE.Vector3(0, 0.30, 0));
geometre.vertices.push(new THREE.Vector3(0.25, -0.1, 0));
geometre.vertices.push(new THREE.Vector3(-0.25, -0.1, 0));
var triangle= new THREE.Line(geometre, new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 12 }));
triangle.layers.set( 1 );
scene.add(triangle);
// POST-PROCESSING ------------------------------------------------------->
var composer = new THREE.EffectComposer(renderer);
var renderPass = new THREE.RenderPass(scene, camera);
composer.addPass(renderPass);
var pass1 = new THREE.GlitchPass(0);
composer.addPass(pass1);
// RENDER -------------------------------------------------------------->
requestAnimationFrame(render);
function render(){
sphere.rotation.y += -0.02;
car.rotation.y += 0.01;
composer.render();
requestAnimationFrame(render);
};
</script>
You have to set layers recursively for the entire hierarchy of objects like so:
gltf.scene.traverse( function( object ) {
object.layers.set( 1 );
} );
By default, a layer configuration does not automatically apply to its descendant nodes in the scene graph.
three.js r116
I am working on 3D Modelling using Thrre.js , and have to display neat and clean product after reading a .DAE file but my product display is not well clean and good . Can anybody please help me , i have also given the light sources but still product shown are very dull .Actual product to be shown
My product image render screen shot , no leather is shown in product and also very dull
I am trying following Code :
<script>
var renderer, scene, camera, controls, light;
var geometry, material, mesh;
init();
animate();
function init() {
document.body.style.cssText = 'margin: 0; overflow: hidden' ;
renderer = new THREE.WebGLRenderer( { alpha: 1, antialias: true, clearColor: 0xffffff } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 50000 );
camera.position.set( 100, 100, 100 );
//camera.position.set(-15, 10, 15);
controls = new THREE.TrackballControls( camera, renderer.domElement );
scene.add(camera);
light = new THREE.AmbientLight(0xffffff, 10);
light.position.set(100,100, 100).normalize();
scene.add(light);
light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(10, 10, 10).normalize();
scene.add(light);
var pointlight = new THREE.PointLight(0xffffff, 1, 0);
pointlight.position.set(50, 100, 50)
camera.add(pointlight);
scene.add(pointlight);
var spotLight = new THREE.SpotLight(0xffffff);
spotLight.position.set(10,10,10);
scene.add(spotLight);
var pointHelper = new THREE.PointLightHelper(pointlight, 0.1);
scene.add(pointHelper);
this.renderer.render(this.scene, this.camera);
geometry = new THREE.BoxGeometry( 10, 10, 10 );
material = new THREE.MeshLambertMaterial( { ambient: 0x333333, color: 0x888888, opacity: 0.000001, transparent: true } );
// material = new THREE.MeshLambertMaterial({ color: 0xffffff, vertexColors: THREE.FaceColors });
mesh = new THREE.Mesh( geometry, material );
mesh.name = 'basic template mesh';
mesh.visible = false;
scene.add( mesh );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
controls.update();
}``
Through this code , i got the very dull product.
Can Anyone Please help me to sort out this problem ?
You are using the wrong material. The MeshLambertMaterial is used for diffuse surfaces, those that do not have specular highlights (paper, raw wood, cloth). You need to use the MeshPhongMaterial which allows the specular highlights (white spots) in the chair.
In your model, you have two types of surface: leather and wood. Even though both of them have specular highlights, their shininess are different. You will have to define separate materials for both surfaces. But, since I see you have a single object, this can be difficult. You can define a single MeshPhongMaterial for both surfaces, but you will have to try several values in the parameters to get the desired effect.
I am really new to computer graphics, and I started experimenting with some things with THREE.js. So I wanted to an animation of a flag (wave motions) and I couldn't find anything (maybe I don't know what to search). So I made my flag with a parametric geometry, and the function is just a cos. And I wan't to animate the flag by dynamically changing the function of the parametric geometry. How can I do that, and is this the correct way of doing this?
P.S. The change in the function that I want is simply moving the cos along the X asis so it looks like the flag is moving.
In dependency on amount of vertices in your mesh, you can use shaders or simple changing of vertices in your animation loop with cos function.
Below, there's the approach with simple changing.
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 0, 2, 10 );
var renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var controls = new THREE.OrbitControls( camera, renderer.domElement );
scene.add( new THREE.GridHelper( 10, 10 ) );
var planeGeom = new THREE.PlaneGeometry( 10, 3, 20, 3 );
var plane = new THREE.Mesh( planeGeom, new THREE.MeshBasicMaterial( { color: "red", side: THREE.DoubleSide } ) );
scene.add( plane );
render();
function render(){
requestAnimationFrame( render );
planeGeom.vertices.forEach( v => {
v.z = Math.cos( .5 * v.x - Date.now() * .001 ) * .5;
});
planeGeom.verticesNeedUpdate = true; // the most important thing, when you change vertices
renderer.render( scene, camera );
}
body{
overflow: hidden;
margin: 0;
}
<script src="https://threejs.org/build/three.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I'm very new to Three.JS and 3D web dev in general what I'm trying to do is mimic this action: https://www.youtube.com/watch?v=HWSTxPc8npk&feature=youtu.be&t=7s Essentially this is a set of 3D planes and upon click the whole stack reacts and gives space around the one that's clicked.
For now, my base case is 3 planes and figuring first out if I can click the the middle one, how do I get the others to jump back smoothly as if they were pushed rather than instant appear and disappear as they do now on the click of a button.
The long term goal is to have a separate button for every plane so that on click, the selected plane will have padding around it and the rest of the planes in stack move accordingly.
I've looked into Tween.js, and CSS3D but pretty overwhelmed as a newbie. Any tutorials or tips would be greatly appreciated!
// Our Javascript will go here.
window.addEventListener( 'resize', onWindowResize, false );
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 1000 );
var renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
var geometry = new THREE.PlaneGeometry( 3, 3, 1 );
var material = new THREE.MeshBasicMaterial( { color: 0x00ff00 } );
var plane = new THREE.Mesh( geometry, material );
plane.rotation.y = -.7;
var material2 = new THREE.MeshBasicMaterial( { color: 0x0000ff } );
var material3 = new THREE.MeshBasicMaterial( { color: 0xff0000 } );
var plane2 = new THREE.Mesh(geometry, material2 );
plane2.rotation.y = -.7;
plane2.position.x = 1;
var plane3 = new THREE.Mesh(geometry, material3);
plane3.rotation.y = -.7;
plane3.position.x = -1;
scene.add( plane, plane2, plane3 );
camera.position.z = 5;
function render() {
requestAnimationFrame( render );
// cube.rotation.x += 0.1;
// cube.rotation.y += 0.1;
renderer.render( scene, camera );
}
render();
function clickFirst() {
TWEEN.removeAll();
var tween = new TWEEN.Tween(plane3.position).to({x: -2}, 1000).start();
tween.easing(TWEEN.Easing.Elastic.InOut);
render();
}
</script>
<button onclick="clickFirst();" style="background-color: white; z-index: 9999;">Click me</button>
First, you need to locate the 2 planes.
Second, you need to make the planes clickable:
https://threejs.org/examples/#webgl_interactive_cubes
https://github.com/josdirksen/learning-threejs/blob/master/chapter-09/02-selecting-objects.html
Third, you should use Tween.js for the transition.
after picking the right plane, make a tween for the other planes with a tween, all to move on the same Axis:
example:
createjs.Tween.get(plane3.position.z).to(
plane3.position.z + 100
, 1000, createjs.Ease.cubicOut)
If you will add some code here after starting to implement i would be able to help more.
I am trying to create a mesh from an .obj file. I am doing this rather than using the obj loader because I meed to morph the shape using different sliders, select specific faces/vertices and draw a path between vertices.
I am currently getting errors when I try to render the scene and it seems to be something to do with the faces from the obj file. When I manually enter the faces I can create a triangle no problem.
Here is my code
var camera, scene, renderer,
geometry, material, mesh;
init();
animate();
function init() {
var xhr = new XMLHttpRequest();
xhr.open('GET', '/img/originalMeanModel.obj', false);
xhr.send(null);
var text = xhr.responseText;
var lines = text.split("\n");
for (i=0; i<19343; i++){
lines[i] = lines[i].split(" ");
}
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 1, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 20;
scene.add( camera );
var geometry = new THREE.Geometry()
/*
geometry.vertices.push( new THREE.Vector3( -0.01, 0.01, 0 ) );
geometry.vertices.push( new THREE.Vector3( -0.01, -0.01, 0 ) );
geometry.vertices.push( new THREE.Vector3( 0.01, -0.01, 0 ) );
*/
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
for(i=0; i<6449; i++){
geometry.vertices.push(
new THREE.Vector3(
parseFloat(lines[i][1]),
parseFloat(lines[i][2]),
parseFloat(lines[i][3])) );
}
/*
geometry.faces.push( new THREE.Face3( 0, 1, 2 ) );
geometry.faces.push( new THREE.Face3( 600, 1, 3000 ) );
geometry.faces.push( new THREE.Face3( 3000, 6400, 70 ) );
*/
for(i=6449; i<19343; i++){
geometry.faces.push( new THREE.Face3( parseInt(lines[i][1]), parseInt(lines[i][2]), parseInt(lines[i][3]) ) );
}
console.log(geometry.faces);
console.log(geometry.vertices);
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: false } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
}
function animate() {
// note: three.js includes requestAnimationFrame shim
requestAnimationFrame( animate );
render();
}
function render() {
renderer.render( scene, camera );
}
When I inspect the console the faces and vertices have been printed out as expected. I also have this error printed out 5700+ times
Uncaught TypeError: Cannot read property 'visible' of undefined Three.js:71
projectScene Three.js:71
render Three.js:245
render threeDemo.php:115
animate threeDemo.php:106
threeDemo.php:106 is this line of the animate function
render();
threeDemo.php:115 is this line of the render function
renderer.render( scene, camera );
Here is a link the the obj file that I am trying to create a mesh for. https://dl.dropbox.com/u/23384412/originalMeanModel.obj
Is anyone could point me in the right direction it would be really appreciated.
Thanks in advance.
You can use two OBJLoaders for loading each frame geometry. Then create a third Geometry and create the lines using as reference the vertices from the frame geometries.