Three.js: Takes too much time to render first frame - javascript

I use Three.js (with WegGL) to render alternating scenes of many image tiles (a few thousands) animating at space. Animations are handled by Tween.js. I use Chrome for testing.
To optimize the image loading, I preload all the texture images before the first scene is displayed. All textures are then saved in memory as THREE.Texture. Now when I prepare a scene for display, I do something like this:
let tile = null, tweens = [], cameraZ = 1000;
for (let y = 0; y < rows; y++){
for (let x = 0; x < columns; x++){
tile = await this.createTile(x, y, [textureSize]);
tile.position.x = x * this.tileWidth - this.picWidth / 2;
tile.position.y = -y * this.tileHeight + this.picHeight / 2;
tile.position.z = cameraZ * 1.1;
tweens.push(new TWEEN.Tween(tile.position)
.to({z: 0}, 4000)
.delay(200 + x * 120 + Math.random() * 1000)
.easing(TWEEN.Easing.Cubic.InOut));
this.scene.add(tile);
}
}
tweens.map(t => t.start());
The scene preparation also include camera and a point light, and takes about 400 ms to complete.
I then render the scene like this:
function render(){
requestAnimationFrame(render);
TWEEN.update();
renderer.render(this.scene, this.camera);
}
render();
Everything is displayed properly by when measuring some processing durations, I see that the first rendering call takes about 1400 ms! Other calls take between 70 to 100 ms.
My final goal is to have multiple scenes like this, play one after another without any freezes. Given that all the required assets are already loaded, what might be the problem and how can I optimize that?
Thanks

During the first frame of rendering, all of your assets and shaders are being compiled and uploaded to the GPU. If you want to avoid that, you'll have to do some tricks behind the scenes.. like forcibly rendering each object once after you load it, perhaps by adding it to a single scene, and calling renderer.render on it. Depending on what the bottleneck is (shader compilation vs asset uploading) this may or may not help.. but the workaround is doing some kind of pre-rendering to force the uploads to the card one at a time, rather than all at once.
Also, as a previous commenter noted, your render loop above has a typo in it.
it should be requestAnimationFrame(render);

Related

Threejs - browser freezes when I load 80000 words what is the more efficient way of rendering plain 2d Text?

I tried to render 80,000 different words using WebGL specifically by three.js
however, once I try to render this amount of words,
my web browser was frozen.
Is there a better way to render a ton of words as 2D context?
I used an add-on of three.js called 'three-spritetext'
and how I added the words to be rendered is like below.
then((d) => {
for (let i = 0; i < characters.length; i++) {
const SimpleText = new SpriteText(`${characters[i]}`, 5);
SimpleText.color = 'orange';
SimpleText.position.x = -200 + Math.random() * 10;
SimpleText.position.y = 15 + Math.random() * 10
lotsofText.push(SimpleText)
}
})
To get the limit of the performance roughly,
I tried to scale down of the length of words to be rendered.
When I run this in my local host, it worked comparably okay when I went with length/3.
my sample code is here with the data.
https://codepen.io/jotnajoa/project/editor/AQOvpy
Thank you in advance.

How to implement a deterministic/tick-based game loop?

