I want to adjust multiple displacements maps - three.js

I want to add several displacement maps to my mesh.
Like this.
But I don't know how to do this.
I tried this.
var image = textureLoader.load('images/nail.jpg');
material = new THREE.MeshPhongMaterial({
color:0xffffff,
displacementMap: image,
displacementScale : 10,
side: THREE.DoubleSide
});
material.needsUpdate = true;
var geometry = new THREE.PlaneGeometry(30,30,30);
var plane = new THREE.Mesh(geometry, material);
scene.add(plane);
add image button source
$('#submit').click(function (){
var path = document.getElementById('file').value;
path = path.split('\\');
path = path[2];
console.log(path);
material.displacementMap = textureLoader.load('images/'+path);
});
Here's the reference site. You can see it by adding an image.

Related

Three.js LegacyGLTFLoader.js shadows missing

I have a GLTF version 1.0 model that I am importing into Three.js using LegacyGLTFLoader.js. When I do so, everything looks good, except that the model does not receive shadows. I am guessing that this is because the imported model's material is THREE.RawShaderMaterial, which does not support receiving shadows (I think). How can I fix this so that my imported model can receive shadows?
Here is sample code:
// Construct scene.
var scene = new THREE.Scene();
// Get window dimensions.
var width = window.innerWidth;
var height = window.innerHeight;
// Construct camera.
var camera = new THREE.PerspectiveCamera(75, width/height);
camera.position.set(20, 20, 20);
camera.lookAt(scene.position);
// Construct renderer.
var renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
renderer.shadowMapEnabled = true;
document.body.appendChild(renderer.domElement);
// Construct cube.
var cubeGeometry = new THREE.BoxGeometry(10, 1, 10);
var cubeMaterial = new THREE.MeshPhongMaterial({color: 0x00ff00});
var cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
cube.castShadow = true;
cube.translateY(15);
scene.add(cube);
// Construct floor.
var floorGeometry = new THREE.BoxGeometry(20, 1, 20);
var floorMaterial = new THREE.MeshPhongMaterial({color: 0x00ffff});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.receiveShadow = true;
scene.add(floor);
// Construct light.
var light = new THREE.DirectionalLight(0xffffff);
light.position.set(0, 20, 0);
light.castShadow = true;
scene.add(light);
// Construct light helper.
var lightHelper = new THREE.DirectionalLightHelper(light);
scene.add(lightHelper);
// Construct orbit controls.
new THREE.OrbitControls(camera, renderer.domElement);
// Construct GLTF loader.
var loader = new THREE.LegacyGLTFLoader();
// Load GLTF model.
loader.load(
"https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb",
function(event) {
var model = event.scene.children[0];
var mesh = model.children[0];
mesh.receiveShadow = true;
scene.add(model);
},
null,
function(event) {
alert("Loading model failed.");
}
);
// Animates the scene.
var animate = function () {
requestAnimationFrame(animate);
renderer.render(scene, camera);
};
// Animate the scene.
animate();
Here are my resources:
https://dl.dropboxusercontent.com/s/y2r8bsrppv0oqp4/three.js
https://dl.dropboxusercontent.com/s/5wh92lnsxz2ge1e/LegacyGLTFLoader.js
https://dl.dropboxusercontent.com/s/1jygy1eavetnp0d/OrbitControls.js
https://dl.dropboxusercontent.com/s/5piiujui3sdiaj3/1.glb
Here is a JSFiddle:
https://jsfiddle.net/rmilbert/8tqc3yx4/26/
One way to fix the problem is to replace the instance of RawShaderMaterial with MeshStandardMaterial. To get the intended effect, you have to apply the existing texture to the new material like so:
var newMaterial = new THREE.MeshStandardMaterial( { roughness: 1, metalness: 0 } );
newMaterial.map = child.material.uniforms.u_tex.value;
You also have to compute normal data for the respective geometry so lighting can be computed correctly. If you need no shadows, the unlint MeshBasicMaterial is actually the better choice.
Updated fiddle: https://jsfiddle.net/e67hbj1q/2/

How to use Texture.clone in Three.js?

