Three.js: object light & displacementMap - javascript

I'm trying to make a realistic silver ring like this:
with different colors and size.
This is my result at this moment:
As you can see my ring is not smooth.
I don't know if it's a problem of the model, or of the code:
There are my models ( i'm using 2 model, one for the light silver and another for the dark one )
light: http://www.websuvius.it/atma/myring/assets/3d/anello-1/interno.obj
dark: http://www.websuvius.it/atma/myring/assets/3d/anello-1/esterno.obj
This is part of my code:
...
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, containerWidth / containerHeight, 0.1, 20000 );
scene.add(camera);
camera.position.set( 0, 150, 400 );
camera.lookAt(scene.position);
renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( containerWidth, containerHeight );
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = false;
var ambientlight = new THREE.AmbientLight( 0xffffff );
scene.add( ambientlight );
var manager = new THREE.LoadingManager();
manager.onProgress = function ( item, loaded, total ) {};
var onProgress = function ( xhr ) {};
var onError = function ( xhr ) {};
var loader = new THREE.OBJLoader( manager );
var textureLoader = new THREE.TextureLoader( manager );
loader.load( path + '/assets/3d/anello-1/interno.obj', function ( object ) {
var backgroundTexture = textureLoader.load( path + '/assets/3d/texture/argento_standard_512_1024.jpg');
backgroundTexture.flipY = false;
var background = new THREE.MeshPhongMaterial({
map: backgroundTexture,
envMap: textureCube,
reflectivity:0.5
});
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
child.material = background;
}
});
object.position.y =-50;
scene.add(object);
}, onProgress, onError );
loader.load( path + '/assets/3d/anello-1/esterno.obj', function ( object ) {
var geometry = object.children[ 0 ].geometry;
var materials = [];
var scavoTexture = textureLoader.load( path + '/assets/3d/texture/argento_scuro_512_1024.jpg');
var material = new THREE.MeshPhongMaterial({
map: scavoTexture,
envMap: textureCube,
reflectivity:0.5
});
materials.push(material);
var customTexture = textureLoader.load( path + "/" + immagine);
customTexture.flipY = false;
var custom = new THREE.MeshPhongMaterial({
map: customTexture,
transparent: true,
opacity: 1,
color: 0xffffff
});
materials.push(custom);
esterno = THREE.SceneUtils.createMultiMaterialObject(geometry, materials);
esterno.position.y=-50;
scene.add(esterno);
}, onProgress, onError );
container = document.getElementById( 'preview3d' );
container.appendChild( renderer.domElement );
animate();
How can i get a better result?
I have to change my models? My code? Both?
Thanks
[EDIT]
Thanks, everyone for the comments.
This is the result now:
Now, i have another question. Is there a way to extrude the text and other elements? I have only a png element of the central part.
Thanks
[EDIT 2]
Now I'm making some experiment with displacementMap.
This is the result at this moment:
Now the problem is: the browser freeze due to heavily subdivided mesh.
This is a part of my code:
var external = new THREE.CylinderBufferGeometry( 3.48, 3.48, 4, 800, 400, true, -1.19, 5.54 );
var materials = [];
var baseMaterial = new THREE.MeshPhongMaterial({
color: 0x222222
});
materials.push(baseMaterial);
var textureMaterial = new THREE.MeshPhongMaterial({
map: image, //PNG with text and symbols
transparent: true,
opacity: 1,
reflectivity:0.5,
color: 0xc0c0c0,
emissive: 0x111111,
specular: 0xffffff,
shininess: 34,
displacementMap: image, //same as map
displacementScale: 0.15,
displacementBias: 0
});
materials.push(textureMaterial);
var externalObj = THREE.SceneUtils.createMultiMaterialObject(external, materials);
I think that the problem is the 800x400 segments of cylinder, that generate a mesh with 320000 "faces". There is a way to optimize the performance keeping this level of detail?
Thanks again.
P.s. maybe I have to open a new question?

Answering your new question: you could use your png also as displacement map. Have a look at the official example: https://threejs.org/examples/webgl_materials_displacementmap.html
But you probably need to heavily subdivide your "central part" for displacement.
Maybe in your case there will be your png as bump map sufficient enough, then you you might check out this example: https://threejs.org/examples/webgl_materials_bumpmap.html

Related

Three.js | Raycasting imported .obj model via blender