First of all, I'm trying to make a "simple" 3D game using Three.js and in the future some network framework to make it multiplayer, since I plan on doing the network part in the future I searched a little and discovered that most "action" game use a "tick" based game loop to make it possible to sync the clients and the server, and then interpolate between the ticks to make it smooth.
I already have some "working" code of the tick (handle input, update, draw) function, what I want to know is if my implementation is right, and how this "deterministic" loop should work, supposing that my implementation is working, when I increase the "tick rate" the game gets faster (the update function is running more times), is this right?
this.loops = 0;
this.tick_rate = 20;
this.skip_ticks = 1000 / this.tick_rate;
this.max_frame_skip = 10;
this.next_game_tick = performance.now();
This first part of the code is inside the constructor of the Game class
Game.prototype.run = function () {
this.handle_input();
this.loops = 0;
while (performance.now() > this.next_game_tick && this.loops < this.max_frame_skip){
this.up_stats.update();
this.update();
this.next_game_tick += this.skip_ticks;
this.loops++;
}
this.draw();
//monitor performance
this.stats.update();
//next update
requestAnimationFrame(this.run.bind(this));
};
Full code at: https://github.com/derezzedex/first_three_js/blob/master/js/game/main.js
This looks pretty reasonable to me and I've used similar patterns in the past.
Structuring synchronized simulations is a huge topic, but what you have is a good starting point and might be enough depending on the complexity of your game.
edit: A bit more detail...
Yes it works the same, except that this.dt is always the same. i.e. 1000 / your desired FPS for the game loop.
If you want to do the smoothing/interpolation in between frames... you'll have to record the previous state of your object as well.. and you probably won't want to use the Euler rotations, since eulers don't interpolate well. because an angle of 360 degrees, flips back to 0, so the interpolation logic gets weird...
But instead.. you can record the state before and after the update...
and interpolate the .quaternion instead.. which for small changes in rotation works fine just linear interpolating .. If the changes are too big, you can use quaternion.slerp() which can handle interpolating over big distances.
So you've got lastTickTime, and currentTime, and nextTickTime ....
each frame.. you're doing something like:
To interpolate you do something like:
var alpha= (currentTime-lastTickTime) / (nextTickTime-lastTickTime);//nextTickTime-lastTickTime = your framerate delta so for 60fps = 1000/60 = 16.666666666666668
var recip = 1.0 - alpha;
object.position.x = (object.lastPosition.x * recip)+(object.nextPosition.x*alpha)
object.position.y = (object.lastPosition.y * recip)+(object.nextPosition.y*alpha)
object.position.z = (object.lastPosition.z * recip)+(object.nextPosition.z*alpha)
object.scale.x = (object.lastScale.x * recip)+(object.nextScale.x*alpha)
object.scale.y = (object.lastScale.y * recip)+(object.nextScale.y*alpha)
object.scale.z = (object.lastScale.z * recip)+(object.nextScale.z*alpha)
object.quaternion.x = (object.lastQuaternion.x * recip)+(object.nextQuaternion.x*alpha)
object.quaternion.y = (object.lastQuaternion.y * recip)+(object.nextQuaternion.y*alpha)
object.quaternion.z = (object.lastQuaternion.z * recip)+(object.nextQuaternion.z*alpha)
object.quaternion.w = (object.lastQuaternion.w * recip)+(object.nextQuaternion.w*alpha)
In a proper three app, you probably shouldn't store the lastPosition and nextPosition directly on the object, and instead put it in the object.userData, but whatever.. it will probably still work..

How to render large scale images from canvas composition

I'm thinking of using three.js for my art project. What I want to do is to write some code that generates some graphics and then save it in high resolution. When I say high resolution I mean something like 7000 x 7000 px or more, because I would like to print it.
So far I have been doning something like that with Flash and vvvv but I would like to know if this is possible and if there are some examples available in three.js.
You can see some of my stuff here: https://www.behance.net/onoxo
WebGL generally has a size limit. Modern GPUs that size limit might be 8192x8192 (256meg) or even 16384x16384 (one gig) but in other areas of the browser (like the space required for the screenshot) you're likely to run out of memory.
You can get round this by rendering portions of the larger image as separate pieces and then stitching them together in some other program like photoshop or the gIMP.
In Three.js you'd do that something like this. Assuming you take one of the samples
function makeScreenshots() {
var desiredWidth = 7000;
var desiredHeight = 7000;
var stepX = 1000;
var stepY = 1000;
for (var y = 0; y < desiredHeight; y += stepY) {
for (var x = 0; x < desiredWidth; x += stepX) {
camera.setViewOffset( desiredWidth, desiredHeight, x, y, stepX, stepY );
renderer.render( scene, camera );
var screenshotDataURL = renderer.domElement.toDataURL();
saveScreenshot( "screenshot" + x + "-" + y + ".png", screenshotDataURL );
}
}
}
Note: you'd have to provide the function saveScreenshot and most likely have a tiny node.js or python server running to use to save the screenshots but with this technique you can generally generate almost any resolution image you want.
See: https://greggman.github.io/dekapng/

CreateJS caching object - object now does not animate on stage

