Three js render a static scene - javascript

From the tutorial on setting up a basic scene here the standard way to call the renderer is something like:
function render() {
requestAnimationFrame( render );
renderer.render( scene, camera );
}
render();
However I am generating a static image so creating frames seems like overkill. Is there any way to render the scene once and then have the rendered image persist?

What I needed to do was delay the call to
this.renderer.render(this.scene, this.camera);
until after all calculations in the scene were finished. Calling it immediately after initialising the renderer was resulting in drawing a white screen as nothing else had been calculated yet.
As a stopgap measure I've put it in a window.setTimeout functions, but I guess the proper way to do it is to put it in a callback function when all the other calculations are finished.

Instead of calling render() just call renderer.render( scene, camera );

Related

Dynamically adding meshes causes controls to lag

I am making a 3D map in Three.js.
I am constructing the geometry and texture from fetched 256x256 pixel images, construct a THREE.Mesh and add it to the scene when it's done.
However, adding tiles to the map causes MapControls to lag noticeably, if the user is panning or zooming around quickly. This can be alleviated by using smaller tiles (such as 128*128), but I want to find a better way, as I've seen examples of very smooth maps using Three.js. The controls work smoothly after all the tiles in view have loaded.
I have 2 event listeners. One triggers when controls change:
this.controls.addEventListener('change', this.update);
And renders the map:
update = () => {
this.renderer.render(this.scene, this.camera);
};
The other listens for the move to end, and then fetches the tiles in view:
this.controls.addEventListener('end', this.fetchTilesIfNecessary);
fetchTilesIfNecessary then creates Promises, which start fetching the tiles. When the tiles are fetched, they are added to the map and this.update is called:
addTile(tile) {
this.scene.add(tile.mesh);
this.update();
}
Perhaps I should mention that I have a callback when the tile mesh gets rendered:
this.mesh.onAfterRender = this.disposeAttributes;
Which clears the attributes which are causing a lot of memory usage
disposeAttributes(renderer, scene, camera, geometry, material, group) {
geometry.getAttribute('position').array = [];
geometry.getAttribute('normal').array = [];
geometry.getAttribute('uv').array = [];
}
Is there a better way? How can I add meshes to the scene dynamically and keep the controls running smoothly?
One way to mitigate this issue is to pre-compile all shaders via WebGLRenderer.compile(). Meaning when your app starts, you add all tiles to your scene and then call renderer.compile() once with the scene's camera.
this.mesh.onAfterRender = this.disposeAttributes;
This is not recommended since it will be executed on each render call. If you want to free the geometry data one the JS side, use the onUpdate() callback of BufferAttribute().
geometry.getAttribute( 'position' ).onUpload( disposeArray );
function disposeArray() {
this.array = null;
}

three js change background image on function call

PREMISE:
I'm trying to create a static scene that has a fixed image background and a geometry in front of it. Since the scene is static, I don't need and envMap.
I created two scenes and cameras (one for the background and one for the geometries) as suggested in this SO question, and this demo, and updated the procedure to consider that THREE.ImageUtils.loadTexture() is deprecated. Here is the working code:
// load the background texture
var loader = new THREE.TextureLoader();
texture = loader.load('path_to_image.jpg');
var backgroundMesh = new THREE.Mesh(
new THREE.PlaneGeometry(2, 2, 0),
new THREE.MeshBasicMaterial({
map: texture
})
);
backgroundMesh.material.depthTest = false;
backgroundMesh.material.depthWrite = false;
// create your background scene
backgroundScene = new THREE.Scene();
backgroundCamera = new THREE.Camera();
backgroundScene.add( backgroundCamera );
backgroundScene.add( backgroundMesh );
The variables backgroundScene and backgroundCamera are global and the following procedure is called inside the init() function. All scenes and cameras are later rendered using:
renderer.autoClear = false;
renderer.clear();
renderer.render( backgroundScene , backgroundCamera);
renderer.render(scene, camera);
PROBLEM:
I implemented an event listener that is supposed to change the background image and geometry when a button is pressed, however this is not working.
I thought that loading the new texture and changing the material property of the backgroundScene variable, clearing the renderer and rendering the scene again would do the job. Here is the code:
var loader = new THREE.TextureLoader();
var texture = loader.load('path_to_new_image.jpg');
console.debug(texture);
console.debug(backgroundScene.children[1].material.map);
backgroundScene.children[1].material.map = texture;
console.debug(backgroundScene.children[1].material.map);
renderer.clear();
renderer.render( backgroundScene , backgroundCamera );
renderer.render(scene, camera);
The console.debug() show me that the new texture is actually loaded and the backgroundScene material is changed accordingly.
However, while the geometries are rendered fine I am left with a blank background and get the following error: [.Offscreen-For-WebGL-0x364ad7e56700]RENDER WARNING: there is no texture bound to the unit 0.
Any ideas of what is going on? Thanks for your help!
you will need to call object.material.needsUpdate = true; for the change to take effect (see here). When the map-property of the material is changed, three.js needs to re-setup the texture-binding, which is skipped unless the needsUpdate-flag is set.
Alternatively, if you just change the material.map.image-property it should work without that extra-step.

