What is the correct way to render on demand

I can’t find an official answer of document for this, although many asked this question before, boss’s answer always mentions camera.onViewMatrixChangedObservable or scene.onPointerObservable, or You can use scene.render() anywhere you want.

But…

camera.onViewMatrixChangedObservable.add( () => scene.render() )

camera.onViewMatrixChangedObservable is actually not working. User input can not fire this observable at all.

scene.onPointerObservable.add( () => scene.render() )

scene.onPointerObservable is over working with some issue. Over working means that mouse move event also fire this observable to render but camera’s view has not changed. Issue means that this method can not handle camera’s inertia, the view changed by inertia will be queued to the next pointer event.

Three.js make this much more simpler with OrbitControl.addEventListener( 'change', () => renderer.render(scene, camera) ) and it works very well.

1 Like

Hi @hjlld and welcome back. It would be very useful if you could supply a simple live example demonstrating the non working of

It is not true that

as this PG shows

I thought PG would had already run engine.runRenderLoop() outside the monaco editor, so it works.

So that rather than

It is

User can not fire this observable in some specific circumstances

Which is why it would be useful to have a simple live example where it is not working. That way a possible bug can be more easily detected and corrected.

try this https://codepen.io/hjlld/pen/wvzXmbM

you can switch render loop to camera.onViewMatrixChangedObservable.add( () => scene.render() )

//engine.runRenderLoop(renderLoop);

let camera = scene.activeCamera;
camera.onViewMatrixChangedObservable.add( () => renderLoop() )


1 Like

Good question.
This is something i am interested in as well but haven’t dug into yet.
The battery on my laptop has seen better days and rendering at 60FPS eats through it (not to mention fan noise…).
I would like for a scene to only be redrawn if there is actually something that /needs/ to be redrawn.

@hjlld You’ll get better help from the Babylon devs if you reproduce the issue in https://playground.babylonjs.com/ .
They are busy people and you are trying to get them to help so if we all stick to the same tool it is more efficient for them.

Unfortunately because scene rendering is done in automatically in the background of the playground @dunk’s problem cannot easily be shown in a PG hence the codepen example.

Makes sense.
Thanks.

First of all sorry not to make the changes directly on codepen but cannot find how to save changes.

@dunk -now, after a bit of work, with added PG Babylon.js Playground

As far as I can make out, the camera view matrix is updated during scene.render() so that if the scene is not rendering there is no change in the view matrix to observe. Also any changes you make to your meshes have to be done explicitly somewhere. As a first step force the view matrix to change by getting it in the engine render loop and put the scene render in the observable.

var renderLoop = function () {
  camera.getViewMatrix(true);
  frames -=0.01
  Cube.rotation.x = frames*2
  Cube.rotation.y = frames*2
  Cube.rotation.z = frames*2  
  torus.scaling.x = Math.abs(Math.sin(frames*2))+0.5
  torus
  cylinder.position.y = Math.sin(frames*3)
  cylinder.rotation.x = frames*1
};

engine.runRenderLoop(renderLoop);

let camera = scene.activeCamera;
camera.onViewMatrixChangedObservable.add( () => scene.render() )

However this version still means that rotations are carried out every frame whether or not the camera view is changing. Further attempt next.

2 Likes

The next fairly obvious attempt is to move the rotation code into the onViewMatrixChangedObservable added function to give

https://www.babylonjs-playground.com/#UNB24W#1

var frames = 0
var renderLoop = function () {
  camera.getViewMatrix(true);
};

engine.runRenderLoop(renderLoop);

let camera = scene.activeCamera;
camera.onViewMatrixChangedObservable.add( () => {
  frames -=0.01;
  Cube.rotation.x = frames*2;
  Cube.rotation.y = frames*2;
  Cube.rotation.z = frames*2;  
  torus.scaling.x = Math.abs(Math.sin(frames*2))+0.5
  torus;
  cylinder.position.y = Math.sin(frames*3);
  cylinder.rotation.x = frames*1;
  scene.render() 
}
)

However it appears that

camera.getViewMatrix(true);

triggers onViewMatrixChangedObservable and so mesh rotations are still carried out every frame.

Changing true to false does not appear to help as in this case the chached value is always read and so the view matrix is never changed. https://www.babylonjs-playground.com/#UNB24W#2

Sorry but I have reached the end of my expertise need @Deltakosh to point in the right direction.

2 Likes

My example is not very well for rendering on demand becasue it’s not a static scene.

so please just ignore the rotations.

my question is simple, how to handle render on demand within a static scene?

1 Like

Ignoring rotations https://www.babylonjs-playground.com/#UNB24W#3

The fps is fixed as it is not now updated by the engine.runRenderLoop

Remove lines 42 -46 to see the difference.

2 Likes

Thanka a lot.

Would be better if this could handle camera’s inertia.

And in my scene (122 drawcalls), fps rate drops a lot, even got much lagger than engine.runRenderLoop( () => scene.render() )

Finally, i can’t find a perfert solution for renderding on demand for static scene with camera’s inertia.

I fall back my code to engine.runRenderLoop( () => scene.render() ) :upside_down_face:

Thanks for your help, but your demo (https://www.babylonjs-playground.com/#UNB24W#3) still render every frame:


You can insert a console to the camera.onViewMatrixChangedObservable, it will continuously print.

Luckily, thanks to your hint, I found a perfect way to do this, just replace camera.getViewMatrix(true) with camera.update(), see: https://www.babylonjs-playground.com/#UNB24W#5

That works perfectly! thanks for your hint!

1 Like

How about this: https://www.babylonjs-playground.com/#UNB24W#5, it works perfectly for me.

1 Like

And in my real case (with many meshes), I found it can improve perfomance dramatically to replace scene.render() with scene.render(false) for avoiding duplicated camera updating (because camera has been updated by render loop). See: https://www.babylonjs-playground.com/#UNB24W#6

Although camera’s calculation is needed in render loop, but quite a big step. :+1:

Is there any way to keep the smooth camera movement while rendering on demand?

You can do something like that:

https://www.babylonjs-playground.com/#UNB24W#7

1 Like