Three.js / ShaderParticleEngine — SPE.Group.tick bad delta argument? - javascript

I'm using the ShaderParticleEngine library for Three.js to create particle emitters.
I picked several code snippets on the Internet to have a working emitter.
Firstly I believed that is wasn't working.
But in fact, the emitter was displayed on the map, but a single motionless particle was on the screen.
After some debugging I undestood that the particle was moving but infinitely slowly. I need to use tick(delta * 1000) to see the emitter in action. And the result is quite ugly (full of gaps, alone particles).I have no problem of low FPS.
The only solution I found is to remove delta argument in the tick function call: particleGroup.tick().
The result is better but is still deceiving, judge by yourself:
Online Emitter Editor:
My result:
I can't understand. I use the same code proposed in the library examples and I use the export feature in the emitter editor.
If I try other variations (eg. on particle life/velocity) I get a very different result in my game, maybe the particle life is not computed correctly because delta argument isn't given?
My game loop:
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
Delta value loop by loop:
30.0000010000003385357559
9.999985195463523e-7
30.0000020000006770715117
0.0000010000003385357559
30.0000020000006770715117
0.0000010000003385357559
0.0000020000006770715117
30.0000010000003385357559
0.000002999999196617864
0.0000010000003385357559
9.999985195463523e-7
0.000002999999196617864
0.0000010000003385357559
0.000001999998858082108
0.0000010000003385357559
20.0000020000006770715117
9.999985195463523e-7
0.0000010000003385357559

To solve smoothness, try the following:
function makeSmoothSPETick(simulator, timeDelta, maxSubstepSize){
var numSubsteps = Math.floor(timeDelta/maxSubstepSize);
var leftOverTime = timeDelta%maxSubstepSize;
while(numSubsteps-->0){
simulator.tick(maxSubstepSize);
}
if(leftOverTime> 0){
//handle the rest
simulator.tick(leftOverTime);
}
}
If you use this function in your code - it will allow you to essentially subdivide steps that are too large into smaller ones of fixed size. As SquareFeet pointed out, say 16ms for 60FPS - you could use something like this:
var render = function() {
time = ctx3d.clock.getElapsedTime();
delta = ctx3d.clock.getDelta();
makeSmoothSPETick(particleGroup, delta, 0.016);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}
You should get results visually similar to what you'd expect if you were running at smooth 60fps. Beware though, if target hardware can't handle these substeps - you may need to get more logic into your solver algorithm. I'd suggest keeping statistics for past 100 frames or so, and using that to decide how much you can split your incoming step value.
EDIT:
To make sure your timing isn't getting mangled, please try the following:
var lastFrameTime = Date.now()/1000;
var animate = function () {
requestAnimationFrame( animate );
render();
stats.update();
};
var render = function() {
time = Date.now()/1000; //getting current time in seconds since epoch
delta = time-lastFrameTime;
lastFrameTime = time;
particleGroup.tick(delta);
if(ctx3d.move)
{
ctx3d.ship.position.z += delta * 500 * 3000;
//ctx3d.camera.position.x = ctx3d.ship.position.x;
//ctx3d.camera.position.z = ctx3d.ship.position.z;
}
ctx3d.renderer.render(ctx3d.scene, ctx3d.camera);
}

I hope posting this as an answer is okay...
I've bumped the particle engine up a minor version to 0.7.7, having implemented a fix for your issue of "not-very-smooth-looking" emitters.
What was happening before was this:
SPE.Emitter.tick() called with a dt value
This tick function determines how many particles should be marked alive based on the dt argument passed to it. For larger dt values, more particles are marked as alive, for smaller values fewer are marked as alice.
The emitter then resets these particles and waits for the next call.
Assuming more than one particle is going to be marked as alive per frame, and they all originate at the same position in space, then all the particles will be at the same place when they're activated. This is why you saw some "clumping" happening.
What happens now is this:
SPE.Emitter.tick() called with a dt value, just as before.
The tick function now determines how many particles should be marked as alive, and whilst marking them so, sets each particles age to be a fraction of the dt value passed in.
So (!), assuming 100 particles are emitted per frame, and a dt value of 0.016 is passed to the emitter's tick function, each of those 100 particles that will be marked as alive is assigned an age value of (0.016 / 100) * i where i is the particle index (in this case, a value of 0 to 100).
I hope that makes sense. You can see the changes here: https://github.com/squarefeet/ShaderParticleEngine/blob/master/src/ShaderParticleEmitter.js#L240-L246
Master branch has been updated.

