regl-project / regl

👑 Functional WebGL
https://regl-project.github.io/
MIT License
5.21k stars 322 forks source link

How to change FPS? #455

Open skulptur opened 6 years ago

skulptur commented 6 years ago

I've searched the docs but couldn't figure out how to change the framerate of the regl.frame loop. It does mention that it's possible to create my own loop but that regl.frame performs some cleanup operations and to use it whenever possible. In case the only way is via a custom function, can someone provide an example of what such function would look like with all the proper precautions?

ForsakenHarmony commented 6 years ago

why do you want to change the framerate?

skulptur commented 6 years ago

Because I use regl to render animations that in the end run at 24fps but regl runs too fast most of the time so it's hard to predict what the final thing will look like.

Even for real time stuff I don't always want it to be at 60fps and using more resources than I can get away with.

I just want to be able to set a hard limit. If I have to do a custom loop that is fine but I think there should be more info on the docs since it mentions it vaguely.

willbamford commented 6 years ago

Think you basically have to do something like:

const loop = () => {
  regl.poll()

  regl.clear({
    color: [0, 0, 0, 1],
    depth: true,
  })

  draw() // your draw commands here
}

Then you can choose how often to invoke loop (e.g. setTimeout). Although you're probably better of making the duration of your animation invariant to the framerate (e.g. calculate delta time between frames and use this to determine how far to move / scale something etc.).

skulptur commented 6 years ago

Thanks, I'll try that. I'm aware of time delta technique but in my case I really need my animations to be exactly the same frame by frame every time I run because I ultimately render to gifs or videos and also another reason for the hard limit is my computer heats a lot unecessarily while developing when it should be so easy to just make it run slower. I was actually surprised to not see that option as part of the lib. Not complaining though, regl is by far the nicest lib I've worked with.

Prinzhorn commented 6 years ago

but in my case I really need my animations to be exactly the same frame by frame every time I run because I ultimately render to gifs or videos

Depending on how you do your animations there is a technique that I've been using, which give's you a guarantee that your gif/video is correct in regards to fps and what every frame displays.

I don't use a render loop at all, but manually progress the animation (e.g. in the case of GSAP using .progress(newProgress)) while doing a simple for-loop. In the following example code it looks like this gifTimeline.progress(currentFrame / numberOfFrames); https://codepen.io/Prinzhorn/pen/GNEmWQ

The benefit is that you don't need your recording to be in sync with the rendering (Screencast will sometimes not be 100% in sync) and you can also render a, say 5 second video, in much less than 5 seconds of actual wall time.

I was actually surprised to not see that option as part of the lib

I think this might be partly because this is not possible to do in a reliable fashion across all browsers. requestAnimationFrame does not give you any guarantees and setTimeout is not accurate enough. requestAnimationFrame is the best choice to get smooth animation for the current user. In your case, e.g. rendering gif/video, you might be better of using the technique described above.

Edit: in your case you might simply mock/patch Date.now/performance.now and progress the time "manually".

skulptur commented 6 years ago

@Prinzhorn Thanks. The rendering part isn't as much of an issue as I'm currently using ccapture.js. My problem was really about previewing since it was running way too fast. As far as I'm concerned the issue is solved although an update to the docs with WebSeed's example might be a good idea.

Prinzhorn commented 6 years ago

@spredemann nice, thanks for pointing to ccapture.js

baptistemanson commented 4 years ago

Sometimes, I like to have some shaders that I don't run at 60 fps because they are expensive in performance. Like read pixels for instance.

Here is what I do:

// define this somewhere
const slowerFrame = (regl, draw, dividerOfFramerate) {
  let i = 0;
  regl.frame( () => {
    i = (i +1) % dividerOfFramerate;
    if(!i) return;
    draw();
  })
}

// then I use it like that:
slowerFrame(regl, drawCommand, 2); // to divide the framerate of this draw loop by 2.

I only deal with multiple of the framerate, as I didn't find any reliable way to Hope that helps!

milahu commented 4 years ago
    if(!i) return;

should be

    if (i) return;
    // or
    //if (i > 0) return;

to skip most draw calls