My problem is very basic : I have a texture object, I want to clone it but Texture.clone doesn't seem to work as expected
My code is as basic as my problem :
var canvas = document.createElement("canvas");
canvas.width = canvas.height = 512;
canvas.getContext("2d").fillStyle = "#ff0000";
canvas.getContext("2d").fillRect(0,0,512,512);
var texture = new THREE.Texture(canvas);
texture.needsUpdate= true;
var material = new THREE.MeshBasicMaterial({map:texture});
var mesh = new THREE.Mesh(new THREE.PlaneGeometry(100,100), material);
scene.add(mesh)
//please, don't focus on "scene" and the webglrenderer object,
//it's define on the top of my code but it's not important here.
This code works as expected.
BUT if I change the line containing the material definition by
var material = new THREE.MeshBasicMaterial({map:texture.clone() });
nothing appear on the screen !
Why ?!
EDIT : Thanks to "TheJim01" , I realized that I didn't apply the "needsUpdate = true" on my cloned-texture.
With
var cloneTexture = texture.clone();
cloneTexture.needsUpdate = true;
var material = new THREE.MeshBasicMaterial({map:cloneTexture });
Everything works as expected.
Thank you
I haven't dug into the renderer code, so I don't know how it uses this information, but Texture.needsUpdate increments the "version" of the texture. CanvasTexture sets this right away, causing the version value to be 1 on the first render.
Texture.clone doesn't perpetuate the version information, and instead re-calls its constructor. Because you aren't setting needsUpdate after the clone, you are not following the same steps.
// from your code:
var texture = new THREE.Texture(canvas);
texture.needsUpdate= true;
// texture.version === 1
// but...
var material = new THREE.MeshBasicMaterial({ map:texture.clone() });
//material.map.version === 0
So the clone you're passing into the new material has a version of 0, which is apparently no good if you're using a canvas as the source.
This should resolve the issue:
var material = new THREE.MeshBasicMaterial({ map:texture.clone() });
material.map.needsUpdate = true;

THREE.js Apply multiple textures on same mesh or new mesh with a cloned geometry

SEE THE SOLUTION BELOW
I am really confused. The target i am trying to achieve is;
Use multiple textures on a loaded mesh.
I have searched multiple times and still i can see the similar questions but nothing helped me.
What i'v tried is (yet);
Created a new mesh with the target mesh's geometry and pushed to target object3d element. (Like a photoshop layer.)
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
Result, wrong geometry attributes, or am i missing something?.
But i think it could be a easier solution like using multiple textures at the same time with a correct order.
Full code:
sampleImage.onload = function() {
var mapCanvas = document.createElement('canvas');
mapCanvas.width = sampleImage.width;
mapCanvas.height = sampleImage.height;
var ctx = mapCanvas.getContext('2d');
ctx.translate(sampleImage.width / 2, sampleImage.height / 2);
ctx.rotate(Math.PI);
ctx.translate(-sampleImage.width / 2, -sampleImage.height / 2);
ctx.drawImage(sampleImage, 0, 0, sampleImage.width, sampleImage.height);
var texture = new THREE.Texture(mapCanvas);
texture.minFilter = THREE.LinearFilter;
texture.needsUpdate = true;
var material = new THREE.MeshPhongMaterial({map: texture, transparent: true});
var targetMesh = book.children[0].children[1],
newMesh = new THREE.Mesh(targetMesh.geometry, material);
book.children[0].children.push(newMesh);
};
I found a solution with cloning.
Do not push directly to the mesh group using javascript array push.
use .add method.
book.children[0].add(newMesh);

How to apply a texture to a custom geometry in Three.js