Related

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..

Javascript time-based animation and requestAnimationFrame

I have been playing around with canvas and animation, with HTML5 games in mind specifically and quickly learnt the limitations of just using requestAnimationFrame (rFA) and have moved to time-based animations.
I want to maintain constant gameplay regardless of monitor refresh rate or FPS but am unsure how best to handle the animations. I have read through all sorts of implementations but have not found any best practice so to speak. Should I be using a combination of the two?
So far I have considered several options:
rFa only (changes results when fps changes):
var animate = function() {
draw();
requestAnimationFrame(animate);
}
time-based only (not always consistent):
var animate = function() {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
window.setTimeout(animate, 1000/60)
}
set FPS on rFA with setInterval (not always consistent):
setInterval(function () {
draw();
requestAnimationFrame();
}, 1000/fps);
rFA trying to force fps (does not seem very robust, variable delta would work better):
var delta = 1000 / fps;
var animate = function() {
now = Date.now();
if (now - last >= delta) {
last = now;
}
draw(delta);
requestAnimationFrame(animate);
}
time-based rFA (some strange results):
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
draw(delta);
requestAnimationFrame(animate);
}
Ignore the lack of browser support and the use of Date.now(), I just want to demonstrate my flow of thinking. I think that the last option is preferable, but the last two can run into problems with updating too far and missing collisions etc as well as updates taking too long that the animation looses all control.
Also when a user tabs out using rFA only the animation will pause, using a time based function to call rFA means that the game/animation will continue to run in the background which is not ideal.
What would be the best way to handle animations trying to keep consistent results regardless of fps, all of the above might be bad and my apologies for the long post (it is just what I have tried so far and am still pretty lost)? even better with with the above issues in mind?
If you have requestAnimationFrame available, I wouldn't go against it and only call draw() from its callbacks. Of course, you should always use delta timing.
Here's a sophisticated variation of raF with a fallback to setTimeout for the game logic updates in case the frame rate is too low:
var maximalUpdateDelay = 25; // ms
var updateTimeout, now;
function animate() {
updateTimeout = setTimeout(animate, maximalUpdateDelay);
var delta = -now + (now = Date.now());
update(now, delta);
}
function main() {
clearTimeout(updateTimeout);
animate(); // update the scene
draw(); // render the scene
requestAnimationFrame(main);
}
main();
I'ld recommend taking a look at the HTML 5 - Game Development course on Udacity. I don't remember the implementation of this problem from the course (but there definitely was one), but my opinion from a gameplay perspective is that just using rAF (like your first bullet) is the most fun, even if there is game slow down due to too much processing needed on slower computers.
I think you're on the right track with the last one because it should give you the most consistency across devices running at different frame rates, but you definitely want to force your delta value down if it gets too high to avoid big jumps:
var animate = function () {
now = Date.now();
delta = now - last;
last = now;
if(delta > 20) {
delta = 20;
}
draw(delta);
requestAnimationFrame(animate);
};

requestAnimFrame can't give a constant frame rate, but my physics engine needs it

