GLTF Model contains lots of Images as textures and is lagging. Any fix?

Hey everyone.

I’ve created a gallery of sorts within Blender with lots around 50 Images (all under around 300kb) each but when I load everything in three when looking in the direction of where all these Images are, my frames drop lots. (The lag happens when I’m looking through walls of the model).

I was wondering if there are any suggestions to reduce this lag? I assume it’s the number of images being loaded in so I’m considering combining multiple Images into one but not sure if that’s going to fix it.

Or if there is any way to unload images within a range of the camera as I’m using pointerlock controls? However they are connected to the GLTF file in the same folder with the AO and UVmapping.

Any suggestions would really be appreciated! Cheers.

Unsure about that - 50 x 300kb shouldn’t be too much to handle (although it may be lagging during the loading, if you’re sending separate requests for the images.)

Would it be possible for you to share the model (can be without the images, just placeholders with similar file size or resolution) or code?

1 Like

Images are uploaded to the GPU the first time they are rendered. If frames are being dropped, interrupting rendering, this is usually the cause. Combining images typically won’t help with it. You could upload every texture ahead of time (e.g. while showing a loading screen) so that it’s all ready to go before the application starts.

There may be other options like Basis Universal or decreasing texture resolution, but it’s hard to say without access to the model.

2 Likes

Hey!

Heres a link to the model files https://drive.google.com/file/d/1rYBUjtBZezGMduwHD7eP-aubPf7b5gaX/view?usp=sharing

I also just noticed that my file sizes have changed from initlally importing them in blender to exporting them in GLTF. I attempted to just replacing the files from the GLTF source files but that broke the model loading.

My code is here:

<!DOCTYPE html>

<html>
<head>
<meta charset="utf-8">
<title>My first three.js app</title>
<style>
body { margin: 0; }
canvas { display: block; }
</style>
</head>
<body>
<!-- <div class="scene"></div> -->
<script src="libraries/three.min.js"></script>
<script src="libraries/PointerLockControls.js"></script>
<script src="libraries/OrbitControls.js"></script>
<script src="libraries/GLTFLoader.js"></script>

<video id="video" loop crossOrigin="anonymous" playsinline style="display:none" muted >
<!-- <source src="sound/aaron.mp4" type="video/mp4">
<source src="movie.ogg" type="video/ogg"> -->
</video>

<section id="loading-screen">
<div id="loader"></div>
</section>


<script>



let scene,camera,renderer, controls, model;

function init(){

scene = new THREE.Scene();
scene.background = new THREE.Color( 0xFFFFFF );

// fog
{
const color = 0xFFFFFF;
const near = 10;
const far = 500;
scene.fog = new THREE.Fog(color, near, far);
}


var video = document.getElementById( 'video' );
var texture = new THREE.VideoTexture(video);
// texture.needsUpdate;
texture.minFilter = THREE.LinearFilter;
texture.magFilter = THREE.LinearFilter;
texture.format = THREE.RGBFormat;
texture.crossOrigin = 'anonymous';

var imageObject = new THREE.Mesh(
new THREE.PlaneBufferGeometry(72/2, 45/2),

new THREE.MeshBasicMaterial({ map: texture }),);

scene.add( imageObject );
video.src = "sound/aaron.mp4";
video.load();
video.play();
imageObject.position.set(82,5,-318);


//Camera Things

camera = new THREE.PerspectiveCamera( 60, window.innerWidth/  window.innerHeight, 0.1, 5000 );
camera.position.set(200,0,0);
console.log(camera.position);


// Lighting Things
const ambient = new THREE.AmbientLight(0xFFFFFF,0.9);
scene.add(ambient);



// Renderer
renderer = new THREE.WebGLRenderer({ antialias:true});
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.setSize(window.innerWidth,window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);


document.body.appendChild( renderer.domElement );



// controls
controls = new THREE.PointerLockControls(camera, renderer.domElement);
// controls = new THREE.OrbitControls(camera, renderer.domElement);

document.body.addEventListener( 'click', function () {
controls.lock();
}, false );




scene.add( new THREE.AxesHelper(500));
// controls.update();


camera.position.y = 1;
camera.position.z = 2;


var onKeyDown = function (event) {
switch (event.keyCode) {
case 87: // w

controls.moveForward(2.4);
break;
case 65: // a

controls.moveRight(-2.4);
break;
case 83: // s

																controls.moveForward(-2.4);
break;
case 68: // d
controls.moveRight(2.4);
 break;
}
};

document.addEventListener('keydown', onKeyDown, false);
}



// Load Model

new THREE.GLTFLoader().load('3d/Alpha/a2.gltf', result => {
model = result.scene;
model.position.set(0,-28,-340);
scene.add(model);
});