How can I control when render three js scene

All examples that I saw, uses a loop structure to render the scene, like that:
renderer.render(cena, camera);
function render(){
renderer.render(cena, camera);
//code to render
requestAnimationFrame(render);
}
render();
But I want to control the rendering in another structure, only when I interact with a thing... like
while(true){
//code - operations
alert('press ok to another step'); // or wait 2 sec
renderer.render(scene,camera);
}
What is the correct method to do this?
Of course, if you want, you can call renderer.render at anytime, although it would probably be a lot better to render it in the requestAnimationFrame() (much better performance).
If you really have a need to change something that you don't want to be renderer (say, several async functions modifying scene object), you can always do something like this.
function render(){
//code to update scene
if (toRender) renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
Set toRender to false while you're updating the scene, this prevent the renderer from re-drawing your scene, then set toRender back to true to have it draw the scene in the next frame.
as shown in this example, you can simply call renderer.render()
http://threejs.org/examples/#raytracing_sandbox

THREE.Object3D or scene or other class provide any tags or methods that I can detected my object has been changed?

Using three.js render obj model in browser.
Here is my logic of program:
function animate() {
requestAnimationFrame(animate);
render(scene, camera);
}
function render(scene, camera){
scene.traverse(function(object) {
scene = setLODDistance(scene, thresHold);
});
renderer.render(scene, camera);
}
animate();
function setLODDistance will return a scene object which has been calculated by LOD.
but I do not want to call this function every fps, I just want to call it after my object in browser been translated or rotated or scaling.
so in THREE.Object3D or scene or other class provide any tags or methods that I can detected my object has been changed(translated or rotated or scaling) ??
thx
You can try to use Object.watch(), poly fill here: link.

ThreeJS Stop Rendering

I am working with ThreeJS on a basic 3d scene that has OrbitControls. Everything works great, except it causes my entire site to lag, as it is looping itself even when the user is not looking at it. I want a function that I can call to start and stop the rendering when certain conditions are met (in this case, the user isn't viewing the canvas). I have a start function that works just fine, but the stop function does not seem to be working, as my site goes unbearably slow after ThreeJS has initialized.
I have looked and looked for a solution to this problem, and have found a couple 'solutions', but for whatever reason they do not work with my application. My assumption is that these solutions are from old versions of ThreeJS.
Here is my code in my main.js file:
var scene,
camera,
controls,
render,
requestId = undefined;
function init() {
scene = new THREE.Scene();
var threeJSCanvas = document.getElementById("threeJS");
camera = new THREE.PerspectiveCamera( 75, threeJSCanvas.width / threeJSCanvas.height, 0.1, 1000 );
controls = new THREE.OrbitControls( camera );
// Controls and Camera settings
// Create Geometry.
}
function render() {
requestId = requestAnimationFrame(render);
renderer.render(scene, camera);
}
function start() {
render();
}
function stop() {
window.cancelAnimationFrame(requestId);
requestId = undefined;
}
In my other javascript file, there is a conditional inside of my pageChange function (this is a multipage app), that looks like the following:
if (page == 5) { // The page with the canvas on it
if (!locationsRendered) {
init();
locationsRendered = true;
}
} else { // If the page is not the page with the canvas on it
if (locationsRendered) {
stop();
}
}
locationsRendered is initialized earlier in this second javascript file in the local scope.
Any help would be much appreciated, as I can not let this simple 3D scene lag my entire app after it has been loaded. It's just not realistic.
If your scene is static, there is no reason for an animation loop. You only need to re-render when the camera moves due to a mouse or touch event.
Just use this pattern:
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render );
function render() {
renderer.render( scene, camera );
}
three.js r.67
I was using trackball controls in my scene and therefore couldn't use the solution above (as the trackball controls continue updating after mouse events finish triggering).
To solve this problem, I used:
function animate() {
renderer.render(scene, camera);
controls.update();
}
renderer.setAnimationLoop(animate);
That runs the animation loop indefinitely. To pause the animation, one can then specify null as the animation loop:
renderer.setAnimationLoop(null); // pause the animation
And to resume the animation, just pass the animation loop again:
renderer.setAnimationLoop(animate); // resume the animation
An alternative solution to completely stopping the render loop is to reduce the frames per second rate and thereby reducing resource consumption.
This approach is particularly useful if you need responsive update on your scene while not necessarily animating, but also need to snap back normal speeds when you need to.
a simple setTimout() achieves this nicely.
var fps 10;
function render() {
//reduce framerate
setTimeout(()=>{
requestAnimationFrame(render);
//must be called to enable rotating
controls.update();
renderer.render(scene, camera);
}, 1000/fps)
};

Categories

Resources