I use Box2D with WebGL.
Box2D demands a constant frame rate (time steps for it's "world" updates).
function update(time) {//update of box2d world
world.Step(
1/60 // 1 / frame-rate
, 3 //velocity iterations
, 8 //position iterations
);
But I've read that requestAnimFrame defined as below is the right way to go.
requestAnimFrame = (function() {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.oRequestAnimationFrame ||
window.msRequestAnimationFrame ||
function(/* function FrameRequestCallback */ callback, /* DOMElement Element */ element) {
window.setTimeout(callback, 1000/60);
};
})();
requestAnimFrame doesn't give me a constant frame rate and so my Box2D's variables are going unsynchronized.
Is there a fix to this?
[EDIT]
John's (Cutch) solution when implemented looks like this:
function interpolate(dt) {
var t = dt/time_step;
body_coordinates = (1-t) * body_coordinates + t * next_body_coordinates;
}
var physicsDt = 0;
function tick() {
var time_now = new Date().getTime();
var dt = time_now - last_time; //Note that last_time is initialized priorly
last_time = time_now;
physicsDt += dt;
clear_the_screen();
requestAnimFrame(tick);
drawEverything();
if(physicsDt >= time_step) {
update();
physicsDt -= time_step;
}
interpolate(dt);
}
Note that my physics update function takes care that the next_attribues are set.
And also, a physics update is called before this, to keep the physics world ahead by 1 frame.
The result
The animation is fairly smooth, except for those times when I can see some really bad jumps and random appearing micro-movements.
I thought the following issues were not addressed in the solution :
----> 1) dt may become larger than time_step : This would make dt/time_step greater than 1 which would ruin the interpolation equations.
When dt remains larger than time_step consistently, problems would increase.
Is it possible to overcome the problem of the time gap becoming larger than time_step ?
I mean, even if we keep the world one frame ahead of the rendering, if time gaps consistently stay greater than time_step, it wouldn't take long pass that "ahead" frame.
----> 2) Imagine dt being lesser than time_step by 1 ms. Then, the world is not updated that one time. Now interpolation is done and the approximate position is found(1 ms behind where it should have been).
Lets say the next time no difference is seen between dt and time_step.
Now, no interpolation is done considering that dt and time_step are equal. So, the next that is drawn is the "ahead" frame in the world, right?(using those equations, with t = 1)
But accurately, the rendered world should be that 1ms behind which it was before.
I mean, that 1ms by which it was behind the world frame should not vanish.
But with t = 1, draws the physics world frame and forgets that 1ms.
Am I wrong about the code or the above 2 points?
I request you to clarify these issues.
[EDIT]
I asked the author of this webpage, for a way to efficiently draw many shapes, in the comments there.
I learnt to do it this way :
I'm saving bufferData calls by keeping separate buffers for each shape and calling createBuffer, bindBuffer, bufferData only once during init.
Everytime I refresh the screen, I have to iterate over all the shapes and
I have to call enableVertexAttribArray and vertexAttribPointer after binding the required shape's buffer(using bindBuffer).
My shapes don't change with time.
There are just a variety of them (like polygons, circles, triangles) that stay from beginning to end.
You must decouple your physics simulation stepping timing from your render vsync timing. The easiest solution is to do this:
frameCallback(dt) {
physicsDt += dt;
if (physicsDt > 16) {
stepPhysics();
physicsDt -= 16;
}
renderWorld();
requestAnimFrame(frameCallback);
}
The biggest issue here is that sometimes you'll be rendering with an outdated physics world, for example, if physicsDt was 15 no simulation update will occur but your objects would have moved almost an entire frame by that point in time. You can work around this by keeping the physics 1 frame ahead of the rendering and linearly interpolating object positions in the renderer. Something like:
var t = dt/16.0;
framePosition = (1-t) * previousFramePositions + (t) * nextFramePositions;
That way your objects move smoothly even if you're rendering is out of sync with your physics simulation. Let me know if you have any questions.
John
requestAnimFrame isn't meant to guarantee a constant frame rate – it's designed so that the browser only does the calculations for frames it actually draws.
If you want/have to calculate frames that aren't drawn, then it isn't the way to go.

HTML Canvas Interval vs RequestAnimationFrame