// basic Animation
function animate() {

// controls.update();
requestAnimationFrame( animate );

renderer.render( scene, camera);



};
init();
animate();




// Resize function
function onWindowResize(){
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix( );
renderer.setSize(window.innerWidth , window.innerHeight);
};

window.addEventListener('resize',onWindowResize,false);



</script>
</body>
</html>

Copying in a full list of these textures:

TEXTURES
 ────────────────────────────────────────────
┌────┬───────────┬──────────────────────┬──────────────────┬───────────┬───────────┬────────────┬────────────┬───────────┐
│ #  │ name      │ uri                  │ slots            │ instances │ mimeType  │ resolution │ size       │ memSize   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 0  │ sun       │ Images/sun.png       │ emissiveTexture  │ 1         │ image/png │ 4000x4000  │ 7.27 MB    │ 61.04 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 1  │ yessss    │ Images/yessss.png    │ occlusionTexture │ 1         │ image/png │ 4000x4000  │ 4.74 MB    │ 61.04 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 2  │ StefC     │ Images/StefC.png     │ baseColorTexture │ 1         │ image/png │ 8750x4167  │ 8.19 MB    │ 139.09 MB │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 3  │ stef      │ Images/stef.png      │ baseColorTexture │ 1         │ image/png │ 989x645    │ 15.49 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 4  │ JoL       │ Images/JoL.png       │ baseColorTexture │ 1         │ image/png │ 864x1224   │ 1.65 MB    │ 4.03 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 5  │ Jolin     │ Images/Jolin.png     │ baseColorTexture │ 1         │ image/png │ 989x645    │ 8.13 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 6  │ HannahR   │ Images/HannahR.png   │ baseColorTexture │ 1         │ image/png │ 4129x2335  │ 2.97 MB    │ 36.78 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 7  │ Hannah    │ Images/Hannah.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 8.67 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 8  │ JuliaM    │ Images/JuliaM.png    │ baseColorTexture │ 1         │ image/png │ 1248x832   │ 467.18 KB  │ 3.96 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 9  │ julia     │ Images/julia.png     │ baseColorTexture │ 1         │ image/png │ 989x645    │ 10.39 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 10 │ aaron     │ Images/aaron.png     │ baseColorTexture │ 1         │ image/png │ 989x645    │ 12.21 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 11 │ BenT      │ Images/BenT.png      │ baseColorTexture │ 1         │ image/png │ 886x1247   │ 538.04 KB  │ 4.21 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 12 │ ben       │ Images/ben.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 9.21 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 13 │ blankroll │ Images/blankroll.png │ baseColorTexture │ 1         │ image/png │ 1920x2880  │ 669.63 KB  │ 21.09 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 14 │ Elim      │ Images/Elim.png      │ baseColorTexture │ 1         │ image/png │ 989x645    │ 7.48 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 15 │ ElimY     │ Images/ElimY.png     │ baseColorTexture │ 1         │ image/png │ 2732x2048  │ 1.31 MB    │ 21.34 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 16 │ Georgia   │ Images/Georgia.png   │ baseColorTexture │ 1         │ image/png │ 989x645    │ 13.17 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 17 │ GeorgiaW  │ Images/GeorgiaW.png  │ baseColorTexture │ 1         │ image/png │ 2480x3121  │ 1.81 MB    │ 29.53 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 18 │ Hun       │ Images/Hun.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 10.21 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 19 │ HunL      │ Images/HunL.png      │ baseColorTexture │ 1         │ image/png │ 2583x3601  │ 5.03 MB    │ 35.48 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 20 │ Jam       │ Images/Jam.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 6.38 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 21 │ Jami      │ Images/Jami.png      │ baseColorTexture │ 1         │ image/png │ 3269x4898  │ 7.66 MB    │ 61.08 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 22 │ jamie2    │ Images/jamie2.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 10.82 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 23 │ JennyQ    │ Images/JennyQ.png    │ baseColorTexture │ 1         │ image/png │ 2048x2048  │ 2.3 MB     │ 16 MB     │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 24 │ jenny     │ Images/jenny.png     │ baseColorTexture │ 1         │ image/png │ 989x645    │ 13.34 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 25 │ Jo        │ Images/Jo.png        │ baseColorTexture │ 1         │ image/png │ 989x645    │ 10.38 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 26 │ JoanneA   │ Images/JoanneA.png   │ baseColorTexture │ 1         │ image/png │ 3472x720   │ 4.1 MB     │ 9.54 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 27 │ Jun       │ Images/Jun.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 13.79 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 28 │ JunHeeK   │ Images/JunHeeK.png   │ baseColorTexture │ 1         │ image/png │ 2902x2059  │ 2.18 MB    │ 22.79 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 29 │ Karina    │ Images/Karina.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 8.17 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 30 │ KarinaS   │ Images/KarinaS.png   │ baseColorTexture │ 1         │ image/png │ 1000x1400  │ 2.32 MB    │ 5.34 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 31 │ Lan       │ Images/Lan.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 9.75 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 32 │ Lauren    │ Images/Lauren.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 12.77 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 33 │ LaurenM   │ Images/LaurenM.png   │ baseColorTexture │ 1         │ image/png │ 4080x2632  │ 6.95 MB    │ 40.96 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 34 │ LaurenT   │ Images/LaurenT.png   │ baseColorTexture │ 1         │ image/png │ 1433x1890  │ 4.54 MB    │ 10.33 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 35 │ LinsyA    │ Images/LinsyA.png    │ baseColorTexture │ 1         │ image/png │ 3765x2156  │ 4.66 MB    │ 30.97 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 36 │ linsy     │ Images/linsy.png     │ baseColorTexture │ 1         │ image/png │ 989x645    │ 16.65 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 37 │ Matildaa  │ Images/Matildaa.png  │ baseColorTexture │ 1         │ image/png │ 3000x1854  │ 1.55 MB    │ 21.22 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 38 │ matilda   │ Images/matilda.png   │ baseColorTexture │ 1         │ image/png │ 989x645    │ 11.08 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 39 │ Nat       │ Images/Nat.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 8.13 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 40 │ NatN      │ Images/NatN.png      │ baseColorTexture │ 1         │ image/png │ 4961x3508  │ 1008.11 KB │ 66.39 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 41 │ NicoleB   │ Images/NicoleB.png   │ baseColorTexture │ 1         │ image/png │ 1760x706   │ 2.13 MB    │ 4.74 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 42 │ nicole    │ Images/nicole.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 12.24 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 43 │ Ott       │ Images/Ott.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 15.47 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 44 │ Ottof     │ Images/Ottof.png     │ baseColorTexture │ 1         │ image/png │ 1520x2150  │ 4.99 MB    │ 12.47 MB  │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 45 │ sue       │ Images/sue.png       │ baseColorTexture │ 1         │ image/png │ 989x645    │ 11.18 KB   │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 46 │ titlecard │ Images/titlecard.png │ baseColorTexture │ 1         │ image/png │ 1978x1290  │ 64.47 KB   │ 9.73 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 47 │ Yasmin    │ Images/Yasmin.png    │ baseColorTexture │ 1         │ image/png │ 989x645    │ 7.82 KB    │ 2.43 MB   │
├────┼───────────┼──────────────────────┼──────────────────┼───────────┼───────────┼────────────┼────────────┼───────────┤
│ 48 │ YasminH   │ Images/YasminH.png   │ baseColorTexture │ 1         │ image/png │ 1500x1000  │ 3.54 MB    │ 5.72 MB   │
└────┴───────────┴──────────────────────┴──────────────────┴───────────┴───────────┴────────────┴────────────┴───────────┘

