Three.js: Is throttling / intelligent calling of rendering necessary? - three.js

Currently I am using a simple loop in Three.js based on requestAnimationFrame:
...
requestAnimationFrame(animate); // initial call
...
function animate() {
requestAnimationFrame(animate);
// do something
renderer.render(scene, camera); // always render the scene
}
So the render function is called each time animate is executed via requestAnimationFrame. Is that considered good practise? Is the renderfunction optimized so that it does not rerender the scene if nothing (visible) has changed? Or do I have to throttle the call / call the function only if something has changed?

Related

Removing/adding an object from/to scene in three.js

I am trying to add or remove an object from scene:
const rootObject = this.scene.getObjectByName('Object Group');
rootObject.remove(obj);// OR rootObject.add(obj);
What I observe is that to make change visible in scene, user needs to perform some action, like panning or zoomin/out etc.
How can change be made visible immediately without needing a manual action?
It looks like the scene is being redrawn not in the animation loop, but in some events (click, panning or zoomin/out, etc.).
So when adding or removing an object, you need to force a frame to be rendered. For example:
document
.getElementById('toggle')
.addEventListener('click', function() {
if (obj) {
removeObject()
} else {
addObject()
}
renderer.render(scene, camera);
})
[ https://jsfiddle.net/xmke5u20/ ]

Render loop exploding

I made a small demo/boilerplate for loading a custom mesh with Threejs and inspecting it using orbital controls.
The problem here is that when rotating the object the render loop seems to explode and I can't figure out why.
https://codepen.io/rosefalk/pen/ooBYme?editors=0010
function render() {
console.log('render')
canvasPosition = document.getElementById( "container" ).children;
renderer.render( scene, camera );
requestAnimationFrame( render );
}
If you look in the console it's chugging away merily at what I assume is ~60 calls a second until you start rotating, then it goes up at extreme rate.
Solution - Removal of this part:
controls.addEventListener( 'change', render );
When controls are updating it calls render, render calls itself and that explodes the whole thing, adding a massive amount of extra calls. It's a remnant from when it made sense to only update the scene on user interaction.

Clean up Threejs WebGl contexts

I have a problem while cleaning up my WebGl-Scenes. I'm using Three.js with a WebGlRenderer. In my application I have to change the views quite often and therefore need to render new scenes all the time. Uptil now I destroy and reinitialize the entire Threejs scene. After switching the scenes about 15 - 20 times I get following warning:
WARNING: Too many active WebGL contexts. Oldest context will be lost.
After switching a couple of times more the context is lost completly and the application crashes.
Is there a way to destroy the current WebGl context, when cleaning up? Or does the WebGlRenderer always create a new WebGl context when being instantiated?
I'm using Three.js R64.
I've got same problem, but i couldn't solve it using SPA, because of requirements.
There is .forceContextLoss() method in WebGLRenderer (rev 71, maybe was early) for this situations.
So, my code in 'deallocate' method is somemethig like
_self.renderer.forceContextLoss();
_self.renderer.context = null;
_self.renderer.domElement = null;
_self.renderer = null;
You can keep the same renderer for different scenes. The renderer does not care what scene it will render. You can supply a different Scene everytime you call render() if you like.
// instantiate only once and keep it
var renderer = new THREE.WebGLRenderer();
// current scene and camera. Switch whenever you like
var scene = new THREE.Scene();
var camera = new THREE.PerspectiveCamera(...);
fillScene(scene);
// rendering always uses current scene
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
/* ...
* somewhere in your application
* ...
*/
if(condition) {
// switch scene
scene = new THREE.Scene();
fillOtherScene(scene);
}
You should create and use only one WebGlRenderer. In my SPA (single page application) I had weird problem with camera/scene in THREE.js after few redirects. It was because WebGlRenderer was created every time when one particular page was rendered. There was no error in console log (only the warning you wrote).
Bug appeared as change in camera position and problems with rendering.

How do we handle webgl context lost event in Three.js

I want to handle a lost context event in Three.js. There is a nice documentation about that here but unfortunately it doesn't work when I apply it to my renderer.domElement. I try to lose the context by clicking and some variable in loseContext() are undefined.
I guess the structure is different in Three.js. Any expert?
You should be able to do something like this about the renderer was initialized and assuming of course that the variable you stored the renderer into is named 'renderer'.
renderer.context.canvas.addEventListener("webglcontextlost", function(event) {
event.preventDefault();
// animationID would have been set by your call to requestAnimationFrame
cancelAnimationFrame(animationID);
}, false);
renderer.context.canvas.addEventListener("webglcontextrestored", function(event) {
// Do something
}, false);
BTW - loseContext is not defined by Three.JS and it isn't a standard method as of this time. You can simulate it by doing the following.
Load this script before Three.JS
https://github.com/vorg/webgl-debug
Then after you've started your renderer you can hook the loseContext to the canvas.
renderer.context.canvas = WebGLDebugUtils.makeLostContextSimulatingCanvas(renderer.context.canvas);
To trigger the loseContext you would do this.
renderer.context.canvas.loseContext();
And you can then also have it fail after a set number of calls by doing this.
renderer.context.canvas.loseContextInNCalls(5);

Can't seem to make a functional THREE.js collada loader

I'm having issues getting a custom made collada object with no built in camera or lighting to render. I more or less copied what I had seen in a few collada examples to create my own loader, which looks like this:
var loader = new THREE.ColladaLoader();
var room, scene, stats;
loader.load('../Models/Rooms/boot.dae', function colladaReady( collada ){
collada.scene.getChildByName('Cube',true).doubleSided = true;
room = collada.scene;
room.scale.x = room.scale.y = room.scale.z = 1;
room.updateMatrix();
init();
});
The init function is fairly basic and looks like this
scene = new THREE.Scene();
scene.add( room );
scene.add( camera );
renderer.render(scene, camera);
Here is the actual object I'm trying to render. I have also tried it out with the monster.dae file that is in the examples folder without success. The Chrome javascript console isn't showing any errors, so I'm not quite sure where to look in my code. It all resembles functional examples, so I'm not sure why it's not functional. Is there something I'm unaware of that is relevant to collada loading?
RESOLVED: The item was rendering, but had no skin or texture associated with it. So it was rendering at the same colour as the background, which understandably appears to not be rendering at all. Discovered by adding a grid to the ground just to check.

Resources