I'm a beginner using three.js and I am trying to have two materials defined on one single object, and alternate between the two using a visibilty flag, but with no success.
Is there another way, or can this be done?
var materials = [
new THREE.MeshPhongMaterial( { color: 0x00ff00,visible:true, shininess: 1 } ),
new THREE.MeshPhongMaterial( { color: 0xff0000,visible:false, shininess: 1 } )
];
obj= THREE.SceneUtils.createMultiMaterialObject( geometry, materials );
scene.add( obj);
scene.traverse(function (node){
if(node instanceof THREE.Mesh) {
node.visible =!node.visible;
}
});
I will entually aplly this to all objects in the scene that's why I'm using the scene.traverse
It looks like you're trying to apply visibility to the materials, yet you're checking the meshes during your traverse. Remove the visibility: true/false from your material definitions, and add the following line:
obj= THREE.SceneUtils.createMultiMaterialObject( geometry, materials );
obj.children[1].visible = false; // add this line
scene.add( obj);
This will apply visibility = false to the second mesh created by createMultiMaterialObject. Your traverse will then correctly flip the visibility of the meshes.
As you get to know THREE.js better, you'll want to look into THREE.MultiMaterial and geometry groups for applying multiple materials to a single mesh.
Related
In ThreeJS, it is possible to add more than one material to an Object3D/Mesh according to its documentation. We can use a single Material or use an array of Material:
Mesh typescript file class declaration and contructor (from ThreeJS src code):
export class Mesh<
TGeometry extends BufferGeometry = BufferGeometry,
TMaterial extends Material | Material[] = Material | Material[] // ### Here: Material[] ###
> extends Object3D {
constructor(geometry?: TGeometry, material?: TMaterial);
Here is my problem which I can't seem to be able to solve...
When I use a single Material, my Mesh is displayed. However, when I use two materials as an array, my Mesh won't display. I also don't have any error print out in the console when rendering the scene.
My ultimate goal here is to be able to have to separate materials for the inner and the outer part of my object.
Here is my code:
export function init(radius: number = 18.0, innerColor: number = 0xFFFFFF, outerColor: number = 0x444444) {
var obj = new Object3D();
loader.load(
objPath,
function(object){
obj = object;
const mesh = obj.children[0] as Mesh;
// WORKING:
mesh.material = new MeshPhongMaterial({color: outerColor});
// NOT WORKING: Using two materials
// mesh.material = new Array<Material>(new MeshPhongMaterial({color: outerColor}), new MeshPhongMaterial({color:innerColor}));
mesh.scale.setLength(radius)
scene.add(mesh);
},
function (error){
console.log(error)
}
);
}
Why can't I manage to see my object when using two materials ?
I known there was the MeshFaceMaterial in previous version and the material array acceptance of the contructor is supposed to be a replacement for that in some sens.
ThreeJS version: r128
Any help would be appreciated !
The easiest way is to clone your mesh and assign two separate materials, one for the inside, another for the outside:
const meshOuter = obj.children[0] as THREE.Mesh;
const meshInner = meshOuter.clone();
// Outer mesh shows front side
meshOuter.material = new THREE.MeshPhongMaterial({
color: outerColor,
side: THREE.FrontSide
});
// Inner mesh shows back side
meshInner.material = new THREE.MeshPhongMaterial({
color: innerColor,
side: THREE.BackSide
});
// Scale inner mesh down just a bit to avoid z-fighting
meshInner.scale.multiplyScalar(0.99);
I have tried a few different lights now (Directional, Spot, Point), but none of them produce a nice shadow on MeshFaceMaterial objects. Instead, the entire MeshFaceMaterial object will become black.
My Test Website (please view with a grain of salt, constantly being changed).
How can I use lights to create shadows on MeshFaceMaterials? Does MeshFaceMaterial support shadows? The documentation says "Affects objects using MeshLambertMaterial or MeshPhongMaterial."
Here is sample code of how I am loading .json model.
loader.load('sample-concrete.js', function ( geometry, materials ) {
mesh1 = new THREE.Mesh(
geometry, new THREE.MeshFaceMaterial( materials )
);
mesh1.rotation.x = -Math.PI / 2;
scene.add( mesh1 );
});
and here is a sample of the material from my .json file.
"materials": [
{
"DbgIndex" : 0,
"DbgName" : "Steel",
"colorDiffuse" : [0.3059, 0.0471, 0.0471],
"colorAmbient" : [0.3059, 0.0471, 0.0471],
"colorSpecular" : [1.0000, 1.0000, 1.0000],
"transparency" : 1.0,
"specularCoef" : 25.0,
"vertexColors" : false
}
Thank you.
A MeshFaceMaterial is just a collection of materials. So if your materials variable contains MeshLambertMaterial or MeshPhongMaterial you should be fine. Shadows will be generated from a DirectionalLight or a SpotLight.
Just make sure your renderer has:
renderer.shadowMapEnabled = true;
your light has:
light.castShadow = true;
each one of your meshes:
mesh.castShadow = true;
and you have at least one object (a plane for example) where you do:
plane.receiveShadow = true;
I'm having trouble applying a texture to an object I exported. My code looks like this:
var loader = new THREE.ObjectLoader();
var texture = THREE.ImageUtils.loadTexture('models/mountain/mountain.png');
loader.load("models/mountain/mountain.json", function (obj) {
var material = new THREE.MeshPhongMaterial({
map: texture
});
mesh = new THREE.Mesh( obj, material );
scene.add( mesh );
});
Just adding the obj to the scene works fine, but when I have to set a mesh and texture I get an error. What should the correct syntax be?
your problem may be that the "obj" returned by the ObjectLoader is actually just a Object3D. The objects containing the actual geometry and materials are children of this "obj".
So to change material you need to:
for(var i = 0; i < obj.children.length; i++)
{
obj.children[i].material = new THREE.PhongMaterial...
}
Also, please look into the MTL loader. OBJ/MTL loader is the usual way to use textured OBJs, as seen in the example: http://threejs.org/examples/#webgl_loader_obj_mtl
I want to change a mesh from a group, trriged by a button.
I'm loading an external .obj file:
loader.load( obj, function ( object ) {
createScene( object, mod.tipo, pid, cor.replace("#","0x") );
});
and add on a group
function createScene( geometry, name, id, cor ) {
geometry.traverse( function ( child ) {
if ( child instanceof THREE.Mesh ) {
var material = new THREE.MeshPhongMaterial( {
specular: 0xffffff,
shininess: 10,
side: THREE.DoubleSide,
map: THREE.ImageUtils.loadTexture('/3d/js/texturas/white.jpg'),
shading: THREE.SmoothShading
} );
material.color.setHex(cor);
child.material = material;
group.add( child );
}
and add this group in a scene: scene.add( group )
to change i set de visible of my mesh to false. But a want to remove it from scene and group.
I already try scene.remove('name') and scene.remove(mesh) but didnt work.
Someone know how to do this?
You probably want:
var toRemove = scene.getNodeByName ( 'name', true );
if (toRemove !== undefined)
scene.remove ( toRemove );
which searches in your scene for a node named name and then removes the resulting node from the scene.
I get a solution:
In my case i was using a group to join the meshes and rotate all of them to start position.
First i have to add the mashes directly in the scene. I could'n make it work on a group
Second i have a global variable that contain an array of all meshes. And as was tring to remove use this reference. I have to change this logic.
So i create this function:
var clearScene = function(name) {
var objsToRemove = scene.children;
for (var t = 0; t<objsToRemove.length; t++){
if (objsToRemove[t] instanceof THREE.Mesh) {
if (objsToRemove[t].name = name) {
scene.remove(objsToRemove[t]);
break;
}
}
}
}
it goes on each meshe looking for the mashe i want to delete. and simply call the remove frome the scene.
this works for me.
I have to change my group logic and mabe rotate mesh by mashe to default position, i think that i will have a better performance
This is my object:
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { map: THREE.ImageUtils.loadTexture( "image.png" ) } ) );
object.position.set(2, 3, 1.5);
now after I've created this object in init(); function, I can directly go to the object and change his position,like this:
object.position.x = 15;
Now the question is how can I change the opacity of the texture???
Thanks :-)
THREE.MeshLambertMaterial extends THREE.Material which means it inherits the opacity property, so all you need to do is access the material on your object, and change the opacity of the material:
object.materials[0].opacity = 1 + Math.sin(new Date().getTime() * .0025);//or any other value you like
Also note that the material must have it's transparent property set to true.
object.materials[0].transparent = true;
(Thank you Drew and Dois for pointing this out)
Update
the property is now simply material:
// enable transparency
object.material.transparent = true;
// set opacity to 50%
object.material.opacity = 0.5;
var map = THREE.ImageUtils.loadTexture( myJSONObject[i].url );
var material = new THREE.MeshLambertMaterial( { map: map, transparent: true } );
var object = new THREE.Mesh( geometry, material );
material.opacity = 0.6;
I know this question is very old but I wanted to give my answer from what I used in case someone needs it. With three.js, I used tweening through Greensock's TweenMax/TweenLite. With that, I was able to tween any property of any object and it ran smoothly. Check out the library here. All I needed to tween the properties was:
TweenLite.to(object, duration, properties);
where duration is in seconds and properties are in an object. The "gotcha" for this, especially while using three.js, is to make sure you get specific with the object parameter. For example, per this question, if you are changing the opacity of a mesh, you cannot do
TweenLite.to(mesh, 2, {material.opacity: 0});
rather, you need to be more specific and write
TweenLite.to(mesh.material, 2, {opacity: 0});
I hope this helps someone. Tweening is really awesome!