Applying different textures on a THREE.js OBJLoader loaded object - three.js

I have a square shape .obj model and 2 textures. How do I apply one texture on it's top face and another on rest of faces?

There are a ton of ways to do what you're asking all with varying complexity depending on your needs. It looks like you want to apply two materials to your object and not two textures.
It looks this way because it seems you want the textures to be interchangeable so there's no way you're going to combine the images and keep resolution and OBJ & THREE.Material only support one set of uv attributes so you can't use a single material and multiple textures. So multiple materials it is...
Multiple materials
If you have two materials (2 THREE.Materials which correlate to 2 WebGL programs) then each face needs to know what material it's assigned to.
While the THREE.js multi material API has been in flux for quite a while and there are differences between THREE.Geometry and THREE.BufferGeometry, fortunately for you THREE.OBJLoader supports material groups out of the box. To get this into THREE.js, you want to apply multiple materials in your 3D editor to your object and then export the OBJ to get everything. Doing it by hand is a little harder and requires calling addGroup as shown in the docs/the link above.
In THREE.js you simply pass in all the materials as an array to your object demonstrated in this answer. I also updated your fiddle to do the same thing. Relevant code shown below
var loadingManager = new THREE.LoadingManager();
var ObjLoader = new THREE.OBJLoader(loadingManager);
var textureLoader = new THREE.TextureLoader(loadingManager);
//Material 1 with first texture
var material = new THREE.MeshLambertMaterial({map: textureLoader.load('https://dl.dropboxusercontent.com/s/nvnacip8fhlrvm4/BoxUV.png?dl=0')});
//Material 2 with second texture
var material2 = new THREE.MeshLambertMaterial({map:
textureLoader.load('https://i.imgur.com/311w7oZ.png')});
ObjLoader.load(
'https://dl.dropboxusercontent.com/s/hiazgei0rxeirr4/cabinet30.obj?dl=0',
function ( object ) {
var geo = object.children[0].geometry;
var mats = [material, material2];
//These are just some random groups to demonstrate multi material, you need to set these up so they actually work for your object, either in code or in your 3D editor
geo.addGroup(0,geo.getAttribute("position").count/2,0);
geo.addGroup(geo.getAttribute("position").count/2,
geo.getAttribute("position").count/2,1);
//Mesh with multiple materials for material parameter
obj = new THREE.Mesh(geo, mats);
obj.position.y = 3;
});

Related

threejs - creating 3d perspective for a line