I have loaded a 3D terrain via Blender with the OBJLoader. I have also created a mesh (yellow pointer on the picture below) which i want to follow the mouse while it's on the terrain.
I tried to use the raycaster method but i don't know exactly how to apply it to my .obj as it seems that I can't access it outside the loader.
How can i make my pointer(yellow mesh) stick to my terrain (loaded .obj) while it's following the mouse ?
Please help a total three.js noob...
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 10000 );
var renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera.position.z = 150;
camera.position.y=300;
camera.position.x=350;
var light = new THREE.HemisphereLight( 0xffffbb, 0x080820, 1 );
scene.add( light );
var controls= new THREE.OrbitControls(camera,renderer.domElement);
controls.enableDamping=true;
controls.campingFactor=0.25;
controls.enableZoom=true;
controls.minDistance= 1;
controls.maxDistance=3000;
controls.minPolarAngle= -Math.PI/2;
controls.maxPolarAngle= Math.PI/2;
var terrain;
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load('models/terrain.mtl',
(materials) => {
materials.preload();
var loader = new THREE.OBJLoader();
loader.setMaterials(materials);
loader.load(
'models/terrain.obj',
function ( object ) {
terrain = object;
scene.add( terrain );
});
}
);
var Cylindergeometry = new THREE.CylinderGeometry( 5, 0, 8, 32 );
var material = new THREE.MeshBasicMaterial( {color: 0xffff00} );
var cylinder = new THREE.Mesh( Cylindergeometry, material );
var Torusgeometry = new THREE.TorusGeometry( 7, 0.5, 8, 6);
var material = new THREE.MeshBasicMaterial( { color: 0xffff00 } );
var torus = new THREE.Mesh( Torusgeometry, material);
Torusgeometry.rotateX(1.5708);
Torusgeometry.translate(0,-4.5,0);
//Merge the two parts to make it one mesh (yellow pivot)
var PivotGeometry = new THREE.Geometry();
PivotGeometry.merge(Cylindergeometry);
PivotGeometry.merge(Torusgeometry);
var pivot = new THREE.Mesh(PivotGeometry, material);
scene.add(pivot);
// Trying to Raycast the terrain to make the pivot follow the mouse while it's on it
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
function onMouseMove( event ) {
mouse.x = ( event.clientX / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( event.clientY / renderer.domElement.clientHeight ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
// See if the ray from the camera into the world hits one of our meshes
var intersects = raycaster.intersectObject( terrain,true );
// Toggle rotation bool for meshes that we clicked
if ( intersects.length > 0 ) {
pivot.position.set( 0, 0, 0 );
pivot.lookAt( intersects[ 0 ].face.normal );
pivot.position.copy( intersects[ 0 ].point );
}
}
//Met à jour l'affichage de la scène
var animate = function () {
requestAnimationFrame( animate );
renderer.render( scene, camera );
};
animate();
My loaded .obj terrain
You have the following line in your code:
var intersects = raycaster.intersectObjects( object );
I don't see where object as a variable is declared. I suggest you declare this variable in your module scope, assign the loaded OBJ file to it and then use this code instead:
var intersects = raycaster.intersectObject( object, true );
OBJLoader.load() returns an object of type THREE.Group which is in some sense a container holding an arbitrary number of meshes. Hence, you want to raycast recursively.

Having problems to set a glTF object to a different layer in Three.js

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

How to load two or more .obj objects in a scene using three.js from user input?

How to load more than two objects in three js of .obj file . I tried by applying a for loop across the loader file of obj and mtl but it is not working instead it shows only the last object ?Please if any one can help.
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
camera.position.z = 1000;
// scene
scene = new THREE.Scene();
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffeedd );
directionalLight.position.set( 0, 0, 1 ).normalize();
scene.add( directionalLight );
// model
THREE.Loader.Handlers.add( /\.dds$/i, new THREE.DDSLoader() );
var setBaseUrl1 = 'newObj/';
var setPath1 = 'newObj/';
var i = 0;
var mltLoad1,objLoad1;
var mtlLoader = new Array(res.length);
var objLoader = new Array(res.length);
for(i=0;i<res.length;i++)
{
mtlLoad1 = String(res[i].concat(".mtl"));
objLoad1 = String(res[i].concat(".obj"));
mtlLoader[i] = new THREE.MTLLoader();
mtlLoader[i].setBaseUrl( setBaseUrl1 );
mtlLoader[i].setPath( String(setPath1) );
mtlLoader[i].load( mtlLoad1, function( materials ) {
materials.preload();
objLoader[i] = new THREE.OBJLoader();
objLoader[i].setMaterials( materials );
objLoader[i].setPath( setPath1 );
objLoader[i].load( objLoad1, function(object) {
object.position.y = -95;
object.position.z = 500;
camera.position.x = 500;
scene.add( object );
});
});
}
}
load model without for loop ,it looks your variable holds obj override .

Use custom mesh instead of generated one in three.js

