I'm playing around with Three.js examples and VR support,
While most examples port well,
When Skybox is involved it doesn't seems to be ported to VR view.
Init:
vrEffect = new THREE.VREffect(renderer, VREffectLoaded);
vrControls = new THREE.VRControls(camera);
function VREffectLoaded(error) {
if (error) {
console.log("error with init VR : " + error);
}
}
When Rendering
if(flags.vrMode===true)
{
vrControls.update();
vrEffect.render( scene, camera );
}
else
{
renderer.render( scene, camera );
}
I've tried to reload the Skybox mesh onto the scene before the first VR render affect but it didn't help
if(flags.vrMode===true)
{
if(flags.tempFirst === true)
{
scene.add( skyBox );
flags.tempFirst != flags.tempFirst;
}
vrControls.update();
vrEffect.render( scene, camera );
}
else
{
renderer.render( scene, camera );
}
Related
I'm trying to understand how to be memory efficient in three.js. In my main application i try to load and dispose textures depending on the distance to the camera to save memory in the gpu. Since i work with a lot of textures and large textures in my main application i can't possibly work with a texture atlas into which i initially load all the textures. Here I have a code example of how i imagine it. Loading the texture works but not deleting it. But i don't see why that is. According to the three.js docu this should work with dispose() but it doesn't work here.Wwhere is my mistake?
In general: If someone thinks there is something in my concept that could be improved, i'd be happy to hear your advice. I only do what i do within the framework of what i can best imagine based on what i know so far. I like to learn about it. For example, i would be interested in how i not only load and dispose textures. Also the object that i have created here should be efficiently added and deleted as a whole, added again and deleted, ... after all, that's the purpose of class objects, being able to be created and deleted again at will.
var camera, controls, scene, renderer, container, aspect;
var cube;
async function main() {
await init();
animate();
}
async function init() {
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
renderer.setClearColor( 0x000000 );
container = document.getElementById('container');
renderer.setSize(container.clientWidth, container.clientHeight);
container.appendChild( renderer.domElement );
aspect = container.clientWidth / container.clientHeight;
scene = new THREE.Scene();
scene.background = new THREE.Color( 0x000000 );
camera = new THREE.PerspectiveCamera( 45, aspect, 1, 1000000 );
camera.position.set(0, 0, 300);
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.enableZoom = true;
controls.enabled = true;
controls.target.set(0, 0, 0);
//------End three.js setup-------
cube = new Cube();
scene.add(cube.box);
}//-------End init----------
function animate() {
requestAnimationFrame( animate );
render();
}//-------End animate----------
function render() {
var distance = camera.position.distanceTo( cube.box.position );
cube.update(distance);
camera.updateMatrixWorld();
camera.updateProjectionMatrix();
renderer.render(scene, camera);
}//-------End render----------
class Cube{
constructor(){
this.texture;
this.texstatus = false;
const geometry = new THREE.BoxGeometry( 10, 10, 10 );
const material = new THREE.MeshBasicMaterial( {
color: 0x00ff00,
map: null
} );
this.box = new THREE.Mesh( geometry, material );
}//end constructor
update(distToCam){
if(distToCam <= 100 && this.texstatus == false){
var loader = new THREE.TextureLoader();
this.texture = loader.load("grass.jpg");
this.box.material.map = this.texture;
this.texstatus = true;
}
if(distToCam > 100 && this.texstatus == true){
//prvious solution deactivated
//this.texture.dispose();
//this.texstatus = false;
//new solution
this.box.material.map = null;
this.texture.dispose();
this.texstatus = false;
this.texture = undefined;
}
this.box.material.needsUpdate = true;
}//end update
}//end Cube class
I have a post processing bloom shader running and a VR (virtual reality) conditional stereo effect. They don't seem to want to work together. I am likely just implementing this wrong. Here is the code but where is the mistake?
In my Init()
effectPass = new THREE.ShaderPass(THREE.CopyShader);
bloomPass = new THREE.BloomPass(2);
renderPass = new THREE.RenderPass( scene, camera );
composer = new THREE.EffectComposer(renderer);
composer.addPass(renderPass);
composer.addPass(bloomPass);
composer.addPass(effectPass);
effectPass.renderToScreen = true;
renderer.autoClear = false;
if (vr) {
stereo = new THREE.StereoEffect(renderer);
stereo.setSize( window.innerWidth, window.innerHeight );
}
In my render()
renderer.clear();
composer.render();
renderFrame = requestAnimationFrame(render);
if (vr) { stereo.render(scene, camera); }
else { renderer.render(scene, camera); }
While testing code in three.js, animation is not needed. I can see the first rendered frame. This would use less power, and stop the fan on the laptop from kicking in as the graphics card heats up.
The examples in three.js have the following structure:
function init() {
animate()
}
function animate() {
requestAnimationFrame( animate );
render();
targetRotation = targetRotationOnMouseDown + ( mouseX - mouseXOnMouseDown ) * 0.05;
}
function render() {
group.rotation.y += ( targetRotation - group.rotation.y ) * 0.05;
renderer.render( scene, camera );
}
I do not see how to turn off the repeated calls to animate.
Is there any easy way to turn off animation?
You do not have to have an animation loop in three.js -- say, if you have a static scene. Just call
renderer.render( scene, camera );
You will need to re-render whenever the camera moves or when loaders finish loading models or loading textures.
If you are using OrbitControls with a static scene, you can instantiate OrbitControls like so:
controls = new THREE.OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render ); // use if there is no animation loop
where render() calls renderer.render( scene, camera ).
If you are loading models or textures, the three.js loaders have a callback function you can specify. For example,
var loader = new THREE.TextureLoader();
var texture = loader.load( 'myTexture.jpg', render );
Also
var manager = new THREE.LoadingManager();
var loader = new THREE.OBJLoader( manager );
loader.load( 'myModel.obj', function( object ) {
// your code...
render();
}
three.js r.75
I have a THREE.Group(); in my scene and at runtime i want to add more to this group but it doesn't get updatet or better i cannot see the sprite that i added on runtime. The sprite is in the group but not rendered.
How can i update a THREE.Group();?
Example Code to clarify my problem. Not really running i know.
var systemGroup = new THREE.Group();
init();
animate();
function init() {
//add something to systemGroup like multiple meshes
systemGroup.add(mesh);
scene.add(systemGroup);
}
function render() {
scene.updateMatrixWorld();
renderer.render( scene, camera );
}
function animate() {
requestAnimationFrame( animate );
render();
}
function addMore(x,y,z){
var map = THREE.ImageUtils.loadTexture( "sprite.png" );
var material = new THREE.SpriteMaterial( { map: map, color: 0xffffff, fog: true } );
var sprite = new THREE.Sprite( material );
sprite.position.set(x,y,z);
systemGroup.add(sprite);
//Here how do i update the group so that the mesh is in the scene?
}
Thank you.
The code you are showing is adding the same mesh over and over again.
As the mesh is already in the group, it doesn't change anything.
What you seem to want is to clone() the mesh and add it at a different position.
Like this:
var group = new THREE.Group()
scene.add(group);
document.body.addEventListener('click', function(evt) {
var clone = mesh.clone()
clone.position.set(-100 + Math.random()*200, 0, 0)
group.add(mesh)
})
No need to update the group with anything else.
I originally had an animate function in place for my three.js scene that is loaded within an AngularJS Modal, but found that after closing the Modal, the animation keeps going, and that is unneeded since I don't require constant animation like a video game would have.
At this point, I switched it to only render when someone uses the OrbitControls to move the simple box in my example, and have an initial call to render the scene so that users can see the box instead of a big blacked out square.
However, upon initial render, the texture does not appear to be applied until I use the orbit controls and move the box, at which point they appear. This is odd, since both my initial call and the listener tied to the OrbitControls are to the same function. How do I get the initial load to show the texture?
$scope.generate3D = function () {
// 3D OBJECT - Variables
var texture0 = baseBlobURL + 'Texture_0.png';
var boxDAE = baseBlobURL + 'Box.dae';
var scene;
var camera;
var renderer;
var box;
var controls;
var newtexture;
// Update texture
newtexture = THREE.ImageUtils.loadTexture(texture0);
//Instantiate a Collada loader
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true;
loader.load(boxDAE, function (collada) {
box = collada.scene;
box.traverse(function (child) {
if (child instanceof THREE.SkinnedMesh) {
var animation = new THREE.Animation(child, child.geometry.animation);
animation.play();
}
});
box.scale.x = box.scale.y = box.scale.z = .2;
box.updateMatrix();
init();
// Initial call to render scene, from this point, Orbit Controls render the scene per the event listener
render();
});
function init() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
renderer = new THREE.WebGLRenderer();
renderer.setClearColor(0xdddddd);
//renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setSize(500, 500);
// Load the box file
scene.add(box);
// Lighting
var light = new THREE.AmbientLight();
scene.add(light);
// Camera
camera.position.x = 40;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
// Rotation Controls
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.addEventListener('change', render);
controls.rotateSpeed = 5.0;
controls.zoomSpeed = 5;
controls.noZoom = false;
controls.noPan = false;
var myEl = angular.element(document.querySelector('#webGL-container'));
myEl.append(renderer.domElement);
}
function render() {
renderer.render(scene, camera);
console.log('loaded');
}
}
You are using ColladaLoader and you want to force a call to render() when the model and all the textures are loaded.
If you add the model to the scene in the loader callback, there is still a chance that even though the model has loaded, the textures may not have.
One thing you can do is add the following before instantiating the loader:
THREE.DefaultLoadingManager.onLoad = function () {
// console.log( 'everything loaded' ); // debug
render();
};
Or alternatively,
THREE.DefaultLoadingManager.onProgress = function ( item, loaded, total ) {
// console.log( item, loaded, total ); // debug
if ( loaded === total ) render();
};
three.js r.72