I often use imgproxy.net to optimize images on my websites. Frequently, I create LQIPs (low-quality image placeholders), a technique that involves initially loading a super tiny, blurry image (2-5 KB) where a high-resolution image will eventually be placed, and then loading the high-quality image in its place. Imgproxy creates LQIPs and optimized images on-the-fly without manual work, so in storage, we only have one version of the image with maximum quality.
I thought it would be great to have something like LQIP for GLTF models. So, I created a small library for this purpose.
To achieve this effect, we should save GLTF files as gltf+bin+textures, as we want to have direct access to the textures. After that, we can import the library:
import { ProgressiveGltfTexturesLoader, ProgressiveGltfTexturesLoaderOptions } from './utils-3d/progressive-gltf-textures-loader';
Then, after create the engine object we can register plugin with options:
SceneLoader.RegisterPlugin(new ProgressiveGltfTexturesLoader({
engine: this.engine,
rules: [{
match: [
'/Assets/DamagedHelmet/glTF/*.jpg',
'/Assets/DamagedHelmet/glTF/*.png',
],
variations: [
'rs:fill:64:64/q:80',
'rs:fill:128:128/q:60',
'rs:fill:256:256/q:60',
'rs:fill:512:512/q:60',
'q:87',
],
}],
replacer: (
url: string,
config: ProgressiveGltfTexturesLoaderOptions['rules'][number],
variation: ProgressiveGltfTexturesLoaderOptions['rules'][number]['variations'][number]
) => {
// Change original to URL to imgproxy path
return `http://localhost:1234/insecure/${variation}/plain/${url}`;
}
}));
Here I describe the rule, when progressive loading shoul work. In this case we work with images by mask:
/Assets/DamagedHelmet/glTF/*.jpg
and /Assets/DamagedHelmet/glTF/*.png
.
Then we define image variations for this rule, here you can see 5 variations with different size and quality. The last (hi res) variant does not change the size, just define quality.
And replacer function that gots original URL and varation that the progressive loader wants to use at this moment. Here I define the imgproxy standalone version domain with port, and configure the url.
So, lastly we shoult initiate this process when we want, for example, immidiately after loading gltf:
SceneLoader.Append('https://www.babylonjs.com/Assets/DamagedHelmet/glTF/', 'DamagedHelmet.gltf', this.scene, scene => {
scene.meshes.forEach((mesh) => gltfLoader.initProgressiveLoading(mesh));
});
without initProgressiveLoading
call we got a model with minimal quality from variations config:
But, when we call this method we reach the progressive loading:
You can see the library here with detailed readme: