I am trying to add a background to a THREE scene, but I cannot get it to work. I followed the advice given here and here without success.
Here are the lines I added to my (complex) code:
// Load the background texture
var loader = new THREE.TextureLoader();
var texture = loader.load( 'textures/stars_texture2956.jpg' );
var backgroundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2, 0),
new THREE.MeshBasicMaterial({
map: texture
}));
backgroundMesh.material.depthTest = false;
backgroundMesh.material.depthWrite = false;
// Create your background scene
backgroundScene = new THREE.Scene();
backgroundCamera = new THREE.Camera();
backgroundScene.add(backgroundCamera );
backgroundScene.add(backgroundMesh );
and the render function looks like this:
function render() {
renderer.render(backgroundScene, backgroundCamera);
renderer.render( scene, camera );
}
Still I do not see the background (it is still white), but everything else works as expected. Is there a way to fix this?
If you wish to add a static background to your scene then the easiest way is to make the background of your scene transparent and place an image under canvas:
var renderer = new THREE.WebGLRenderer({ alpha: true });
renderer.setClearColor( 0xffffff, 0);
If you wish to create panoramic background which changes when you rotate camera then you need to create a skybox - a big mesh around your scene textured with a set of textures which cover 360 degrees of view. Have a look at this example:
http://threejs.org/examples/#webgl_materials_envmaps
The map is undefined because the TextureLoader Constructor expects a manager, not an url.
// instantiate a loader
var loader = new THREE.TextureLoader();
// load a resource
var texture = loader.load( 'textures/land_ocean_ice_cloud_2048.jpg' );
http://threejs.org/docs/#Reference/Loaders/TextureLoader
To solve your problem with the two scenes, you need to disable autoClear. The second renderer call clears the first one. Set after initializing: renderer.autoClear = false;. Now manually clear in your render-function:
function render() {
renderer.clear(); // <-
renderer.render(backgroundScene, backgroundCamera);
renderer.render( scene, camera );
}
try to coding like this
THREE.ImageUtils.crossOrigin = '';
var img = 'http://bpic.588ku.com/back_pic/03/92/40/4957e29f80d8a4a.jpg!ww650';
var grass = THREE.ImageUtils.loadTexture(img);
new THREE.MeshLambertMaterial({ map: grass });
it works for me
I have found a solution: Instead of creating a different scene and not knowing how to add cameras/whatever so it get rendered correctly, just add the background mesh to the actual scene.
The code then is as follows:
// Load the background texture
var loader = new THREE.TextureLoader();
var texture = loader.load( 'textures/messier-41.jpg' );
var backgroundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(2048, 2048,8,8),
new THREE.MeshBasicMaterial({
map: texture
}));
backgroundMesh.material.depthTest = false;
backgroundMesh.material.depthWrite = false;
which must be put before the code that adds the other parts of the scene.
Related
I have successfully gotten a .obj file to display using three.js from a snippet of code I found online which I adapted for my needs. But am now trying to add the .mtl material file and am getting stuck.
I've tried a few things but nothing seems to be working. I am new to three.js so am definitely misunderstanding something here...
Here is the current code I am using that works beautifully to display my .obj file:
var renderer, scene, camera, Nefertiti;
var ww = window.innerWidth,
wh = window.innerHeight;
function init(){
renderer = new THREE.WebGLRenderer({canvas : document.getElementById('scene')});
renderer.setSize(ww,wh);
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(50,ww/wh, 0.1, 10000 );
camera.position.set(0,0,500);
scene.add(camera);
//Add a light in the scene
directionalLight = new THREE.DirectionalLight( 0xffffff, 0.8 );
directionalLight.position.set( 0, 0, 350 );
directionalLight.lookAt(new THREE.Vector3(0,0,0));
scene.add( directionalLight );
//Load the obj file
loadOBJ();
}
var loadOBJ = function(){
//Manager from ThreeJs to track a loader and its status
var manager = new THREE.LoadingManager();
//Loader for Obj from Three.js
var loader = new THREE.OBJLoader( manager );
//Launch loading of the obj file, addNefertitiInScene is the callback when it's ready
loader.load( '/mypath/Nefertiti-3d.obj', addNefertitiInScene);
};
var addNefertitiInScene = function(object){
Nefertiti = object;
//Move the Nefertiti in the scene
Nefertiti.scale.set(0.7,0.7,0.7);
Nefertiti.rotation.x = 0.5;
Nefertiti.rotation.y = 5.5;
Nefertiti.rotation.z = 0.2;
Nefertiti.position.y = -40;
Nefertiti.position.z = 1;
//Go through all children of the loaded object and search for a Mesh
object.traverse( function ( child ) {
//This allow us to check if the children is an instance of the Mesh constructor
if(child instanceof THREE.Mesh){
child.material.color = new THREE.Color(0XFFFFFF);
//Sometimes there are some vertex normals missing in the .obj files, ThreeJs will compute them
child.geometry.computeVertexNormals();
}
});
//Add the 3D object in the scene
scene.add(Nefertiti);
var canvas = renderer.domElement;
canvas.addEventListener('mousemove', onMouseMove);
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
}
animate();
function onMouseMove(event) {
Nefertiti.rotation.y += event.movementX * 0.001;
Nefertiti.rotation.x += event.movementY * 0.0005;
}
};
init();
Here is a snippet I tried adding in to load the mtl file that didn't work (i've just shortened the actual path to /mypath/ for cleanliness here)
var loadOBJ = function(){
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setBaseUrl( '/mypath/' );
mtlLoader.setPath( '/mypath/' );
var url = "/mypath/Nefertiti-3d.mtl";
mtlLoader.load( url, function( materials ) {
materials.preload();
//Manager from ThreeJs to track a loader and its status
var manager = new THREE.LoadingManager();
//Loader for Obj from Three.js
var loader = new THREE.OBJLoader( manager );
loader.setMaterials( materials );
loader.setPath( '/mypath/' );
//Launch loading of the obj file, addNefertitiInScene is the callback when it's ready
loader.load( '/mypath/Nefertiti-3d.obj', addNefertitiInScene);
object.position.y = - 95;
scene.add( object );
}, onProgress, onError );
};
From researching I can see that there must be a mesh for a material to work, but I just cant figure out from any documentation how I can correctly implement it.
Any assistance in being able to add a mtl file to my code would be so appreciated!!
** EDIT **
So I have changed the snippet of code based on the advice from Mugen87 to the below:
var loadOBJ = function(){
var mtlLoader = new THREE.MTLLoader();
mtlLoader.setPath( '/mypath/' );
var url = "/mypath/Nefertiti-3d.mtl";
mtlLoader.load( url, function( materials ) {
materials.preload();
//Manager from ThreeJs to track a loader and its status
var manager = new THREE.LoadingManager();
//Loader for Obj from Three.js
var loader = new THREE.OBJLoader( manager );
loader.setMaterials( materials );
loader.setPath( '/mypath/' );
//Launch loading of the obj file, addNefertitiInScene is the callback when it's ready
loader.load( '/mypath/Nefertiti-3d.obj', addNefertitiInScene);
}, onProgress, onError );
};
And I have also included the MTLLoader.js from the examples on three.js (I'm not sure if this is right, I'm finding it difficult to find information on this) and am getting the below errors in console:
Uncaught SyntaxError: Cannot use import statement outside a module
(index):136 Uncaught TypeError: THREE.MTLLoader is not a constructor
at loadOBJ ((index):136)
at init ((index):132)
at (index):199
loadOBJ # (index):136
init # (index):132
(anonymous) # (index):199
Any ideas? Is there something wrong with how I have the MTL in the code?
object.position.y = - 95;
scene.add( object );
I suppose you have copied over both lines of code from the official OBJ/MTL example, right? Unfortunately, they don't make sense here since object is undefined. Keep in mind that addNefertitiInScene is your onLoad() callback which is responsible for adding the loaded object to the scene.
Besides, mtlLoader.setBaseUrl( '/mypath/' ); should not be necessary. The method was removed quite a while ago.
Also ensure with the browser's dev tools that the MTL file is actually loaded.
three.js R109
I try to add an img on one object to change his aspect. But all my test result by white object...
The purpose is juste to apply an image on object (all face). He my test code :
var obj = scene.getObjectByName('wall_20_118')
var texture = new THREE.ImageLoader().load( 'core/img/3d/shutter.jpg' );
material = new THREE.MeshBasicMaterial({map: texture,side:THREE.DoubleSide });
obj.material = material
obj.material.map.needsUpdate = true
The object turn to white but I can see my image.
How can I do that ?
Thank in advance
You should be using the THREE.TextureLoader and it's asynchronous, which means that the image is loaded and a callback function will be invoked once the loading finished and the image can be used as a texture on the object.
You would have to change your code like this:
var obj = scene.getObjectByName('wall_20_118');
new THREE.TextureLoader().load( 'core/img/3d/shutter.jpg', function onLoad(texture) {
var material = new THREE.MeshBasicMaterial({
map: texture,
side: THREE.DoubleSide
});
obj.material = material
} );
Can i show a svg in a three.js scene using the WebGL renderer? I know that this can be done using the SVG renderer and loader but I can't use it for my problem.
Thanks.
You can use the Sprite object and specify your svg file in the TextureLoader(), like so:
var map = new THREE.TextureLoader().load( "sprite.svg" );
var material = new THREE.SpriteMaterial( { map: map, color: 0xffffff, fog: true } );
var sprite = new THREE.Sprite( material );
scene.add( sprite );
You can read more here: Three.js - Sprite
While I was working with THREE.js library, I got that kind of notification:
THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter
should be set to THREE.NearestFilter or THREE.LinearFilter.
So, I tried to fix it. It works, when I put the filter:
var groundGeometry = new THREE.PlaneBufferGeometry( 5000, 5000, 96, 96 );
var grassTexture = THREE.ImageUtils.loadTexture("images/grass.jpg");
grassTexture.minFilter = THREE.LinearFilter;//here is the filter
var groundMaterial = new THREE.MeshBasicMaterial( {map: grassTexture, side: THREE.DoubleSide} );
var groundPlane = new THREE.Mesh(groundGeometry, groundMaterial);
scene.add( groundPlane );
However, I tried another way, but at the end I got just the black plane:
var groundGeometry = new THREE.PlaneBufferGeometry( 5000, 5000, 96, 96 );
var groundMaterial = new THREE.MeshBasicMaterial( {map: THREE.ImageUtils.loadTexture({image: "images/grass.jpg", minFilter: THREE.LinearFilter}), side: THREE.DoubleSide} );
var groundPlane = new THREE.Mesh(groundGeometry, groundMaterial);
scene.add( groundPlane );
You are getting a black plane in the second case because you are not passing the proper arguments to THREE.ImageUtils.loadTexture(). You are passing an object {}. You would have to do something like this:
var groundMaterial = new THREE.MeshBasicMaterial( {
map: THREE.ImageUtils.loadTexture( "images/grass.jpg" ),
side: THREE.DoubleSide
} );
groundMaterial.map.minFilter = THREE.LinearFilter;
It is true that the texture loading is asynchronous, but without knowing the rest of your code, it cannot be determined if that is an issue here.
BTW, you likely do not need 18,000 faces in your PlaneBufferGeometry. Try
groundGeometry = new THREE.PlaneBufferGeometry( 5000, 5000, 1, 1 );
three.js r.71
The texture is not downloaded and ready for use when you think it is.
In the first case the second statement requests an asynchronous loading of the texture. Two statements later the texture is assigned to a variable. It works just by luck.
In the second case you download and assign a texture all in one statement. Hardly enough time for the texture to be downloaded.
The proper way: (taken from http://threejs.org/docs/#Reference/Loaders/ImageLoader)
var groundMaterial;
...
// instantiate a loader
var loader = new THREE.ImageLoader();
// load a image resource
loader.load(
// resource URL
'images/grass.jpg',
// Function when resource is loaded
function ( image ) {
// here you are guaranteed that the texture has downloaded so you can do something with it.
groundMaterial = new THREE.MeshBasicMaterial( {map: image, side: THREE.DoubleSide} );
} );
i'm using three.js revision 53
when loading a texture in Canvas Renderer (IE on Win7) and adding a callback for the onLoad event, the texture is not getting displayed. When i remove the callback function, the texture is getting displayed as expected:
// NOT WORKING with callback added
var material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('text_build.png', {}, function()
{
//do something
})
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(135, 135), material);
plane .overdraw = true;
scene.add(plane );
// WORKING without callback
var material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture('text_build.png')
});
var plane = new THREE.Mesh(new THREE.PlaneGeometry(135, 135), material);
plane .overdraw = true;
scene.add(plane );
When running the same code in WebGL Renderer (FF,Chrome on WIn7), both examples work just fine.
Maybe someone can point me to the mistake i'm obviously doing here.
Thanks a lot.
Try this:
var material = new THREE.MeshBasicMaterial({
map: THREE.ImageUtils.loadTexture( 'text_build.png', new THREE.UVMapping(), function() { ... } )
});