Three.js - Change Material on Runtime - javascript

I have some .js files exported from Blender and load them with THREE.JSONLoader();
my callback:
var callback = function( geometry ) { createMesh(geometry);
my loading:
loader.load( "Models/sculp.js", callback );
my create method:
function createMesh(geometry){
inArr[id] = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: 0xbbbbbb} ) );
inArr[id].scale.set( 100, 100, 100 );
scene.add( inArr[id] );
id++;
}
Now I want to change my material on runtime by using my keyboard (changes color and opacity).
How can I do that?

As you create a new material for each mesh I assume you only want to change the color of one mesh and not of all in the inArr array, and you probably need some sort of select for that. But changing the color of the material alone is quite easy:
var onKeyDown = function(event) {
if (event.keyCode == 67) { // when 'c' is pressed
object.material.color.setHex(0xff0000); // there is also setHSV and setRGB
}
};
document.addEventListener('keydown', onKeyDown, false);
object is the mesh you want to change. Key codes can be found here: http://www.cambiaresearch.com/articles/15/javascript-char-codes-key-codes

Related

three.js - mask transparency with a texture image

I would like to add a mask effect on my scene.
I found this cool jsfiddle : https://jsfiddle.net/f2Lommf5/4703/
I've been wondering if it's possible to set the white part of that texture transparent so I can have my background object cropped depending on the above plane texture.
I tried to play with the alphaTest value but in vain.
Does anyone have any idea on how to reach this result ? Thank you
I'm not 100% sure I understand your intended result but it should be possible to implement the effect via post processing. In the following live demo, MaskPass is used to create a mask where no pixels of the actually beauty pass are rendered. The important code section is:
var clearPass = new ClearPass();
var maskPass = new MaskPass( sceneMask, camera );
maskPass.inverse = true;
var renderPass = new RenderPass( scene, camera );
renderPass.clear = false;
var clearMaskPass = new ClearMaskPass();
var outputPass = new ShaderPass( CopyShader );
var parameters = {
minFilter: THREE.LinearFilter,
magFilter: THREE.LinearFilter,
format: THREE.RGBFormat,
stencilBuffer: true
};
var renderTarget = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, parameters );
composer = new EffectComposer( renderer, renderTarget );
composer.addPass( clearPass );
composer.addPass( maskPass );
composer.addPass( renderPass );
composer.addPass( clearMaskPass );
composer.addPass( outputPass );
Notice that the mask object (the plane) is managed in a separate scene.
Live demo: https://jsfiddle.net/e6p0axb1/5/

Threejs IcosahedronGeometry vertex color

I am trying to change color of a selected vertex dynamically. Referring to https://jsfiddle.net/pmankar/m70cpxes/, I created a IcosahedronGeometry point cloud geometry and on click event I would like to change the color of vertex 100.
document.addEventListener('click', function() {
mesh.geometry.colorsNeedUpdate = true;
mesh.geometry.colors[100] = new THREE.Color("rgb(255,0,0)");
console.log(mesh.geometry);
})
Now, I have two questions:
How to make the vertex 100 change its colors
Why is it showing random color to the point cloud
You declared var material, then created materail = new THREE.PointsMaterial(); and then applied material again to your mesh. There's a typo: material != materail.
Also, to have different colors of vertices you have to set colors of them
geometry = new THREE.IcosahedronGeometry(102, detailsLevel);
var colors = [];
geometry.vertices.forEach(function(){
colors.push(new THREE.Color(0xffffff));
});
geometry.colors = colors;
and then in your material you have to set vertexColors as THREE.VertexColors
material = new THREE.PointsMaterial( { size:4, vertexColors: THREE.VertexColors} );
after all, in your "click" event listener you can do
mesh.geometry.colors[100].set(0xff0000);
mesh.geometry.colorsNeedUpdate = true;
jsfiddle example

using three.js, need to replace the image dynamically

I am new to three.js, pls guide me how to change the image dynamically
Tried with the below option of replacing the image from the original image
I tried with the below code
var mtlLoader = new THREE.MTLLoader();
mtlLoader.load( 't2.mtl', function( materials ) {
materials.preload();
var texture = THREE.TextureLoader('bbc.png');
var material = new THREE.MeshLambertMaterial( { map: texture } );
materials.setMaterials( material );
objLoader.load( 't2.obj', function ( object ) {
scene.add( object );
}, onProgress, onError );
});
I am not getting any error but updated image is not displayed ? but the actual object is displayed in blank color. Pls suggest
Thanks
Shaik

three.js change material for .obj file using a button

