I'm pretty new to three.js and I tried for hours to create a skybox/skydome for a better visual feeling to my world (in this case space). Googled, checked tutorials, asked here on StackOverflow. And nothing worked, or I got a silly and dumb answer here on SO. Question is simple: how to make a skybox/dome?
This is how you do a skydome in threejs.
var skyGeo = new THREE.SphereGeometry(100000, 25, 25);
First the geometry. I wanted it big and made it big
var loader = new THREE.TextureLoader(),
texture = loader.load( "images/space.jpg" );
Loads the texture of your background space. One thing here is that you need it to run through a server to be able to load the texture. I use wamp or brackets preview.
Create the material for the skybox here
var material = new THREE.MeshPhongMaterial({
map: texture,
});
Set everything together and add it to the scene here.
var sky = new THREE.Mesh(skyGeo, material);
sky.material.side = THREE.BackSide;
scene.add(sky);
This might not be the best solution for this, but it´s easy specially for a beginner in threejs. Easy to understand and create.
This is how you can load image as texture and apply that on innerside of a sphere geometry to emulate skydome.
Complete solution with error callback for future reference
//SKY
var loader = new THREE.TextureLoader();
loader.load(
"./assets/universe.png",
this.onLoad,
this.onProgress,
this.onError
);
onLoad = texture => {
var objGeometry = new THREE.SphereBufferGeometry(30, 60, 60);
var objMaterial = new THREE.MeshPhongMaterial({
map: texture,
shading: THREE.FlatShading
});
objMaterial.side = THREE.BackSide;
this.earthMesh = new THREE.Mesh(objGeometry, objMaterial);
scene.add(this.earthMesh);
//start animation
this.start();
};
onProgress = xhr => {
console.log((xhr.loaded / xhr.total) * 100 + "% loaded");
};
// Function called when download errors
onError = error => {
console.log("An error happened" + error);
};
Related
I have a model, a background sky and a ground surface. Texturing the surface results in no surface.
I've tried the basic approach and come to the conclusion that it is probably that the scene is being rendered before the texture has finished loading. Having searched and found various possible solutions, I have tried several of them, without really understanding how they are supposed to work. None of them has worked. One problem is that it is an old problem and most of the suggestions involve outdated versions of the three.js library.
// Ground
// create a textured Ground based on an answer in Stackoverflow.
var loader = new THREE.TextureLoader();
loader.load('Textures/Ground128.jpg',
function (texture) {
var groundGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
const groundMaterial = new THREE.MeshLambertMaterial({map: texture});
var ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.receiveShadow = true; //Illumination addition
ground.rotation.x = -0.5 * Math.PI; // rotate into the horizontal.
scene.add(ground);
}
);
// This variation does not work either
http://lhodges.users37.interdns.co.uk/me/downloads/Aphaia/Temple.htm
http://lhodges.users37.interdns.co.uk/me/downloads/Aphaia/Temple7jsV0.15b.htm
The first of the above is the complete page in which the ground is a plain billiard table green. The second is the page containing the above code.
There appear to be no error (Last time I tried.)
By the time your texture loads and you add the ground, your scene has already rendered (and there is no other render call).
You need to call renderer.render(scene, camera); after adding the ground to the scene.
// Ground
// create a textured Ground based on an answer in Stackoverflow.
var loader = new THREE.TextureLoader();
loader.load('Textures/Ground128.jpg',
function (texture) {
var groundGeometry = new THREE.PlaneBufferGeometry(2000, 2000, 100, 100);
const groundMaterial = new THREE.MeshLambertMaterial({map: texture});
var ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.receiveShadow = true; //Illumination addition
ground.rotation.x = -0.5 * Math.PI; // rotate into the horizontal.
scene.add(ground);
renderer.render(scene, camera); // <--- add this line
}
);
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);
I know there is the skinning_blending model but as it has the gui control (the dat.gui as for all other threejs examples) so the code became too complexe and i couldn't get the simple way like which i found for a previous older threejs version like this one :
var loader = new THREE.JSONLoader();
loader.load('../assets/models/simpleManMesh2.json', function (model, mat) {
var mat = new THREE.MeshLambertMaterial({color: 0xF0C8C9, skinning: true});
mesh = new THREE.SkinnedMesh(model, mat);
// register the animation
THREE.AnimationHandler.add(model.animation);
var animation = new THREE.Animation(mesh, "saluuut");
scene.add(mesh);
// start the animation
animation.play();
}, '../assets/models');
for which and same as many other people i got the error :
THREE.AnimationHandler.add() has been deprecated.
So what i ask for is a same simple code (without any gui control) but which works in the lastest version. Best regards.
It just now is THREE.Animation() :
loader.load('yourfile.json',function(geometry,materials){
for(var i in materials)materials[i].skinning=true;
mesh=new THREE.SkinnedMesh(
geometry,
new THREE.MeshFaceMaterial(materials)
);
scene.add(mesh);
animation=new THREE.Animation(mesh,geometry.animations[0]);
animation.play();
});
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);
I have a house model in my game, and I have some materials for the house geometry. There is a material for the wall of the house, and I have a texture-map-image to show the bricks.
var mat = new THREE.MeshPhongMaterial( {
ambient: 0x969696,
map: THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' ),
overdraw: true,combine: THREE.MultiplyOperation
} );
In this way above, the texture map appears like GL_CLAMP I want it to show like GL_REPEAT.
What should I do?
If you can not see the images check this.
I have posted a full working example at:
http://stemkoski.github.com/Three.js/Texture-Repeat.html
The relevant part of the code example is:
// for example, texture repeated twice in each direction
var lavaTexture = THREE.ImageUtils.loadTexture( 'images/lava.jpg' );
lavaTexture.wrapS = lavaTexture.wrapT = THREE.RepeatWrapping;
lavaTexture.repeat.set( 2, 2 );
var lavaMaterial = new THREE.MeshBasicMaterial( { map: lavaTexture } );
var lavaBall = new THREE.Mesh( THREE.GeometryUtils.clone(sphereGeom), lavaMaterial );
scene.add( lavaBall );
It's called THREE.RepeatWrapping there. The loadTexture defaults to THREE.ClampToEdgeWrapping (see ctor function from previous link). Don't know if you can use the callback (because this in JS is a bit weird (looks like it points to the created Image, not the created Texture)). Signature:
loadTexture: function ( path, mapping, callback ) {
Better you just name the texture locally and set the wrap modes manually:
var t = THREE.ImageUtils.loadTexture( 'textures/G/G0.jpg' );
t.wrapS = t.wrapT = THREE.RepeatWrapping;
Looks like you're not going far with threejs without looking at the actual code...
Image must be 8x8, 16x16, 32x32, 128x128, 256x256, 512x512 etc.
And all be working.
=)