ThreeJS my GLTF loading speed is too slow on host server? - javascript

I'm currently creating a Website with a 3D Market Place feature in it. Alas, I'm having trouble implementing the main feature of this website.
This is the website (alpha state): https://www.openstring-studios.com/
All the code is in the index.html file there so you can see how it's made, don't mind the mess though, my issue is only with the loading of 3D models into the scene.
There is this website called sketchfab.com and they have this amazing feature of previewing PBR Assets within a WebGL Window. And it loads reasonably fast.
Here's my problem. I want to create a similar feature for my website and got the ThreeJS Library to do it. However, even though everything loads quite quickly on my xampp localhost as soon as I upload it on my host server, the models take forever to load (probably because of the 4k textures).
This is the code I use:
function init() {
camera.position.z = 0.3;
camera.position.y = 0.15;
camera.position.x = 0.4;
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth/sceenDivide, 600 );
renderer.physicallyCorrectLights = true;
renderer.gammaOutput = true;
renderer.gammaFactor = 2.8;
new THREE.RGBELoader().setDataType( THREE.UnsignedByteType )
.setPath( '' ).load( 'white_cliff_top_1k.hdr', function ( texture ) {
var cubeGenerator = new THREE.EquirectangularToCubeGenerator( texture, { resolution: 512 } );
var pmremGenerator = new THREE.PMREMGenerator( cubeGenerator.renderTarget.texture );
var pmremCubeUVPacker = new THREE.PMREMCubeUVPacker( pmremGenerator.cubeLods );
envMap = pmremCubeUVPacker.CubeUVRenderTarget.texture;
cubeGenerator.update( renderer );
pmremGenerator.update( renderer );
pmremCubeUVPacker.update( renderer );
loader = new THREE.GLTFLoader().setPath( '' );
loadModel( 'Promo_ProductSoil.gltf');
loadModel( 'Promo_ProductCover.gltf');
loadModel( 'StraightPot_OrangePaintedIron.gltf');
pmremGenerator.dispose();
pmremCubeUVPacker.dispose();
scene.background = cubeGenerator.renderTarget;
} );
}
function loadModel( src ) {
loader.load( src , function ( gltf ) {
gltf.scene.traverse( function ( child ) {
if ( child.isMesh ) {
child.material.envMap = envMap;
child.position.y -= 0.12
mesh = child;
}
gltfSource = gltf;
} );
scene.add( gltf.scene );
},
function ( xhr ) {
onProgress( xhr )
} );
}
Is there a solution how I can achieve a similar loading speed as seen on the website sketchfab.com because at the moment my simple pot model takes over a minute before it is displayed.
The only reason why the planes load so fast is that their material has only a low-resolution BaseColor and nothing else.
Any ideas on how to solve this. My website is hosted through domain.com, should I ask them or is it more a ThreeJS issue and if so are there alternatives?

Is there a solution how I can achieve a similar loading speed as seen on the website sketchfab.com because at the moment my simple pot model takes over a minute before it is displayed.
As mentioned in the comments, your glTF assets are just too big. E.g. StraightPot_OrangePaintedIron.gltf has file size of 41 MB. There are several way in order to speed up transmission time.
You can compress the geometry data of your assets with DRACO and use THREE.DRACOLoader in combination with THREE.GLTFLoader to decompress the asset on the client side. The following live example demonstrates the usage of both loaders. Use a tool like glTF Pipeline to compress existing glTF assets.
Reduce the resolution of your textures. Also consider to use texture compression to further reduce the file size. However, it's still a bit inconvenient to use platform independent texture compression with WebGL. Basis will probably make things easier in the future.
Reduce the complexity of your asset in general by simplifying the geometry. Sometimes it's also possible to lower the amount of materials and to merge the respective textures.
three.js R108

Related

javascript games ThreeJS and Box2D conflicts?

I've been trying to experiment with box2d and threejs.
So box2d has a series of js iterations, I've been successful at using them so far in projects as well as threejs in others, but I'm finding when including the latest instance of threejs and box2dweb, threejs seems to be mis-performing when just close to box2dweb but maybe I'm missing something really simple, like a better way to load them in together, or section them off from one another?
I've tried a few iterations of the box2d js code now and I always seemed to run into the same problem with later versions of threejs and box2d together! - currently version 91 threejs.
The problem I'm seeing is very weird.
I'm really hoping someone from either the box2d camp or threejs camp can help me out with this one, please?
Below is a very simple example where I don't initialize anything to do with box2d, but just by having the library included theres problems and you can test by removing that resource, then it behaves like it should.
The below demo uses threejs 91 and box2dweb. It is supposed to every couple of seconds create a box or a simple sphere each with a random colour. Very simple demo, you will see the mesh type never changes and the colour seems to propagate across all mesh instances. However if you remove the box2dweb resource from the left tab then it functions absolutely fine, very odd :/
jsfiddle link here
class Main {
constructor(){
this._container = null;
this._scene = null;
this._camera = null;
this._renderer = null;
console.log('| Main |');
this.init();
}
init(){
this.initScene();
this.addBox(0, 0, 0);
this.animate();
}
initScene() {
this._container = document.getElementById('viewport');
this._scene = new THREE.Scene();
this._camera = new THREE.PerspectiveCamera(75, 600 / 400, 0.1, 1000);
this._camera.position.z = 15;
this._camera.position.y = -100;
this._camera.lookAt(new THREE.Vector3());
this._renderer = new THREE.WebGLRenderer({antialias:true});
this._renderer.setPixelRatio( 1 );
this._renderer.setSize( 600, 400 );
this._renderer.setClearColor( 0x000000, 1 );
this._container.appendChild( this._renderer.domElement );
}
addBox(x,y,z) {
var boxGeom = new THREE.BoxGeometry(5,5,5);
var sphereGeom = new THREE.SphereGeometry(2, 5, 5);
var colour = parseInt(Math.random()*999999);
var boxMat = new THREE.MeshBasicMaterial({color:colour});
var rand = parseInt(Math.random()*2);
var mesh = null;
if(rand == 1) {
mesh = new THREE.Mesh(boxGeom, boxMat);
}
else {
mesh = new THREE.Mesh(sphereGeom, boxMat);
}
this._scene.add(mesh);
mesh.position.x = x;
mesh.position.y = y;
mesh.position.z = z;
}
animate() {
requestAnimationFrame( this.animate.bind(this) );
this._renderer.render( this._scene, this._camera );
}
}
var main = new Main();
window.onload = main.init;
//add either a box or a sphere with a random colour every now and again
setInterval(function() {
main.addBox(((Math.random()*100)-50), ((Math.random()*100)-50), ((Math.random()*100)-50));
}.bind(this), 4000);
so the way im including the library locally is just a simple...
<script src="js/vendor/box2dweb.js"></script>
So just by including the box2d library threejs starts to act weird, I have tested this across multiple computers too and multiple version of both box2d (mainly box2dweb) and threejs.
So with later versions of threejs it seems to have some comflicts with box2d.
I found from research that most of the box2d conversions to js are sort of marked as not safe for thread conflicts.
Im not sure if this could be the cause.
I also found examples where people have successfully used box2d with threejs but the threejs is always quite an old version, however you can see exactly the same problems occurring in my example, when I update them.
So below is a demo I found and I wish I could credit the author, but here is a copy of the fiddle using threejs 49
jsfiddle here
.....and then below just swapping the resource of threejs from 49 to 91
jsfiddle here
its quite an odd one and maybe the two libraries just don't play together anymore but would be great if someone can help or has a working example of them working together on latest threejs version.
I have tried a lot of different box2d versions but always found the same problem, could this be a problem with conflicting libraries or unsafe threads?
but also tried linking to the resource include in the fiddles provided.
Any help really appreciated!!

How can I load a 3d model in Aframejs? It currently works fine in threejs

I have created a 3d animated model, which I managed to run in threejs.
var loader = new THREE.FBXLoader();
loader.load( 'model.fbx', function ( object ) {
object.mixer = new THREE.AnimationMixer( object );
mixers.push( object.mixer );
console.log(object.animations.length);
var action = object.mixer.clipAction( object.animations[ 0 ] );
action.play();
object.traverse( function ( child ) {
if ( child.isMesh ) {
child.castShadow = true;
child.receiveShadow = true;
}
});
scene.add( object );
});
It works perfectly fine on threejs, but how can I use it in aframe, I am trying to create AR app.
I am not getting enough documentation, in AFrame I can display obj model on marker but aframe-extras doesn't seem to work, but Threejs FBX loader works fine. I need help to display threejs scene on on marker scan.
I used FBX2glTF to convert model to glTF and worked fine for me. https://github.com/facebookincubator/FBX2glTF
Regarding the topic: 3D models in a-frame
Try using the three.js JSON, or glTF formats. Both formats are recommended by the a-frame team in the docs.
I remember Don McCurdy pointing out that the fbx models are complicated and hard to interpret, that's why JSON formats came to webGL.
While working with ar.js i remember having no problems using Three.js JSON models with multiple animations, as well as glTF static/one-animation models.
You can easily export you model to gltf using khronos, or kupomans exporters, and three.js JSON using this one.
Furthermore, the glTF models work with the core a-frame library, without any additions !
Regarding fbx's, i've never got them to work properly, so since the other ones are designed for webGL i'd try them out.