I successfully applied a texture to a cube geometry with this:
var geometry = new THREE.CubeGeometry(10, 10, 10);
var meshMaterial = new THREE.MeshPhongMaterial({ transparent: false, map: THREE.ImageUtils.loadTexture('/app/images/wood.jpg') });
meshMaterial.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, meshMaterial);
With this I get a nice textured cube like this:
Now I want to apply the same texture (512x512 jpg image) to a custom model I'm loading from an STL and this is what I get (in this case a pyramid):
This is the code:
loader.load(jsonParam.url, function (geometry) {
var meshMaterial = new THREE.MeshPhongMaterial({ transparent: false, map: THREE.ImageUtils.loadTexture('/app/images/wood.jpg') });
meshMaterial.side = THREE.DoubleSide;
var mesh = new THREE.Mesh(geometry, meshMaterial);
mesh.castShadow = false;
mesh.receiveShadow = true;
scene.add(mesh);
});
Why the texture is not being applied and I get only what seems to be an average of the texture colors?
You need UV mapping.
You can either edit the model in modelling software to add UV coordinates or maybe generate them as in the answers posted here.
I suppose another option would be to create your own shader that maps the texture to the model surface without using UV coordinates.

How can I change the texture in a Blender model loaded into three.js, after it's been loaded, with ImageUtils.loadTexture?

I'm doing a student project at involves a gift box where users can change how it looks.
I started learning what to do by making a cube, importing a texture and setting a gui.dat control to allow the user to change the texture.
I'm now trying to replace the cube with a blender model of a gift box but I'm having trouble changing the texture.
EDIT: The full code is on github here:
https://github.com/GitKiwi/GiftBox/blob/master/Workspace/Proto%208c%20Changing%20textures%20on%20giftbox.html
The coding for the working cube model is:
`// add cube with texture
var cubeGeometry = new THREE.CubeGeometry(4,4,4);
var cubeMaterial = new THREE.MeshLambertMaterial({ map:
THREE.ImageUtils.loadTexture("birthday.jpg") });
var cube = new THREE.Mesh(cubeGeometry,cubeMaterial);
cube.position.set (0,0,0);
cube.rotation.set (0,-1.2,0);
cube.receiveShadow = true;
// add the cube to scene
scene.add(cube); `
//gui texture change
`var controls = new function()
{ this.changeTexture = "birthday";
this.changeTexture = function (e){
var texture = THREE.ImageUtils.loadTexture
("../assets/textures/general/" + e + ".jpg");
cube.material.map = texture; }`
//gui control
var gui = new dat.GUI();
gui.add(controls, "changeTexture", ['christmas', 'valentine', 'birthday']).onChange(controls.changeTexture);
I'm loading the gift box in four parts and I'm just trying to get the first part, the box, to change texture. I load it with:
var box;
var loaderOne = new THREE.JSONLoader();
loaderOne.load('../assets/models/box.js', function (geometry)
{
var material = new THREE.MeshLambertMaterial({color: 0xffff00});
box = new THREE.Mesh(geometry, material);
box.position.set (5,0,5);
box.scale.set (1,1,1);
//box.name = "mybox";
scene.add(box);
});
I can't get it to change texture with the gui control. I've tried changing the "cube" to "box" in the gui texture change code and I've tried naming the box and calling it(commented out in the code above) but those didn't work. I've searched for answers to this in a number of places but I'm just really stuck. I feel I'm perhaps missing something obvious?
Any help to would really be appreciated.
The code wasn't working because there were no texture maps for the model I was importing.
What I did was go back to Blender and create a model with two textures that could each be applied to the whole model. The exported JSON file then had the model geometry and the two textures (with their texture maps).
In three.js I loaded it:
// load in geometry and textures
var loader = new THREE.JSONLoader();
loader.load('../models/two textures on cube.js', function (geometry, material)
{
matOne = new THREE.MeshLambertMaterial(material[1]);
matTwo = new THREE.MeshLambertMaterial(material[2]);
box = new THREE.Mesh(geometry, matOne);
//position, scale
box.position.set (0,0,0);
box.rotation.set (0,-1.2,0);
box.scale.set (2,2,2);
box.receiveShadow = true;
scene.add(box);
}, '../models');
and then used this code to switch the textures:
//gui control panel
var controls = new function()
{
//changing the texture
this.changeTexture = function (e)
{
switch (e)
{
case "birthday":
box.material = matOne;
break;
case "christmas":
box.material = matTwo;
break;
}
}
}
with this code for the gui.dat controls:
//gui control panel
var gui = new dat.GUI();
gui.add(controls, "changeTexture", ['birthday', 'christmas']).onChange(controls.changeTexture);

Resources