So, maybe total brainfart here. The syntax for setInterval() is pretty clear. Do something every x miliseconds. How is this best translated to using the requestAnimationFrame() ?
I have about 300 objects and each is supposed to perform an animation sequence at a certain interval (every 8, 6, 2, etc seconds)? How can I best accomplish this using requestAnimationFrame() which gets called ~60 times a second? There is probably an easy answer, I just, for the life of me, can't figure it out.
To force requestAnimationFrame to stick to a specific FPS you can use both at once!
var fps = 15;
function draw() {
setTimeout(function() {
requestAnimationFrame(draw);
// Drawing code goes here
}, 1000 / fps);
}
A little weird, but noth the most confusing thing in the world.
You can also use requestAnimationFrame not with FPS but with elapsed time in order to draw objects that need to be updated based on the time difference since the last call:
var time;
function draw() {
requestAnimationFrame(draw);
var now = new Date().getTime(),
dt = now - (time || now);
time = now;
// Drawing code goes here... for example updating an 'x' position:
this.x += 10 * dt; // Increase 'x' by 10 units per millisecond
}
These two snippets are from this fine article, which contains additional details.
Good question by the way! I don't think I've seen this answered on SO either (and I'm here way too much)
requestAnimationFrame is pretty low level, it just does what you already said: roughly gets called at 60fps (assuming the browser can keep up with that pace). So typically you would need to build something on top of that, much like a game engine that has a game loop.
In my game engine, I have this (paraphased/simplified here):
window.requestAnimationFrame(this._doFrame);
...
_doFrame: function(timestamp) {
var delta = timestamp - (this._lastTimestamp || timestamp);
for(var i = 0, len = this.elements.length; i < len; ++i) {
this.elements[i].update(delta);
}
this._lastTimestamp = timestamp;
// I used underscore.js's 'bindAll' to make _doFrame always
// get called against my game engine object
window.requestAnimationFrame(this._doFrame);
}
Then each element in my game engine knows how to update themselves. In your case each element that should update every 2, 6, 8 seconds needs to keep track of how much time has passed and update accordingly:
update: function(delta) {
this.elapsed += delta;
// has 8 seconds passed?
if(this.elapsed >= 8000) {
this.elapsed -= 8000; // reset the elapsed counter
this.doMyUpdate(); // whatever it should be
}
}
The Canvas API along with requestAnimationFrame are rather low level, they are the building blocks for things like animation and game engines. If possible I'd try to use an existing one like cocos2d-js or whatever else is out there these days.

requestAnimationFrame at a limited framerate

As I understand it, usage of the JS requestAnimationFrame API is intended for cases where the framerate isn't in need of being controlled, but I have a use case where it's essential that a <canvas> only updates at a certain fps interval that may be anywhere between 1 and 25 (between 1 and 25 frames per second, that is). Can I then somehow still effectively use rAF to get at the optimizations it offers?
This question has similarities to mine, but the accepted answer there made close to zero sense to me in the context of that question.
I have two possible solutions for this. The first one involves using a while loop to halt the execution of the script for a specified delay before calling requestAnimationFrame from within the callback. In the example where I saw this, it effectively limited the fps of the animation, but it also seemed to slow down the entire tab. Is this still actually a good solution? The second alternative, as mentioned in the question I linked to above, calls requestAnimationFrame within a setInterval. To me that seems a bit convoluted, but it could be that's the best option?
Or is there a better alternative to accomplish this?
Yoshi's answer is probably the best code solution to this problem. But still I'll mark this answer as correct, because after some research I basically found that my question was invalid. requestAnimationFrame is really meant to keep frame rates as high as possible, and it optimizes for scenarios where animation is meant to be kept consistent and smooth.
Worth noting though is that you don't need requestAnimationFrame to get optimization (even though rAF was touted as a great performance booster) since browsers still optimize regular drawing of a <canvas>. For example, when a tab isn't focused, Chrome for one stops drawing its canvases.
So my conclusion was that this question was invalid. Hope this helps anyone who was wondering something similar to me.
This is just a proof of concept.
All we do is set our frames per second and intervals between each frame. In the drawing function we deduct our last frame’s execution time from the current time to check whether the time elapsed since the last frame is more than our interval (which is based on the fps) or not. If the condition evaluates to true, we set the time for our current frame which is going to be the “last frame execution time” in the next drawing call.
var Timer = function(callback, fps){
var now = 0;
var delta = 0;
var then = Date.now();
var frames = 0;
var oldtime = 0;
fps = 1000 / (this.fps || fps || 60);
return requestAnimationFrame(function loop(time){
requestAnimationFrame(loop);
now = Date.now();
delta = now - then;
if (delta > fps) {
// Update time stuffs
then = now - (delta % fps);
// Calculate the frames per second.
frames = 1000 / (time - oldtime)
oldtime = time;
// Call the callback-function and pass
// our current frame into it.
callback(frames);
}
});
};
Usage:
var set;
document.onclick = function(){
set = true;
};
Timer(function(fps){
if(set) this.fps = 30;
console.log(fps);
}, 5);
http://jsfiddle.net/ARTsinn/rPAeN/
What you can do, though I don't really know if this is really any better is:
render to an invisible context with requestAnimationFrame
update a visible context with setInterval using a fixed fps
Example:
<canvas id="canvas"></canvas>​
<script type="text/javascript">
(function () {
var
ctxVisible = document.getElementById('canvas').getContext('2d'),
ctxHidden = document.createElement('canvas').getContext('2d');
// quick anim sample
(function () {
var x = 0, y = 75;
(function animLoop() {
// too lazy to use a polyfill here
webkitRequestAnimationFrame(animLoop);
ctxHidden.clearRect(0, 0, 300, 150);
ctxHidden.fillStyle = 'black';
ctxHidden.fillRect(x - 1, y - 1, 3, 3);
x += 1;
if (x > 300) {
x = 0;
}
}());
}());
// copy the hidden ctx to the visible ctx on a fixed interval (25 fps)
setInterval(function () {
ctxVisible.putImageData(ctxHidden.getImageData(0, 0, ctxHidden.canvas.width, ctxHidden.canvas.height), 0, 0);
}, 1000/40);
}());
</script>
Demo: http://jsfiddle.net/54vWN/

Categories

Resources