Three.js project crashes mobile

I've been working on a three.js project to try and learn the framework. Got a basic model floating around that works fine on the desktop browser but will crash repeatedly on mobile. I uploaded the project on my server http://threedeesneaker.404vanity.com/
Is there any way to optimize this for mobile devices? I tried both chrome and safari for iPhone and iPad.
The code it self:
(function() {
var scene, camera, renderer;
var geometry, material, mesh, sneaker;
init();
animate();
function init() {
scene = new THREE.Scene();
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
var ambient = new THREE.AmbientLight( 0x444444 );
scene.add( ambient );
camera = new THREE.PerspectiveCamera( 3, WIDTH / HEIGHT, 1, 20000 );
camera.position.z = 1000;
window.addEventListener('resize', function() {
var WIDTH = window.innerWidth,
HEIGHT = window.innerHeight;
renderer.setSize(WIDTH, HEIGHT);
camera.aspect = WIDTH / HEIGHT;
camera.updateProjectionMatrix();
});
geometry = new THREE.BoxGeometry( 200, 200, 200 );
material = new THREE.MeshBasicMaterial( { color: 0xff0000, wireframe: true } );
mesh = new THREE.Mesh( geometry, material );
scene.add( mesh );
// prepare loader and load the model
var oLoader = new THREE.OBJMTLLoader();
oLoader.load('models/sneaker.obj', 'models/sneaker.mtl', function(object) {
object.scale.set(1, 1, 1);
object.rotation.y = 600;
object.rotation.z= 600;
sneaker = object;
scene.add(sneaker);
});
// var loader = new THREE.OBJLoader();
// loader.load('models/sneaker.obj', function(object) {
// sneaker = object;
// sneaker.scale.set(1,1,1);
// sneaker.rotation.y = 600;
// sneaker.rotation.z= 600;
// scene.add(sneaker);
// });
renderer = new THREE.WebGLRenderer();
renderer.setSize( WIDTH, HEIGHT );
renderer.setClearColor(0x333F47, 1);
var light = new THREE.PointLight(0xffffff);
light.position.set(-100,200,100);
scene.add(light);
document.body.appendChild( renderer.domElement );
}
function animate() {
requestAnimationFrame( animate );
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
sneaker.rotation.x += 0.01;
sneaker.rotation.y += 0.02;
renderer.render( scene, camera );
}
})();
First a comment on your js : check if typeof sneaker !== 'undefined' in your render loop before asking to rotate your mesh, before loading it generates errors.
Your scene crashes because you are using too detailed materials, I can see a 4096x4096 bump map for instance. It strongly increases frame rendering time on desktop and is probably the reason why the page is irresponsive on mobile : the fragment shader computations become too big.
However it would be a shame to completly delete those details you spent time on. What you can do is to add a device detector in your js. You can use that to display two different models on desktop and on mobile.
But there are further important improvements you can bring. As they are part of my original post I let them there :) :
Resize your textures. You are using two 4096 x 4096 jpg of 4.5MB, this is heavy (note that there are webgl-enabled smartphones with only 500Mo RAM that get realeased these days). Moreover you have very few details that justifies it. You could change your uv to reduce a lot the parts with no details, and probably resize the picture to 512x512. Finally, use a JPG compressor that will reduce the weight by 70-80%. Depending on your picture PNG can be a better choice also. The device's GPU memory is still something else, and if you still need to improve performance you can check in the script if the client supports .pvr or .ktx texture formats, optimized for GPU memory.
An important problem that makes your visualization unappropriate for mobile devices is that you have ... 23 render calls, because you are using 15 textures and 23 geometries.
What it means is that, for each frame, you will have to bind 23 different geometries before the final frame renders. Some mobile CPU-GPU couples cannot do that 60 times per second. Don't plan more than 10 render calls for average mobile devices. That means less geometries with less materials. Merge.
I have not inspected your .obj file in detail to understand how you get 23 geometries in the end, neither where your 13 textures come from, up to you.
A lot of 3D apps (OpenGL) on the stores have more than 23 objects of course. But stores know the apps and they know your phone so they can do the compatibility job and hide the app to low devices.
Here is the tip to check your render calls, geometries and materials in the scene : in your main function, after having set the renderer, include a pointer to it in the window object window.renderer = renderer. Now at runtime in your console, once resources have been loaded, type renderer.info. It will return those data in an object.