I have just discovered the world of three.js and it's amazing.
I downloaded the examples, and started checking some of them.
I have never been coding in JavaScript, so I was wondering if somebody could help me with editing one of the example files (misc_controls_trackball.html). Instead of generated geometry (mesh.position.x = ( Math.random() - 0.5 ) ...) I was wondering if I could just include an already made mesh (from 3 studio max for example)?
I think this is the part of the code which generates the mesh:
// world
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
var geometry = new THREE.CylinderGeometry( 0, 10, 30, 4, 1 );
var material = new THREE.MeshLambertMaterial( { color:0xffffff, shading: THREE.FlatShading } );
for ( var i = 0; i < 500; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
}
In what way should this be changed, so that I could import my external mesh (in form of .3ds, .obj, .dae, does not matter)?
Thank you.
Here is the misc_controls_trackball.html example file along with "js" folder.
Tried this?
http://threejs.org/examples/webgl_loader_collada
It`s an example for Collada, but for the other formats the concept is the same, just using a different loader.
var loader = new THREE.ColladaLoader();
// Depending on how you created your model, you may need to
loader.options.convertUpAxis = true;
// Then load it:
loader.load( './models/collada/monster/monster.dae', function ( collada ) {
// All this will happen asynchronously
dae = collada.scene;
// Before displaying it, you can tweak it as necessary
dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
dae.updateMatrix();
scene.add(dae);
// At the next frame, you`ll have your model loaded.
} );
EDIT, additions
First you need the links to the proper libraries, including the ColladaLoader
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.js"></script>
<script src="js/loaders/ColladaLoader.js"></script>
Then a number of things needed fixing in the code.
- scene object was missing
- Model loaded, but to be scaled up a bit
- No call to render() in the animate function, so you had no animation.
- The fog statement was broken... Best spending some time on the basics, first...
function init() {
// Create your scene first
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
var loader = new THREE.ColladaLoader();
// Depending on how you created your model, you may need to
loader.options.convertUpAxis = true;
// Then load it:
//loader.load( './models/collada/monster/monster.dae', function ( collada ) {
loader.load( 'models/monster.dae', function ( collada ) {
// All this will happen asynchronously
dae = collada.scene;
// Give it a decent scale
dae.scale.x = dae.scale.y = dae.scale.z = 1;
dae.updateMatrix();
scene.add(dae);
// At the next frame, you`ll have your model loaded.
} );
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
//renderer.setClearColor( scene.fog.color, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
// The following is not necessary at this stage, as you`ll call it
// from animate later down (if you want to do an animation, of course,
// which I guess you do)
render();
}
And the animate function should look like this
function animate() {
requestAnimationFrame( animate );
controls.update();
render();
}
Hope that helps! :)

three.js load object/model and then manipulate sub-parts/children

I have a 3D model of a robot arm, that I want displayed and manipulated in the browser.
My question is: how do I load the model into three.js, so that I can manipulate all sub-parts of the robot arm.
As an example I have a rotary motor and a shaft attached as an assembly in Inventor.
Image: http://i.stack.imgur.com/custz.png
This is exported as an stl file and imported in Three.js using STLLoader.js.
Image: http://i.stack.imgur.com/nLmBe.png
I want to know how I can manipulate the shaft to turn to a specified angle.
I have loaded the model using the following code:
<div id="container"></div>
<script src="three.js\build\three.min.js"></script>
<script src="js\STLLoader.js"></script>
<script>
// Set size variables
var SIZE_x = 400, SIZE_y = 400;
// Set three main THREE variables
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(75, SIZE_x/SIZE_y, 0.1, 1000);
var renderer = new THREE.WebGLRenderer();
// Set renderer options
renderer.setSize(SIZE_x, SIZE_y);
renderer.setClearColor(0xEEEEEE, 1.0);
renderer.clear();
// Append to HTML Dom
//document.body.appendChild(renderer.domElement);
$('#container').append(renderer.domElement);
// Create light
var pointLight = new THREE.PointLight(0xFFFFFF);
pointLight.position.x = 10;
pointLight.position.y = 50;
pointLight.position.z = 130;
scene.add(pointLight);
// Move camera
camera.position.x = 0;
camera.position.y = 20;
camera.position.z = 20;
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var geometry = event.content;
var material = new THREE.MeshLambertMaterial( { ambient: 0xff5533, color: 0xff5533 } );
var mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
mesh.traverse(function ( child ) {
//if ( child instanceof THREE.Mesh ) {
console.log("Hej: " + child);
//}
});
//scene.add( new THREE.Mesh( geometry ) );
console.log('Loaded');
} );
loader.load( 'models/AssemblySimple1.stl' );
// Render loop
var render = function () {
requestAnimationFrame(render);
camera.lookAt(scene.position);
renderer.render(scene, camera);
};
render();
</script>
Any points and hints are welcome. Also if there is a preferred export file format. I have both SolidWorks and Inventor at my disposal. Or if I've taken a completely wrong approach to the problem, please let me know of other ways.
Thanks
Three js has function called THREE.STLLoader() .This one can be used to load stl file.
Here is the way how it is loaded
var loader = new THREE.STLLoader();
var group = new THREE.Object3D();
loader.load("../lib/SolidFz.stl", function (geometry) {
console.log(geometry);
var mat = new THREE.MeshLambertMaterial({color: 0x7777ff});
group = new THREE.Mesh(geometry, mat);
group.rotation.x = -0.5 * Math.PI;
group.scale.set(0.6, 0.6, 0.6);
scene.add(group);
});
Here scene
var scene new THREE.Scene();
After that you have a created 3d object and loaded one added into that 3d object.Then you can control that 3d object as you wish. According to this way you can load several parts and do the what you want to do with that

Categories

Resources