How to multiple textures material use in Three.js?.Here three textures using and one 3D sofa.obj format file. I tried a lot of time. Below my code. What do I mistake in my code?
var loader = new THREE.OBJLoader();
var textureLoader = new THREE.TextureLoader();
threeDTexture = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/paradis_beige.jpg' );
threeDTexture2 = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/1.jpg' );
threeDTexture3 = new THREE.ImageUtils.loadTexture( 'models/Sofa/Texturses/2.jpg' );
loader.load('models/Sofa/sofa.obj', function (object) {
var geo = object.children[0].geometry;
var mats = [threeDTexture, threeDTexture2,threeDTexture3];
objct = new THREE.Mesh(geo, mats);
object.traverse(function (child) {
if (child instanceof THREE.Mesh) {
child.material = objct;
}
});
object.position.x = posX;
object.position.y = 0;
object.position.z = posZ;
var size = new THREE.Box3().setFromObject(object).getSize();
object.scale.set(width/size.x, height/size.y, depth/size.z);
scene1.add(object);
console.log(object);
console.log(size);
console.log(width/size.x, height/size.y, depth/size.z);
},
function ( xhr ) {
returnValue = ( xhr.loaded / xhr.total * 100 ) + '% loaded';
console.log(returnValue);
},
function ( error ) {
console.log( 'An error happened' );
}
);
break
first, the textureLoader declared on line-2 is not in use;
second, belowing mats is not an Material Array, you should new MeshBasicMaterial(...) to wrap a texture, see the document here.
var mats = [threeDTexture, threeDTexture2,threeDTexture3];
objct = new THREE.Mesh(geo, mats); // Mesh will not accept this mats param!
Related
I am new to three.js, and i am trying to use raycasting, i am quite confused with how raycasting would work on imported 3d models. Here i imported an obj model, and when i try to detect whether i touch with the imported 3D model, the funciton is not working as i expect. But when i change it to detect a box object i create with three.js, is working as i expected, can someone help with this problem?
import * as THREE from 'https://unpkg.com/three#0.126.1/build/three.module.js';
import { OrbitControls } from 'https://unpkg.com/three#0.126.1/examples/jsm/controls/OrbitControls.js';
import {OBJLoader} from "https://unpkg.com/three#0.126.1/examples/jsm/loaders/OBJLoader.js";
import {MTLLoader} from "https://unpkg.com/three#0.126.1/examples/jsm/loaders/MTLLoader.js";
import * as GUI from "https://unpkg.com/dat.gui#0.7.7/build/dat.gui.module.js";
//Set Up Enviroment for 3D model
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);
const scene = new THREE.Scene();
//Set up Lighting
var keyLight = new THREE.DirectionalLight(new THREE.Color('hsl(30,100%,75%)'),1.0);
var fillLight = new THREE.DirectionalLight(new THREE.Color('hsl(240,100%,75%)'),0.75);
var backLight = new THREE.DirectionalLight(0xffffff,1.0);
keyLight.position.set(-100,0,100);
fillLight.position.set(100,0,100);
backLight.position.set(100,0,-100).normalize();
scene.add(keyLight);
scene.add(fillLight);
scene.add(backLight);
//Set up Camera
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth/window.innerHeight,
0.1,
1000
);
const orbit = new OrbitControls(camera,renderer.domElement);
camera.position.set(0,2,7.5);
orbit.update();
//Set up base
const planeGeometry = new THREE.PlaneGeometry(100,100);
const planeMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
wireframe: false,
side: THREE.DoubleSide
});
var plane = new THREE.Mesh(planeGeometry,planeMaterial);
scene.add(plane);
plane.rotation.x = -0.5 * Math.PI;
//Model for testing
const boxGeometry = new THREE.BoxGeometry(10,10,10);
const boxMaterial = new THREE.MeshBasicMaterial({color: 0x0000ff})
var box = new THREE.Mesh(boxGeometry,boxMaterial);
scene.add(box);
box.position.x += 20;
//Set up GUI for controlling base
const gridHelper = new THREE.GridHelper(100);
scene.add(gridHelper);
const gui = new GUI.GUI();
const options = {
planeColor: '#ffea00',
wireframe: false
};
gui.addColor(options,'planeColor').onChange(function(e){
plane.material.color.set(e);
});
gui.add(options,'wireframe').onChange(function(e){
plane.material.wireframe = e;
});
//Set up Background image
const textureLoader = new THREE.TextureLoader();
textureLoader.load(
'../img/doge.jpg',
function ( texture ) {
scene.background = texture;
},
undefined,
function ( err ) {
console.error( 'An error happened.' );
}
);
//Import obj and mtl file to build 3D model
var building;
const mtlLoader = new MTLLoader();
mtlLoader.load(
'../source/building_04.mtl',
(materials)=>{
materials.preload();
console.log(materials);
const objLoader = new OBJLoader()
objLoader.setMaterials(materials)
objLoader.load(
'../source/building_04.obj',
(object)=>{
scene.add(object)
building = object;
object.position.y +=1;
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log("Object error")
}
)
},
(xhr) => {
console.log((xhr.loaded / xhr.total) * 100 + '% loaded')
},
(error) => {
console.log("Material Eror")
}
)
//Set up raycasting environment
const raycaster = new THREE.Raycaster();
const mousePosition = new THREE.Vector2();
window.addEventListener('mousemove', function(e){
mousePosition.x = (e.clientX/this.window.innerWidth)*2 - 1;
mousePosition.y = -(e.clientY/this.window.innerHeight)*2 + 1;
});
//Function making sure everything to be up to date
function animate(){
//Raycasting
raycaster.setFromCamera( mousePosition, camera );
const intersects = raycaster.intersectObjects( scene.children,true);
if(intersects.length > 0){
for ( let i = 0; i < intersects.length; i ++ ) {
if(building != undefined){
if(intersects[0].object.id === building.id){
console.log("Touched!");
}
else{
console.log("did not touch!");
}
}
else{
console.log("Not reeady!");
}
console.log(intersects[i].object.id);
}
}
else{
console.log("did not touched")
}
console.log("finished")
renderer.render(scene,camera);
}
renderer.setAnimationLoop(animate);
//Make the screen to resize following the browser size
window.addEventListener('resize',function(){
camera.aspect = window.innerWidth/this.window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth,window.innerHeight);
});
The problem is that the object you get from OBJLoader and assign to your building variable is an instance of THREE.Group. Groups do not have an own geometry or material. They are more or less containers holding other 3D objects (like meshes, point clouds, lines or other groups) and representing them as a logical group.
That means they are never the result of a raycasting evaluation which means if(intersects[0].object.id === building.id) always fails.
I suggest you do this after the loading process which tags your objects:
building.traverse( function( object ) {
object.userData.tag = 'building';
} );
You can then use the tag property in your raycasting routine for detecting the building.
I plan to add equirectangular (or spherical) panorama support on top of existing 6 cube panorama. I found this library that easily does it by converting equirectangular image to 6 cubes:
https://github.com/spite/THREE.EquirectangularToCubemap
where we can simply use equiToCube.convert( texture, 1024 ):
renderer = new THREE.WebGLRenderer();
var loader = new THREE.TextureLoader();
loader.load( 'equirectangular-panorama.jpg', function( res ) {
var equiToCube = new EquirectangularToCubemap( renderer );
mesh = new THREE.Mesh(
new THREE.TorusKnotGeometry( 10, 3, 100, 32 ),
new THREE.MeshBasicMaterial( { envMap: equiToCube.convert( res, 1024 ) } )
);
scene.add( mesh );
} );
Seems simple. However, my 6 cube method to load images looks like this:
function getTextureCube(path, step, total){
var urls = ["1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","6.jpg"];
var loader = new THREE.CubeTextureLoader();
var maxAnisotropy = renderer.getMaxAnisotropy();
var textureCube = loader.load(urls, function(texture){
texture.minFilter = THREE.NearestFilter;
texture.anisotropy = maxAnisotropy;
texture.wrapS = THREE.RepeatWrapping;
texture.repeat.x = -1;
}, function ( xhr ) {
log( (xhr.loaded / xhr.total * 100) + '% loaded' );
}, function ( xhr ) {
log( 'error loading texture', xhr );
});
return textureCube;
}
And I cannot successfully utilize equiToCube.convert( texture, 1024 ) for conversion which results in black screen instead of panorama.
function getEquirectangularTextureCube(path, step, total){
var loader = new THREE.TextureLoader(); // Note TextureLoader
var maxAnisotropy = renderer.getMaxAnisotropy();
var equiToCube = new EquirectangularToCubemap( renderer );
var textureCube = loader.load(path, function(texture){
texture.minFilter = THREE.NearestFilter;
texture.anisotropy = maxAnisotropy;
texture.wrapS = THREE.RepeatWrapping;
texture.repeat.x = -1;
}, function ( xhr ) {
log( (xhr.loaded / xhr.total * 100) + '% loaded' );
}, function ( xhr ) {
log( 'error loading texture', xhr );
});
return equiToCube.convert( textureCube, 2048 ); // Note .convert
}
A few thoughts to help: this returned texture is used in fragment shader and uniforms if that helps with answer. Also, I may be using equiToCube incorrectly - I did using it inside loader.load but unsuccessfully.
Any ideas how to integrate equirectangular (or spherical) panorama support will be appreciated.
I have collada model (.dae) displayed by this code, and I add THREE.DoubleSide but it dose not work how can i fix this?
var loadingManager = new THREE.LoadingManager( function() {
scene.add( car );
} );
var loader = new THREE.ColladaLoader( loadingManager );
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load('./model/car_Red.jpg');
loader.options.convertUpAxis = true;
loader.load( './car.dae', function ( collada ) {
car = collada.scene;
car.traverse(function (node) {
if (node.isMesh)
{
node.material.map = texture;
node.material.side = THREE.DoubleSide;
}
});
console.log(car);
I imported the 3D model that contains .obj .mtl and bunch of jpeg and pngs
trying to load the model with /images like this
But, I'm getting is only a black model like his
I wonder what I have missed as I followed the guidelines for using the two loaders.
here is my code.
//loader
var MTTLoader = new THREE.MTLLoader();
MTTLoader.setPath( '/assets/HotAirBalloonIridesium/' );
MTTLoader.load('Air_Balloon.mtl',(materials) => {
console.log(materials);
materials.preload()
var objLoader = new THREE.OBJLoader();
objLoader.load('/assets/HotAirBalloonIridesium/Air_Balloon.obj', (object) => {
console.log(materials)
objLoader.setMaterials(materials)
scene.add(object);
})
})
I wonder what i'm missing as my asset folder contains all the model files
try loading obj using
var loader = new THREE.OBJLoader( manager );
loader.load( 's_v1.obj', function ( object ) {
object.traverse( function ( child ) {
if ( child instanceof THREE.Mesh )
{
// child.material.map = texture2;
// child.material.specularMap=texture;
// child.material.map=texture;
}
} );
// object.position.x = - 60;
// object.rotation.x = 0; //20* Math.PI / 180;
// object.rotation.z = 0;//20* Math.PI / 180;
object.scale.x = 80;
object.scale.y = 80;
object.scale.z = 80;
obj = object
scene.add( obj );
animate(obj);
} );
okay quick update, there was nothing wrong with the loader but I was using the wrong lighting as Phong Material needed
var hemiLight = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.50);
var dirLight = new THREE.DirectionalLight(0xffffff, 0.50);
to be apparent.
You must call "setMaterials" before load obj.
//loader
var MTTLoader = new THREE.MTLLoader();
MTTLoader.setPath( '/assets/HotAirBalloonIridesium/' );
MTTLoader.load('Air_Balloon.mtl',(materials) => {
console.log(materials);
materials.preload()
var objLoader = new THREE.OBJLoader();
objLoader.setMaterials(materials); // "setMaterials" must before "load"
objLoader.load('/assets/HotAirBalloonIridesium/Air_Balloon.obj', (object) => {
console.log(materials)
scene.add(object);
})
})
I'm trying to update the image asset on a geometry after user interaction, basically loading low res assets to begin with and swapping to high res assets when the user interacts with it.
If I just use standard code like below:
var materials = [
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/0.jpg' ) }),
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/1.jpg' ) }),
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/2.jpg' ) }),
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/3.jpg' ) }),
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/4.jpg' ) }),
new THREE.MeshPhongMaterial({ map:loader.load('assets/object/5.jpg' ) }),
];
target.material.map.needsUpdate = true;
var texture = new THREE.MeshFaceMaterial( materials );
target.material = texture;
The low res assets are cleared from the geometry showing the base colour, the high-res assets take a second to load and then appear on the geometry.
I need to load the assets and then swap them, and from some reading it seems like THREE.LoadingManager() is the way to go, however I'm a bit lost.
So far this is what I have but it's throwing errors....
var textureManager = new THREE.LoadingManager();
textureManager.onProgress = function ( item, loaded, total ) {
console.log(item+' = '+loaded / total * 100) + '%';
};
textureManager.onLoad = function () {
console.log(' loading complete');
var materials = [
new THREE.MeshPhongMaterial({ map:myTextureArray[0] }),
new THREE.MeshPhongMaterial({ map:myTextureArray[1] }),
new THREE.MeshPhongMaterial({ map:myTextureArray[2] }),
new THREE.MeshPhongMaterial({ map:myTextureArray[3] }),
new THREE.MeshPhongMaterial({ map:myTextureArray[4] }),
new THREE.MeshPhongMaterial({ map:myTextureArray[5] })
];
target.children[i].material.materials.map.needsUpdate = true;
var texture = new THREE.MeshFaceMaterial( materials );
target.children[i].material = texture;
};
var textureLoader = new THREE.ImageLoader( textureManager );
myTextureArray = [];
var myTexture = new THREE.Texture();
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/0.jpg', function ( image ) { myTexture.image = image; } ) );
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/1.jpg', function ( image ) { myTexture.image = image; } ) );
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/2.jpg', function ( image ) { myTexture.image = image; } ) );
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/3.jpg', function ( image ) { myTexture.image = image; } ) );
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/4.jpg', function ( image ) { myTexture.image = image; } ) );
myTextureArray.push( textureLoader.load( 'assets/objtect1/hires/5.jpg', function ( image ) { myTexture.image = image; } ) );
Any ideas where I might be going wrong?
For the benefit of anyone else, the problem was that I was using the ImageLoader, not TextureLoader.
Replace:
var textureLoader = new THREE.ImageLoader( textureManager );
With
var textureLoader = new THREE.TextureLoader( textureManager );