I am trying to include a skybox to a scene in a web page, so I followed a tutorial I found (https://dev.to/codypearce/how-to-create-a-skybox-with-three-js-2bn8), everything seems to work correctly as my console (on firefox) doesn't display any errors or warning.
First the code creates an array of texture, then assembles the textures in a box and finally adds the box to the scene. I don't have any light in my scene as every objects are visible without any so far (and when I try to add one it doesn't change anything, and the console doesn't show any error either).
I thought it could be a matter of distance of the camera to the skybox so I got the skybox closer to the camera but still nothing.
I put my code down here if you want to see what I did so far. Thanks in advance for your help!
var camera, scene, rendu;
var r, t;
init();
function init() {
r = 3;
t = 1.1;
// ---------- scene et camera --------- //
camera = new THREE.PerspectiveCamera( 70 , window.innerWidth / window.innerHeight , 0.01 , 2000 );
camera.position.set( 0 , 0 , 4 );
camera.lookAt(new THREE.Vector3( 0, 0, 0 ));
scene = new THREE.Scene();
loadSkybox();
// ---------- rendu ------------- //
rendu = new THREE.WebGLRenderer( { antialias: true} );
rendu.setSize( window.innerWidth, window.innerHeight );
rendu.setPixelRatio(window.devicePixelRatio);
rendu.setAnimationLoop( animation );
document.body.appendChild( rendu.domElement );
}
function animation() {
rendu.render( scene, camera );
}
function createPathStrings(filename) {
const basePath = "./ulukai/";
const baseFilename = basePath + filename;
const fileType = ".png";
const sides = ["ft", "bk", "up", "dn", "rt", "lf"];
const pathStrings = sides.map(side => {
return baseFilename + "_" + side + fileType;
});
return pathStrings;
}
function createMaterialArray(filename) {
const skyboxImagepaths = createPathStrings(filename);
const materialArray = skyboxImagepaths.map(image => {
let texture = new THREE.TextureLoader().load(image);
return new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide });
});
return materialArray;
}
function loadSkybox() {
// ----------- skybox -------------- //
var skyboxImage = "corona";
skyboxGeo = new THREE.BoxGeometry(1000, 1000, 1000);
skybox = new THREE.Mesh(skyboxGeo, createMaterialArray(skyboxImage));
scene.add(skybox);
}
Related
i am working on an the application to optimize packs in a truck.
i use three.js to show the 3D results.
below is the code i use.
i want to make it a moving scene , i want the boxes to show one by one move towards there position inside the truck
curent 3D result
i don't know how to do that, any idea please ?
var camera, controls, scene, renderer;
init();
//render(); // remove when using next line for animation loop (requestAnimationFrame)
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xcccccc );
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 400, 200, 0 );
// controls
controls = new THREE.MapControls( camera, renderer.domElement );
//controls.addEventListener( 'change', render ); // call this only in static scenes (i.e., if there is no animation loop)
controls.enableDamping = true; // an animation loop is required when either damping or auto-rotation are enabled
controls.dampingFactor = 0.25;
controls.screenSpacePanning = false;
controls.minDistance = 1;
controls.maxDistance = 50;
controls.maxPolarAngle = Math.PI / 2;
// world
var geometry = new THREE.BoxBufferGeometry( 1, 1, 1 );
geometry.translate( 0, 0.5, 0 );
console.log("this is the list we get " , result ) ;
for (Element in result)
{
if( result[Element].Object === "Box" || result[Element].Object === "BigBox" )
{
var material = new THREE.MeshPhongMaterial( { opacity: 0.25 , color: getRandomColor() , transparent: false } );
var material2 = new THREE.MeshPhongMaterial( { opacity: 0.25 , color: getRandomColor() , transparent: true } );
var mesh = new THREE.Mesh( geometry, material );
if(result[Element].Object === "BigBox")
{
mesh.opacity = 0.01
mesh = new THREE.Mesh( geometry, material2 );
}
mesh.opacity = 0.75
mesh.position.x = result[Element].CenterPoint.X ;
mesh.position.y = result[Element].CenterPoint.Y;
mesh.position.z = result[Element].CenterPoint.Z;
mesh.scale.x = result[Element].Dimentions.X;
mesh.scale.y = result[Element].Dimentions.Y;
mesh.scale.z = result[Element].Dimentions.Z;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
}
}
// lights
var light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
var light = new THREE.DirectionalLight( 0x002288 );
light.position.set( - 1, - 1, - 1 );
scene.add( light );
var light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function animate() {
requestAnimationFrame( animate );
controls.update(); // only required if controls.enableDamping = true, or if controls.autoRotate = true
render();
}
function render() {
renderer.render( scene, camera );
}
var link = document.createElement( 'a' );
link.style.display = 'none';
document.body.appendChild( link ); // Firefox workaround, see #6594
function save( blob, filename ) {
link.href = URL.createObjectURL( blob );
link.download = filename;
link.click();
// URL.revokeObjectURL( url ); breaks Firefox...
}
function saveString( text, filename ) {
save( new Blob( [ text ], { type: 'text/plain' } ), filename );
}
function saveArrayBuffer( buffer, filename ) {
save( new Blob( [ buffer ], { type: 'application/octet-stream' } ), filename );
}
So, if I understand you well, you have two tasks to do:
Move the camera
Show the boxes one by one
MOVE THE CAMERA
Create a global variable:
let previousTime = performance.now();
Then create an update() function and call it in your animate() function:
function update() {
const time = performance.now();
const deltaTime = time - previousTime; // this variable tells us, how much time has passed since the last frame (the last call of update() function)
previousTime = time;
moveCamera(deltaTime);
}
And the moveCamera() function:
function moveCamera(deltaTime) {
// you have to set this variable by your preferences
const SPEED = 10;
// This is the vector that determines the direction in which your camera moves.
// In this case the camera will move only along the X axis.
// Set it by your preferences depending on which way you want the camera to move.
const MoveCameraDirection = new THREE.Vector3(1,0,0);
// You have to multiply it by SPEED and by deltaTime
// Multiplying by deltaTime is to make the movement speed independent of the number of FPS
MoveCameraDirection.multiplyScalar( SPEED * deltaTime );
camera.position.add(MoveCameraDirection);
}
Camera movement is done.
SHOW THE BOXES ONE BY ONE
You need to have a handler to your boxes. So create a variable boxes and place your objects there:
var camera, controls, scene, renderer, boxes = [];
...
// before scene.add( mesh );
mesh.visible = false ; // set all boxes invisible at the begin
boxes.push(mesh);
Now, in your update() function, under moveCamera(deltaTime) you can call showBoxes().
The question is: when to show the boxes? Let's say we want to show the boxes, when the camera passes them.
Let's create this function:
function showBoxes() {
// I don't know which direction is your camera moving, so let's assume it's moving along the X axis (like in previos function). You can use any other axis as well.
// We want to show only the boxes that have **box.position.x < camera.position.x** and are not currenly displayed
const boxesToShow = boxes.filter( box => !box.visible && box.position.x < camera.position.x);
for( let i=0 ; i < boxesToShow.length ; i++ ) {
// we can do it just like this:
// boxesToShow[i].visible = true;
// but we don't want them to show all at once, so let's use a timer and display them one by one in 1 second intervals
setTimeout( () => boxesToShow[i].visible=true, i * 1000 );
}
}
I am trying to use Three js to load in a 3d heart model and attach a picture to the front of the heart but it seems the heart isn’t showing up at all even without loading the image in. I am new at Three js so I might be doing it all wrong but I tried using the code straight from the documents and it still isn’t working. I am getting no errors and I can see AxesHelper also I have loaded in a cube and that works so I don't think there is a problem with my scene.
function handleHeart(img) {
document.getElementById("divRight").innerHTML = ""
let renderer = new THREE.WebGLRenderer();
document.getElementById("divRight").appendChild(renderer.domElement);
let w = document.getElementById("divRight").clientWidth
let h = 600
renderer.setSize( w, h)
let camera = new THREE.PerspectiveCamera(35, w / h, 0.1, 3000 );
const controls = new THREE.OrbitControls( camera, renderer.domElement );
camera.position.set( 0, 0, 10 );
camera.lookAt(new THREE.Vector3(0, 0, 0))
controls.update();
let scene = new THREE.Scene();
scene.background = new THREE.Color( 'grey' );
light = new THREE.AmbientLight(0xffffff);
scene.add(light);
const loader = new THREE.GLTFLoader();
loader.load(
// resource URL
'models/heart_v1.glb',
// called when the resource is loaded
function ( gltf ) {
let material = new THREE.MeshBasicMaterial( { map: img } );
let model = gltf.scene || gltf.scenes[0];
//model.scale.set(1000,1000,1000)
model.material = material
scene.add(model)
model.position.z = -10
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log(error)
console.log( 'An error happened' );
})
scene.add(new THREE.AxesHelper(100))
renderer.render(scene, camera)
}
here is a replit : https://repl.it/#AlecStein44/Threejs-help#javascript.js
model.material = material
This line is incorrect. It should be:
model.traverse( function( object ) {
if ( object.isMesh ) object.material = material;
} );
Notice that applying a texture will still not work since your model heart_v1.glb has no texture coordinates.
Im loading a GLTF model (9mb) into ThreeJS. It's definitely loading slow. It takes about 4-5 seconds to load on my PC and about 11 seconds to load on my IPhone. How can i speed up the rendering times? My PC and IPhone load examples from the ThreeJS website faster than my project. My project has only one object being loaded so I feel like it should load faster than the examples on ThreeJS website.
My example project is located here # http://flowolfsworld.com/
Code
var ourObj;
var ourObj2;
// Instantiate a loader
var loader = new THREE.GLTFLoader();
// Optional: Provide a DRACOLoader instance to decode compressed mesh data
var dracoLoader = new THREE.DRACOLoader();
dracoLoader.setDecoderPath( '/js/draco/' );
loader.setDRACOLoader( dracoLoader );
let scene, camera, renderer, stars, starGeo;
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000)
camera.position.z = 25;
//renderer = new THREE.WebGLRenderer();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor("#000000");
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
starGeo = new THREE.Geometry();
for(let i=0;i<6000;i++) {
star = new THREE.Vector3(
Math.random() * 600 - 300,
Math.random() * 600 - 300,
Math.random() * 600 - 300
);
star.velocity = 0;
star.acceleration = 0.02;
starGeo.vertices.push(star);
}
let sprite = new THREE.TextureLoader().load( 'star.png' );
let starMaterial = new THREE.PointsMaterial({
color: 0xaaaaaa,
size: 0.7,
map: sprite
});
stars = new THREE.Points(starGeo,starMaterial);
scene.add(stars);
// window.addEventListener("resize", onWindowResize, false);
var hemiLight = new THREE.HemisphereLight( 0xffffff, 0x444444 );
hemiLight.position.set( 0, 300, 0 );
scene.add( hemiLight );
var dirLight = new THREE.DirectionalLight( 0xffffff );
dirLight.position.set( 75, 300, -75 );
scene.add( dirLight );
loader.load(
// resource URL
'objs/dracowolf.gltf',
// called when the resource is loaded
function ( gltf ) {
scene.add( gltf.scene );
ourObj = gltf.scene;
animate();
},
// called while loading is progressing
function ( xhr ) {
console.log( ( xhr.loaded / xhr.total * 100 ) + '% loaded' );
},
// called when loading has errors
function ( error ) {
console.log( 'An error happened' );
}
);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
if(ourObj){
ourObj.rotation.y -= .01;
}
starGeo.vertices.forEach(p => {
p.velocity += p.acceleration
p.y -= p.velocity;
if (p.y < -200) {
p.y = 200;
p.velocity = 0;
}
});
starGeo.verticesNeedUpdate = true;
stars.rotation.y +=0.002;
}
init();
A few suggestions on this particular model:
Use .glb, not .gltf. The binary form of glTF will be 25-30% smaller than a .gltf with embedded binary data, and doesn't have to be decoded from a Data URI. Using .gltf with a separate binary .bin is also an option. Use glTF-Pipeline to make these changes.
Preload the Draco decoder by calling dracoLoader.preload() before your model starts loading. On my test of your page, that would save 500ms spent fetching the decoder after the model has already been downloaded.
Consider using https://github.com/zeux/meshoptimizer#installing-gltfpack to simplify the model, or at least to quantize it, and then gzip it. This is an alternative to Draco, and may not compress the file quite as well, but can sometimes decrease overall loading time despite that.
Uncaught TypeError: THREE.PointerLockControls is not a constructor
I can't use firstperson controls for whatever reason, I am really lost for reason with this one. It's got me really stumped.
const THREE = require('THREE');
var FirstPersonControls = require('first-person-controls');
const CANNON = require('cannon');
var keyboard = new THREEx.KeyboardState();
var lights = [];
var camSpeed = 1;
var world, mass, body, body2, shape, shape2, timeStep=1/60,
camera, scene, renderer, geometry, material, mesh, textureCube;
initThree();
initCannon();
animate();
function initCannon() {
world = new CANNON.World();
world.gravity.set(0,-9.8,0);
world.broadphase = new CANNON.NaiveBroadphase();
world.solver.iterations = 10;
shape = new CANNON.Box(new CANNON.Vec3(1,1,1));
shape2 = new CANNON.Box(new CANNON.Vec3(50,1,50));
mass = 1;
body = new CANNON.Body({
mass: 1
});
body2 = new CANNON.Body({
mass: 0
});
body.position.set(1,10,1);
body.addShape(shape);
body2.addShape(shape2);
body.angularDamping = 0.5;
world.addBody(body);
world.addBody(body2);
}
function initThree() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 10000 );
var controls = new THREE.FirstPersonControls(camera);
controls.lookSpeed = 0.1;
controls.movementSpeed = 10;
var clock = new THREE.Clock(true);
var prefix = ".png"
var r = __dirname + "/skyboxes/mp_cliffside/";
var urls = [
r + "px" + prefix, r + "nx" + prefix,
r + "py" + prefix, r + "ny" + prefix,
r + "pz" + prefix, r + "nz" + prefix
];
textureCube = new THREE.CubeTextureLoader().load( urls );
var dottedAlphaMap = new THREE.TextureLoader().load( __dirname + "/textures/brickmap.png" );
var dottedAlphaMap2 = new THREE.TextureLoader().load( __dirname + "/textures/stonemap-wet-texture.jpg" );
scene.background = textureCube;
lights[0] = new THREE.PointLight( '#ffffff', 3, 100 );
lights[0].position.set( 0, 5, 0 );
scene.add( lights[0] );
scene.add( camera );
renderer = new THREE.WebGLRenderer({ alpha:false });
renderer.setSize( window.innerWidth, window.innerHeight );
camera.position.y = 40;
camera.rotation.x = -90 * Math.PI / 180;
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
updatePhysics();
render();
}
var controllee = camera;
function updatePhysics() {
// Step the physics world
world.step(timeStep);
// Copy coordinates from Cannon.js to Three.js
lights[0].position.copy(camera.position)
}
function render() {
requestAnimationFrame(render);
controls.update(clock.getDelta());
if(keyboard.pressed("F")){
camera.fov += 0.1;
camera.updateProjectionMatrix();
}
if(keyboard.pressed("G")){
camera.fov -= 0.1;
camera.updateProjectionMatrix();
}
if(keyboard.pressed("space")){
controllee.translateY(camSpeed/10);
}
if(keyboard.pressed("shift")){
controllee.translateY(-camSpeed/10);
}
if(keyboard.pressed("W")){
controllee.translateZ(-camSpeed/10);
}
if(keyboard.pressed("S")){
controllee.translateZ(camSpeed/10);
}
if(keyboard.pressed("A")){
controllee.translateX(-camSpeed/10);
}
if(keyboard.pressed("D")){
controllee.translateX(camSpeed/10);
}
if(keyboard.pressed("I")){
controllee.rotateX(camSpeed/100);
}
if(keyboard.pressed("K")){
controllee.rotateX(-camSpeed/100);
}
if(keyboard.pressed("J")){
controllee.rotateY(camSpeed/100);
}
if(keyboard.pressed("L")){
controllee.rotateY(-camSpeed/100);
}
if(keyboard.pressed("U")){
controllee.rotateZ(camSpeed/100);
}
if(keyboard.pressed("O")){
controllee.rotateZ(-camSpeed/100);
}
renderer.render( scene, camera );
}
I am using imported three.js and cannon.js, from node package manager.
I am trying to get the controls to be like an fps, but stuff like this keeps getting in my way!
Any help is appreciated, the only thing i can think of is that its not included in the NPM version of three, in which case, I'm SOL
Update: I have changed my code to include three via a tag. Same goes with the PointerLockControls, but now the problem is that I dont know how the heck to lock the pointer.
UNDERSTAND that you have to use "controls.lock()" to effectively lock your mouse to the screen (your pointer will disappear and you will be able to look around like a FPS game).
Unfortunately, you CAN NOT lock the mouse pointer from code. Instead, a user interaction WITH A DOM ELEMENT is required.
The simplest Dom element you can use is the "body", by using document.body.. see:
//add document.body to PointerLockControls constructor
let fpsControls = new PointerLockControls( camera , document.body );
//add event listener to your document.body
document.body.addEventListener( 'click', function () {
//lock mouse on screen
fpsControls.lock();
}, false );
NOTE 1: no need to call fpsControls.update() in animation function;
NOTE 2: make sure your body section is IN FRONT of the canvas and covering the entire screen, setting z-index: -1 on the canvas' CSS if necessary (or setting z-index: 10, in body's CSS ). Example:
body{
z-index: 10
margin: 0;
padding: 0;
height: 100vh;
width: 100vh;
overflow: hidden;
}
This way, you can click anywhere in the screen to experience the expected behavior. ESC will make you unlock the controller
Keep Calm and Happy Coding
but now the problem is that I don't know how the heck to lock the pointer.
You can do it like in the following example:
Create splash screen that says "Click to Play"
Register an click event listener to the respective DOM element
In the listener code call document.body.requestPointerLock() in order to asynchronously ask the browser for the pointer lock
I have just discovered the world of three.js and it's amazing.
I downloaded the examples, and started checking some of them.
I have never been coding in JavaScript, so I was wondering if somebody could help me with editing one of the example files (misc_controls_trackball.html). Instead of generated geometry (mesh.position.x = ( Math.random() - 0.5 ) ...) I was wondering if I could just include an already made mesh (from 3 studio max for example)?
I think this is the part of the code which generates the mesh:
// world
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0xcccccc, 0.002 );
var geometry = new THREE.CylinderGeometry( 0, 10, 30, 4, 1 );
var material = new THREE.MeshLambertMaterial( { color:0xffffff, shading: THREE.FlatShading } );
for ( var i = 0; i < 500; i ++ ) {
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = ( Math.random() - 0.5 ) * 1000;
mesh.position.y = ( Math.random() - 0.5 ) * 1000;
mesh.position.z = ( Math.random() - 0.5 ) * 1000;
mesh.updateMatrix();
mesh.matrixAutoUpdate = false;
scene.add( mesh );
}
In what way should this be changed, so that I could import my external mesh (in form of .3ds, .obj, .dae, does not matter)?
Thank you.
Here is the misc_controls_trackball.html example file along with "js" folder.
Tried this?
http://threejs.org/examples/webgl_loader_collada
It`s an example for Collada, but for the other formats the concept is the same, just using a different loader.
var loader = new THREE.ColladaLoader();
// Depending on how you created your model, you may need to
loader.options.convertUpAxis = true;
// Then load it:
loader.load( './models/collada/monster/monster.dae', function ( collada ) {
// All this will happen asynchronously
dae = collada.scene;
// Before displaying it, you can tweak it as necessary
dae.scale.x = dae.scale.y = dae.scale.z = 0.002;
dae.updateMatrix();
scene.add(dae);
// At the next frame, you`ll have your model loaded.
} );
EDIT, additions
First you need the links to the proper libraries, including the ColladaLoader
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r69/three.js"></script>
<script src="js/loaders/ColladaLoader.js"></script>
Then a number of things needed fixing in the code.
- scene object was missing
- Model loaded, but to be scaled up a bit
- No call to render() in the animate function, so you had no animation.
- The fog statement was broken... Best spending some time on the basics, first...
function init() {
// Create your scene first
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 500;
controls = new THREE.TrackballControls( camera );
controls.rotateSpeed = 1.0;
controls.zoomSpeed = 1.2;
controls.panSpeed = 0.8;
controls.noZoom = false;
controls.noPan = false;
controls.staticMoving = true;
controls.dynamicDampingFactor = 0.3;
controls.keys = [ 65, 83, 68 ];
controls.addEventListener( 'change', render );
// world
var loader = new THREE.ColladaLoader();
// Depending on how you created your model, you may need to
loader.options.convertUpAxis = true;
// Then load it:
//loader.load( './models/collada/monster/monster.dae', function ( collada ) {
loader.load( 'models/monster.dae', function ( collada ) {
// All this will happen asynchronously
dae = collada.scene;
// Give it a decent scale
dae.scale.x = dae.scale.y = dae.scale.z = 1;
dae.updateMatrix();
scene.add(dae);
// At the next frame, you`ll have your model loaded.
} );
// lights
light = new THREE.DirectionalLight( 0xffffff );
light.position.set( 1, 1, 1 );
scene.add( light );
light = new THREE.DirectionalLight( 0x002288 );
light.position.set( -1, -1, -1 );
scene.add( light );
light = new THREE.AmbientLight( 0x222222 );
scene.add( light );
// renderer
renderer = new THREE.WebGLRenderer( { antialias: false } );
//renderer.setClearColor( scene.fog.color, 1 );
renderer.setSize( window.innerWidth, window.innerHeight );
container = document.getElementById( 'container' );
container.appendChild( renderer.domElement );
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
// The following is not necessary at this stage, as you`ll call it
// from animate later down (if you want to do an animation, of course,
// which I guess you do)
render();
}
And the animate function should look like this
function animate() {
requestAnimationFrame( animate );
controls.update();
render();
}
Hope that helps! :)