I generate a series of complete objects using CSG and then render each one in sequence. I want to save the result of the render to a JPEG file, before moving onto the next one. I know how to make the object, how to render it and how to save off the three.js canvas.
It seems like it must be a simple problem but I cannot understand how to structure my code so I can build → render → save each one synchronously and be certain that each has rendered before I save it.
How can this be achieved?
Likely quite a lot of ways how you could structure that, depending on the rest of your code, ex.:
- Create separate
Three.Scene
for each object. Place object in the centre of it. (No need for multiple cameras, just one should be ok.)
- Rendering is synchronous, so now all you really need to do is:
scenes.forEach((scene) => {
renderer.render(scene, camera);
// NOTE Download canvas as JPG
});
-
Here’s an example of how to download canvases.
If your models are dummy thicc and can’t be loaded all at once, you can use async functions to go one-by-one, instead of creating all scenes right away:
(async () => {
for (let i = 0; i < modelUrls.length; i++) {
const gltf = await (new GLTFLoader().loadAsync(modelUrls[i]));
scene.add(gltf);
// NOTE Rendering is synchronous, you don't have to await it
renderer.render(scene, camera);
// NOTE Download canvas as JPG
}
})();
If models contain loadable materials, be sure to use a LoadingManager - wrap it in Promise
and await
until all assets are loaded before rendering.
Thank you! I am sure I tried something similar but it looked like renderer.render(…) was asynchronous so I gave up.
That almost worked perfectly - the first model in the list doesn’t get moved to it’s final position though and I can’t see why. Must be some state set in the subsequent code that makes thing work the next time through the loop but I cannot see it.
I’ll keep trying and thanks again.
I worked around the bad first load/save by inserting a dummy at the start of the list and then discarding it…
However, it appears to run so far (~300 images in a few seconds) that the browser cannot keep up and it randomly misses about 20% of items. (different 20% each time). Maybe there is a way to slow this down and give the browser a chance to keep up.
Update: Adding a delay with code like this:
const sleep = (time) => {
return new Promise((resolve) => setTimeout(resolve, time));
};
seemed to do the trick - only needed 25ms delay but now I get all items, every time. Kind of annoying though - that number probably depends on the speed of my computer.