I'm having trouble creating a button to change the .mtl file of my .obj to another .mtl using three.js. Any thoughts on how to do that would be awesome!
Here is the original code, adapted from the objmtl loader example on threejs.org. So far, I've only been able to make the obj visible / invisible using a button, but I'd love to add more buttons to change the .mtl files to reflect .mtls with other colors and properties.
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl( 'examples/obj/male02/' );
mtlLoader.setPath( 'examples/obj/male02/' );
mtlLoader.load( 'male02_dds.mtl', function( materials ) {
materials.preload();
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials( materials );
objLoader.setPath( 'examples/obj/male02/' );
objLoader.load( 'male02.obj', function ( object ) {
object.position.y = - 95;
dude = object;
scene.add( dude );
info.innerHTML += '<br/><br/><input id=pants2 type="button" onclick="dude.visible = false" value="Dude: OFF"/>';
info.innerHTML += '<input id=pants2 type="button" onclick="dude.visible = true" value="Dude: ON"/>';
If you can work without the .mtl file, you can use something like that:
texture = new THREE.TextureLoader().load('dir/to/texture.jpg');
sectexture = new THREE.TextureLoader().load('dir/to/second_texture.jpg');
lambert = new THREE.MeshLambertMaterial({color: 0xffffff, map: texture});
objLoader = new THREE.OBJLoader();
objLoader.setPath( 'examples/obj/male02/' );
objLoader.load( 'male02.obj', function ( object ) {
object.traverse(function(child) {
if (child instanceof THREE.Mesh){
child.material = lambert;
}
});
scene.add( object );
}, onProgress, onError );
With that, you can load your texture into the lambert material, so if you want to change it you can use lambert.map=texture or lambert.map=sectexture.
[EDIT] I have fixed the mistake on child.material

Rendering single mesh color change

I have a grid that contains boxes, very similar to http://threejs.org/examples/#canvas_interactive_voxelpainter. Now I initiated a hover state when a box on the scene is mouseover it turns the background gray. Which is great! Except when I multiple "box" on the grid and I go to change the material background color of the hovered item, it renders all of the "box's" with a gray background.
Heres what I am doing:
var voxel = new THREE.Mesh( this.cubeGeometry, this.cubeMaterial );
voxel.position.copy( intersect.point ).add( intersect.face.normal );
voxel.position.divideScalar( 50 ).floor().multiplyScalar( 50 ).addScalar( 25 );
this.scene.add( voxel );
this.blocks.push( voxel );
var domEvents = new THREEx.DomEvents(this.camera, this.renderer.domElement)
// DOM events for inside 3d rendering
domEvents.addEventListener(voxel, 'mouseover', this.onDocumentMouseOverCube.bind(this),false);
domEvents.addEventListener(voxel, 'mouseout', this.onDocumentMouseOutCube.bind(this),false);
Here we create our box - we than give it eventListeners for that specific mesh. Once this mesh is hovered over, our mouseover is executed:
this.mouse.x = ( event.origDomEvent.clientX / this.renderer.domElement.width ) * 2 - 1;
this.mouse.y = - ( event.origDomEvent.clientY / this.renderer.domElement.height ) * 2 + 1;
this.raycaster.setFromCamera( this.mouse, this.camera );
var intersects = this.raycaster.intersectObjects( this.blocks );
if ( intersects.length > 0 ) {
var intersect = intersects[ 0 ];
if ( intersect.object != this.plane ) {
console.log(intersect.object);
// update color on hover
intersect.object.material.color.setHex(this.colorHover);
console.log("hover color");
this.render();
}
}
Now this works great, the only issue is - this.render() is called (this.renderer.render( this.scene, this.camera )) like it should be. But when I have multiple box's on it goes ahead and changes every single background color of each box I have even logged all my objects to confirm object.material.color is the gray hex for only one box and that not all of the box's are being set, which proves to be true. I am sending the correct data over. So I am assuming it has to do with the rendering of the actual engine?
Suggestions?
There is only one instance of the material, which is shared among the meshes. The easy solution is to clone the material for each mesh:
var voxel = new THREE.Mesh( this.cubeGeometry, this.cubeMaterial.clone() );
Now every box accepts its own color.
I dont know if this still applies but thinking about performance you would want to go with a custom shader material, because the attributes and vertex/fragment programs are copied by reference then. See this post Three.js, sharing ShaderMaterial between meshes but with different uniform sets.
Example code:
var phongShader = THREE.ShaderLib.phong;
this.shaderMaterial = new THREE.ShaderMaterial({
uniforms: phongShader.uniforms,
vertexShader: phongShader.vertexShader,
fragmentShader: phongShader.fragmentShader,
lights:true
});
var voxel = new THREE.Mesh( this.cubeGeometry, this.shaderMaterial.clone() );
And then you change the color via uniforms like so:
intersect.object.material.uniforms.diffuse.value.setHex( this.colorHover );
Three.js r.71
PS: cloning the default Phong material in r.71 also shows only one programm in the renderer info for me, so maybe Three.js is optimizing this internally.

Categories