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.
Related
Hello guys!
Been doing some experiments with three.js, managed to get a 70mb model down to 10 MB using draco, however I am not really familiar with Draco and I haven't been able to find a good example on how to use Draco loader together with GLTF Loader, can anyone show me a good example or check my code to see which things I have to change to use it and load the compressed model? TYSM!
import * as THREE from '../build/three.module.js';
import Stats from './jsm/libs/stats.module.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { GLTFLoader } from './jsm/loaders/GLTFLoader.js';
let camera, scene, renderer, stats;
var raycaster, mouse = { x : 0, y : 0 };
var objects = [];
var selectedObject;
init();
animate();
function init() {
const container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.25, 20 );
camera.position.set( - 1.8, 0.6, 2.7 );
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xa0a0a0 );
raycaster = new THREE.Raycaster();
const hemiLight = new THREE.HemisphereLight( 0xa0a0a0, 0xa0a0a0, 2);
scene.add(hemiLight);
// model
const loader = new GLTFLoader().setPath( 'models/fbx/lowlight3_out/' );
loader.load( 'lowlight3.gltf', function ( gltf ) {
gltf.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
} );
scene.add( gltf.scene );
} );
Guys just managed to get it working, just add to add Draco by following the three.js default example:
Add
import { DRACOLoader } from './jsm/loaders/DRACOLoader.js';
&
const loader = new GLTFLoader().setPath( 'models/fbx/compressed/' );
const dracoLoader = new DRACOLoader();
dracoLoader.setDecoderPath( './js/libs/draco/' );
loader.setDRACOLoader( dracoLoader );
For anyone having the same issue, just take a look to this example:
https://threejs.org/docs/#examples/en/loaders/GLTFLoader
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);
}
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 );
}
}
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.
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! :)