Pay attention to the “resolution” and “memSize” columns… even if you re-compress the images to the ~15MB they were before Blender altered them, WebGL has to decompress them all completely before uploading to the GPU, so it’s really uploading hundreds of megabytes here. Unfortunately, even a 10kb PNG could be many megabytes after it is decompressed.

Since a lot of these textures are just text, the biggest thing you could do would be to replace the text rendering with something like https://github.com/Jam3/three-bmfont-text/ or Troika-3d-text: library for SDF text rendering. Trimming all of the images of text so that they don’t contain so much empty space would also help, but not as much.

2 Likes

Thanks so much for this Don.
I’ll have a look at these rendering options to see if it will take time to write and position all the text.

I had no idea WebGL had to decompress to the upload. Good to know.

Do you think replacing all the images as plane geometries with images as textures would be much different in the performance or about the same?

-Cheers

I expect it would be the same, except that maybe it would be easier to control exactly when each texture gets loaded. So, maybe, you could spread out the lag of the uploads more.

Another option to consider — remove the textures from all the “label” objects, and instead put their text onto “Custom Properties” on the object in Blender. Enable the “Export custom properties” option at export, and you’ll get their values in three.js objects’ .userData, so you could do something like this assuming your custom property’s name is “MyLabel”:

scene.traverse((o) => {
  if (o.userData.MyLabel) {
    o.add( new THREE.Mesh( new THREE.TextGeometry( o.userData.MyLabel ) ) );
  }
});

… It’d be a bit more complicated than that to get the text exactly where you want it, of course, but you can adjust the positioning.

EDIT: Also note that TextGeometry requires a .font parameter which I didn’t include above. See three.js docs.

1 Like

I want to know the way how to list the textures like you do.
Thanks~

Hi @alwxkxk — the texture sizes were listed using the inspect command from the glTF-Transform CLI:

gltf-transform inspect model.glb
1 Like