Auto-scale when rendering an .stl file with three.js

I'm trying to create a server/webpage that works as follows:
Client uploads an .stl file that they want 3D-printed
Server runs a mesh repair script on the .stl file
The newly repaired .stl file is rendered in the client's browser so they can confirm that the script didn't alter the .stl file in a bad way
The problem I'm having is with step 3. I'm trying to use this example from the three.js repo to render the .stl file:
https://github.com/mrdoob/three.js/blob/master/examples/webgl_loader_stl.html
The problem is, if the model in the .stl file is too large you can't see the entire thing.
I can fix this so that the entire model is in view of the camera by playing around with the three parameters in the mesh.scale.set(x, y, z) function in this code block:
var loader = new THREE.STLLoader();
loader.addEventListener( 'load', function ( event ) {
var geometry = event.content;
var mesh = new THREE.Mesh( geometry, material );
mesh.position.set( 0, - 0.37, 0 );
mesh.rotation.set( - Math.PI / 2, 0, 0 );
mesh.scale.set( 2, 2, 2 );
mesh.castShadow = true;
mesh.receiveShadow = true;
scene.add( mesh );
} );
loader.load( './models/stl/binary/pr2_head_tilt.stl' );
However, I would like to know how to automatically recognize the size of the model in the .stl file, and scale it accordingly.
I know it is possible, as GitHub has achieved this with their STL file viewer.
You can see how it works by navigating here and clicking on one of the .stl files:
https://github.com/mrdoob/three.js/tree/master/examples/models/stl/binary
Basically the GitHub STL file viewer is exactly what I want to emulate, since it loads any .stl file cleanly without requiring the user to zoom-in/out to properly view the model.
How much of a model is visible in your viewport does depend on your camera parameters and how you have set up your scene.
You can use the .boundingBox or .boundingSphere of the Geometry to figure out how big the model is and then use some heuristic to scale it down or up appropriately to what you would like to achieve. There is also a BoundingBoxHelper that can help you visualize the extends of your model.
In addition you can look at:
How to Fit Camera to Object and
Adjusting camera for visible Three.js shape

How do I use texture on a sphere in three.js

I've downloaded a sphere example from: http://aerotwist.com/lab/getting-started-with-three-js/ and I can see the nice red sphere. I'd like to use a texture on it. I've tried this:
var texture = THREE.ImageUtils.loadTexture("ball-texture.jpg");
texture.wrapS = texture.wrapT = THREE.ClampToEdgeWrapping;
texture.repeat.set( 125, 125 );
texture.offset.set( 15, 15 );
texture.needsUpdate = true;
var sphereMaterial = new THREE.MeshBasicMaterial( { map: texture } );
var sphere = new THREE.Mesh(new THREE.Sphere(radius, segments, rings),sphereMaterial);
but I can't see anything, all is black. Does anyone have a working example for sphere texture?
You might have two problems.
First, try loading it like this:
var texture = THREE.ImageUtils.loadTexture('ball-texture.jpg', {}, function() {
renderer.render(scene, camera);
});
texture.needsUpdate = true;
Make sure that the texture size is a power of two (512x512px for IE).
Are you using Firefox? This could be a problem in your browser. Firefox uses some kind of cross-site-blocker for textures. The result is black instead. Take a look at this site for more info: http://hacks.mozilla.org/2011/06/cross-domain-webgl-textures-disabled-in-firefox-5/
Do you have a rendering loop, or did you render the scene just once?
You need to have a rendering loop so that when the THREE.ImageUtils loads the image and updates the texture, you re-render the scene with the now updated texture.
All the three.js examples seem to rely on this technique. I.e., Fire off several async operations involving a fetch of a remote resource, start rendering loop, let scene be updated as remote resources arrive.
IMHO this is Three.js's biggest gotcha for Javascript newbs (like me) who are not familiar with how async operations work.
I had this problem, but if you are loading the html as a file (i.e. locally not a webserver), many browsers (chrome for e.g.) will not allow you to load images in the standard three.js way as it is a security violation.

Categories

Resources