I am using CreateJS to add graphics (shapes) and bitmaps to my stage, and add that to my HTML5 canvas.
After moving the circle graphic around the screen (20px in size), there was severe lag after a little while.
I followed this article to figure out performance issues: http://blog.toggl.com/2013/05/6-performance-tips-for-html-canvas-and-createjs/
So I tried caching... now when I press the keys, the circle does not move. Am I caching incorrectly?
world = new createjs.Container();
segment = new createjs.Shape();
segment.graphics.beginFill("red").drawCircle(0, 0, 20);
segment.x = 100;
segment.y = 100;
segment2 = new createjs.Shape();
segment2.graphics.beginFill("black").drawCircle(0, 0, 20);
segment2.x = 150;
segment2.y = 150;
ContainerOfPeople = new createjs.Container();
ContainerOfPeople.addChild(segment, segment2);
world.addChild(ContainerOfPeople); //add container of people to world container (which will contain all objects in a container)
world.cache(0, 0, 1000, 1000); //cache all objects within world container
stage.addChild(world);
Edit:
If I don't cache the tiles after creating the map, I can see them rendered to the canvas:
function createWorld() {
background = new createjs.Container();
for (var y = 0; y < mapWidth; y++) {
for (var x = 0; x < mapHeight; x++) {
var tile = new createjs.Bitmap('images/tile.png');
tile.x = x * 28;
tile.y = y * 30;
background.addChild(tile);
}
}
//background.cache(0, 0, mapWidth, mapHeight);
stage.addChild(background);
}
If I do cache the background container of tile children, it won't render
function createWorld() {
background = new createjs.Container();
for (var y = 0; y < mapWidth; y++) {
for (var x = 0; x < mapHeight; x++) {
var tile = new createjs.Bitmap('images/tile.png');
tile.x = x * 28;
tile.y = y * 30;
background.addChild(tile);
}
}
background.cache(0, 0, mapWidth, mapHeight);
stage.addChild(background);
}
Why?
You wouldn't want to cache the whole world if you are animating/moving its child objects. Think of caching as taking a snapshot of the DisplayObject and all of its children. While an item is cached, you won't see any changes you make to the children until you update the cache again as explained in the EaselJS docs:
http://www.createjs.com/Docs/EaselJS/classes/DisplayObject.html#method_cache
cache ( x y width height [scale=1] )
Defined in cache:735
Draws the display object into a new canvas, which is then used for subsequent draws. For complex content that does not change frequently (ex. a Container with many children that do not move, or a complex vector Shape), this can provide for much faster rendering because the content does not need to be re-rendered each tick. The cached display object can be moved, rotated, faded, etc freely, however if its content changes, you must manually update the cache by calling updateCache() or cache() again. You must specify the cache area via the x, y, w, and h parameters. This defines the rectangle that will be rendered and cached using this display object's coordinates.
To expand on the explanation, let's say you have a game character that is a Container made up of 6 child shapes, 2 arms, 2 legs, a body and a head. During gameplay, the character's arms and legs flail around. In this scenario, you DON'T want to cache the character as you would be forced to update the cache each time the arms and legs moved, removing any performance gain from caching.
However, let's say once the character dies, he freezes in a dead position, and alpha fades off the screen. In this case you WOULD cache the character. This is because alpha animations become increasingly CPU intensive with the greater number of shapes it has to consider. By caching the character, you are effectively telling the CPU to tween just one shape instead of 6. You can then uncache once your alpha tween is complete and you want to return the player for round 2.
Update
easeljs loads images asynchronously when they are first referenced. Because you aren't preloading your image, the image data isn't yet loaded into memory when you are caching your background.
http://jsfiddle.net/8EvUX/
Here's a fiddle where the image is embedded as a base64 encoded string and is therefore available to cache immediately to prove the caching works as expected. My suggestion would be to use the preloadjs library to load your image first before adding it to the stage.

Canvas multiple images and Mobile Browser performance

I'm writting about the mobile browser performance with HTML5 canvas. I'm trying to make a simple platform game (like super mario bros). I have a main character, two enemies and blocks imitating jumping platforms. Character and enemies are drawn by drawImage, block are drwn by fillRect (for now, later it will be also drawImage). Everything is animated (when character moves, the character X is added to blocks X and so on).
Now I'm trying to add some random coins.
First I created variable for Image
var coinB = new Image();
coinB.src = 'coin.png';
Next I'm creating array with objects with random X and Y:
var k;
for (k = 0; k <= 30; k++) {
coins.push({
x: Math.floor(Math.random() * 36 + 4) * 100,
y: Math.floor(Math.random() * 3 + 1) * 100,
width:25,
height:25
});
}
And after that I'm trying to select everything and draw:
var l;
/* left is the character X for the animation */
for (l = 0; l < coins.length; l++) {
ctx.drawImage(coinB, coins[l].x - left, coins[l].y, coins[l].width, coins[l].height);
}
Everything is in a function() that is in requestAnimFrame.
Unfortunately after that, game has about 30fps (from previously 60 fps without coins) on Mobile FireFox (Chrome Mobile 20-30 fps). So it's about half of fps with coins.
Is there a better way to import images and draw them? For example I do the new Image() for all thing (mainchar = new Image(), enemy = new Image(), coin = new Image()= ect), the same with .src. I assume it's not the best solution.
How should I do, to gain better performance (to lose less fps) ?
Thank you for help.
I have a similar experience; I have looked for tips and trick, but there are no magic ways to solve the performance problem.
The key to improving performances is reducing the calls to "drawImage" to the absolute minimum... keep in mind that it is the bottleneck of your process!
So, be sure to draw only what is currently visible (i.e. dont draw coins/blocks/background) that are out of the view).
For what concern images loading, I dont see any alternative to what you are currently doing.
The best you can do is using a unique file, containing all the images, and then using the right portion when you need it; this should reduce download times (1 larger files is better than many smaller files), but wont increase performances.
Hope this helped a little, have fun!

Categories

Resources