I'm working on an app where I visualize ATV trails in a 3d perspective (NAIP imagery draped over elevation data). I am using three.js for the rendering engine.
In the above image, the white line you see is just a THREE.Line instance, where I convert a trails gps coordinates into threejs coordinates. I'd like to add more of 3d perspective to this line. I tried implementing a THREE.TubeGeometry where the path was a THREE.CatmullRomCurve3 using the same Vector3 points as how I built the line you see in the image above. That did not produce a desirable result...
From the many, many THREE examples I have looked at, I really think an extruded geometry would achieve the look I am after... But I cant for the life of me figure out how to extrude a geometry for the line. Any suggestions/thoughts?
UPDATE 1:
Here is my desired look (same trail - no imagery). This image was produced in QGIS using the Q2Threejs plugin
UPDATE 2: Here is a code of how I have attempted to create a tubegeometry. Maybe I am messing something up in there...
// trailVectors are an array of Vector3 - same as ones used to create line
var trailCurve = new THREE.CatmullRomCurve3(trailVectors);
var tubeGeometry = new THREE.TubeGeometry(trailCurve,80,1,15,false);
var material = new THREE.MeshBasicMaterial({color:0x00ff00});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
var wireframeMaterial = new THREE.LineBasicMaterial({color:0xffffff,lineWidth:2});
var wireframe = new THREE.Mesh(tubeGeometry,wireframeMaterial);
tubeMesh.add(wireframe);
scene.add(tubeMesh);
UPDATE 3
THREE.TubeGeometry(trailCurve,80,4,2,false) per mzartman request
I think that you should be able to achieve what you want with a TubeGeometry. I think the big thing is that your example (from the picture shown) has more than 2 radius segments. That gives it the tubular shape and makes it look sort of like a blob. If you set the radial segment count to 2 (as it's shown below) then I think it would look a lot better.
tubeGeometry = new THREE.TubeBufferGeometry(
[YOUR_PATH_HERE],
params.extrusionSegments, // <--- Edit this for higher resolution on the spline
3, // <--- This defines the height
2, // <--- This 2 keeps 2D (i.e. not a tube!!!!)
true );
var mesh = new THREE.Mesh( geometry, material );
var wireframe = new THREE.Mesh( geometry, wireframeMaterial );
mesh.add( wireframe );
scene.add( mesh );
Update:
I think that you might do better with a material that shows some shadow like the MeshPhong. Also, to do the wireframe you want to add it as an option in the material initialization. Give it a show with the following:
var tubeGeometry = new THREE.TubeGeometry(curve,80,1,2,false);
var material = new THREE.MeshPhongMaterial({color:0x00ff00, wireframe: true});
var tubeMesh = new THREE.Mesh(tubeGeometry,material);
scene.add(tubeMesh);

Dynamic change a lot of textures is very low performance

I am a newbie in Three js. In my project, I need draw a ground with a lot of texture. The ground has many layers, every layer has 4 textures and textures in different layers are different size. Below picture describe the ground:
Ground is one mesh has multiple material:
this.mesh = new THREE.Mesh(geometry, new THREE.MultiMaterial(materials));
Suppose I have a car it always at center of ground, in other word, center of all layers. When it move, the ground will translate to make sure the car always at center. So everytime ground translate, I need to update texture in new position.
The picture draw 3 layers for illustration, but in my project is 6 layers. So everytime all texture change, that means need to change 6 * 4 = 24 textures, and that cause low fps in my program.
This is my function to load texture from indexed DB every time a texture change:
Ground.prototype.loadTextureFromIndexedDB = function (url, materialIndex) {
var loader = new THREE.TextureLoader();
loader.crossOrigin = '';
loader.load(url,
function (texture) {
var groundMaterial = ground.mesh.material.materials[materialIndex];
groundMaterial.map.dispose();
groundMaterial.map = texture;
groundMaterial.map.anisotropy = ground.maxAnisotropy;
groundMaterial.map.minFilter = THREE.LinearFilter;
groundMaterial.map.needsUpdate = true;
img = null;
window.URL.revokeObjectURL(url);
});
}
I have tried many solutions. One of them is make a mesh with a BufferGeometry and MultiMaterial with array of ShaderMaterial. As what I known, it is the best for performance in THREE JS, isn't it? If it is then maybe THREE JS is not powerful as I thinked. Should I change to another API for my project?
Anyone suggest me any solution to make higher performance in my program? Thanks a lot!

ThreeJs and Blender (using colladaLoader): first contact

How can I render an exported scene (with many objects, each with different colors and different properties, such as rotation aroung an axis in the scene) from Blender (with colladaLoader -->.dae) in ThreeJs?
So, the first step is to learn how to create a scene in threeJs and learn some feature with Blender. When you are ready, create your first model and before exporting keep this in mind:
you need to an object with vertices, so if you just create a text with Blender, you have to convert it to a mesh, otherwise threeJs will not render it
be sure to choose the Blender render option and not the Cycles,
otherwise the .dae you export will not be rendered in threeJs
when applying a texture, use just colors and basic materials (basic, phong and lambert) - the others will not work using the colladaLoader
to see if the object will be rendered with color in threeJs with
colladaLoader just look at object in Blender with object mode
(solid) - if it's gray and not colored of the color you choose, it
will be rendered in threeJs the same way
if you apply the 'solidify' modifier to the object and then on threeJs set it to transparent, it will be rendered as wireframed
if you append multiple objects in the scene and 'join' them, the
respective positions and rotations will be respected in threeJs,
otherwise not: for example, if you want to renderize a flower in the
bottle (and thoose objects are different blender files which are
appended/linked in the scene), the flower will not fit in the bottle
in threeJs, but would have a different position and rotation than
the bottle
grouping the objects will not solve this: to see the scene as you see it in Blender you have to 'join' the objects (with the consequences that this entails) or manually change position and rotation on threeJs
the .dae export options don't matter for the rendering of the object in threeJs
and now, the part that regards threeJs:
be sure to import the colladaLoader with:
<script src="jsLib/ColladaLoader.js"></script>
insert this code into your init() function so the loader will load your .dae model:
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load( 'model.dae', function ( collada ) {
// with this you can get the objects of the scene; the [0] is not the first object
// you display in blender in case of many objects (which means you didn't join them)
var obj1 = collada.scene.children[0];
// you can name the object so you can use it even out of the function, if you want
// animate it for example obj1.name = "daeObj1";
// you can set here some material properties as trasparency
obj1.material.needsUpdate = true;
obj1.material.transparent = true;
obj1.material.opacity = 0.5;
obj1.hearth.material.wireframe = false;
// and now some position and rotation for good visualization
obj1.position.set(0, -5, -0.6); //x,z,y
obj1.rotation.set(0, 45, 0);
// and add the obj to the threeJs scene
scene.add(obj1);
});
and some code to the animate() function if you want to update some of your objects, with rotation for example
scene.traverse (function (object) {
if (object.name === 'daeObj1') {
object.rotation.z -= 0.01;
}
});
I hope someone will benefit from this post

Stretch image texture to fit mesh face in Three.js

I'm trying to make the following in three.js:
I made the model in sketchup with some simple coloured textures and used the collader importer, the result looks like this:
Now I want to dynamically load some photographs onto each of the different planes, however what I end up with is this:
So as you can see, each image is loaded but they are very small and repeated across the rest of the surface.
This is how I load the textures: (preloadTexture() is just a simple preloader)
for(i in cubeSidesArray)
{
preloadTexture(modelThumbsArray[i]);
var newTexture = new THREE.MeshPhongMaterial( { map: THREE.ImageUtils.loadTexture(modelThumbsArray[i]) } );
cubeSidesArray[i].material = newTexture;
}
How do I get the textures to fill the surface?
Thanks!
Edit - I played with the model in sketchup and managed to get it a little better, but not much!
Edit 2 - Still no luck, I'm starting to think building it in code from scratch would be simpler
Option 1: I would advise you to do or next.
1 -.Import the model blender
2 -.Export blender to threejs
3 -.Use this method of charging.
AgregarModeloBlender function (geometry, materials) {
console.log(materials);
material = new THREE.MeshFaceMaterial( materials );
modelo3d_ = new THREE.Mesh( geometry,material );
escenario.add(modelo3d_);
modelo3d_.add(camera);
modelo3d_.scale.set(5,5,5);
modelo3d_.position.set(-900,25,850);
modelo3d_.rotation.y=Math.PI;
}
4 -. Subsequently trabajr with textures independently.
Example: http://all.develoteca.com/builder/
Option 2: I would advise you to do or this:
1 -. Create the geometric shape (vertices) to modify each of the faces of the texture.
Example: http://develoteca.com/Panel/
Greetings.

Combine THREE.MeshFaceMaterial and THREE.BackSide?

Using three.js v56:
I have an array of textures called materialArray, then I create
var cubeMaterial = new THREE.MeshFaceMaterial( materialArray );
However, I would like this texture to be reversed; for a "normal" material called simpleMaterial, one would enter:
simpleMaterial.side = THREE.BackSide;
Alas, this has no effect in this case; the material is still displayed on the "front" side.
So, how can the texture be set to the backside when working with an array of textures as in MeshFaceMaterial?
Thanks in advance!
materialArray needs to be an array of materials, not an array of textures.
You must set the side property to THREE.BackSide for each material in the material array that you want flipped.
three.js r.56

Resources