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); }
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'm struggling to get an animation to play together with my GLTF 3D model. Most similar issues that I've seen on Stack Overflow are relating to the mixer not being updated. Which is not the problem in my case.
This is my fiddle: https://jsfiddle.net/rixi/djqz1nb5/11/
import * as THREE from "https://threejsfundamentals.org/threejs/resources/threejs/r122/build/three.module.js";
import { GLTFLoader } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/loaders/GLTFLoader.js";
import { OrbitControls } from "https://threejsfundamentals.org/threejs/resources/threejs/r132/examples/jsm/controls/OrbitControls.js";
let clock, controls, scene, camera, renderer, mixer, container, model;
initScene();
animate();
function initScene() {
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
clock = new THREE.Clock();
renderer = new THREE.WebGLRenderer();
controls = new OrbitControls(camera, renderer.domElement);
controls.update();
container = document.getElementById("container");
container.appendChild(renderer.domElement);
renderer.setSize(window.innerWidth, window.innerHeight);
}
scene.background = new THREE.Color("#f8edeb");
// LIGHT
const light = new THREE.DirectionalLight(0xffffff, 1);
light.position.set(2, 2, 5);
//HELPERS
const axesHelper = new THREE.AxesHelper(5);
let gridHelper = new THREE.GridHelper(30, 30);
scene.add(light, axesHelper, gridHelper);
//GLTF START
const GLTFloader = new GLTFLoader();
GLTFloader.load("https://richardlundquist.github.io/library/alice_TEST2.glb", function (gltf) {
model = gltf;
mixer = new THREE.AnimationMixer(gltf.scene);
mixer.clipAction(gltf.animations[0]).play();
scene.add(model.scene);
});
camera.position.set(0, 20, 50);
function animate() {
requestAnimationFrame(animate);
let delta = clock.getDelta();
if (mixer) {
mixer.update(clock.getDelta());
}
renderer.render(scene, camera);
}
There is no error in the console. The animation is listed in the array and plays as it should in Don McCurdy's GLTF viewer (https://gltf-viewer.donmccurdy.com/)
But for some reason it will not play in my three js setup. Any clues? I would be extremely grateful for any help or hints on how to solve the issue.
I found two critical errors here.
At the top of your code, you pull in Three r122 with GLTFLoader r132. These need to be the same revision. Try setting them all to r132.
You call getDelta() twice here:
let delta = clock.getDelta();
if (mixer) {
mixer.update(clock.getDelta());
}
The second call to getDelta() comes immediately after the first, so always returns zero delta time. Thus, the animation never moves forward.
I am trying to add touch controls to a three.js scene. I want to move the camera in whatever direction the user touches. It works great using the keyboard because you can press and hold the button and the camera moves continuously. But when I try the same thing using touchstart, you have to keep tapping the screen over and over to move, you can't just hold your finger down like on a keyboard or mouse.
I looked at touchmove, but if you just tap and hold without moving, there are no new touches.
Is there something similar to holding down the keyboard or mousekey using touch events?
There is no builtin callback for a touch event which fires repeatedly like the keyboard. You can, however, simply track the start and end of the touch and then call the move method at a set interval.
First, subscribe to the correct events and set a bool to track the state:
var isTouching = false;
window.addEventListener("touchstart", () => isTouching = true);
window.addEventListener("touchend", () => isTouching = false);
In Three.js you will most likely already have a render loop (e.g. a function called "animate"). Check the state variable at every iteration and apply the movement each time. You may need to also factor in deltaTime (the duration of the last frame), to make movement framerate independent.
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
if (isTouching) {
console.log("move camera");
}
renderer.render(scene, camera);
}
Here is a snippet which shows the basic approach. Click and hold in the left or right half of the output window to move the camera.
var camera, scene, renderer, mesh, material, clock;
init();
animate();
var isTouching = false;
var mousePositionX;
window.addEventListener("mousedown", (e) => {
isTouching = true;
mousePositionX = e.clientX;
});
window.addEventListener("mouseup", (e) => isTouching = false);
function init() {
renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
clock = new THREE.Clock();
camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 400;
scene = new THREE.Scene();
material = new THREE.MeshPhongMaterial();
var geometry = new THREE.BoxGeometry(200, 200, 200);
mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);
var light = new THREE.AmbientLight(0x404040);
scene.add(light);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(1, 1, 1).normalize();
scene.add(directionalLight);
window.addEventListener('resize', onWindowResize, false);
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.005;
mesh.rotation.y += 0.01;
let deltaTime = clock.getDelta();
if (isTouching) {
let speed = 200; // px per second
let movement = speed * deltaTime;
if (mousePositionX > window.innerWidth / 2) {
camera.translateX(-movement);
} else {
camera.translateX(movement);
}
}
renderer.render(scene, camera);
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
body {
padding: 0;
margin: 0;
}
canvas {
display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/93/three.min.js"></script>
I have a basic json scene exported from the three.js/editor. I want to add a repeated texture wrap to an object in this scene but I do not know how to do so since the only examples I have found add the wrap in the creation of the object.
I have already tried accessing the texture and giving it a wrap, but I think I might need to add a texture to the object from the JavaScript then declare the texture wrap instead of trying to add it to an already loaded texture.
<script>
var camera, scene, renderer;
var mesh;
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.z = 400;
scene = new THREE.Scene();
var objectLoader = new THREE.ObjectLoader();
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
});
renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
while (scene.getObjectByName('Box 1')){
var texture = scene.getObjectByName('Box 1').material;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
}
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function animate() {
requestAnimationFrame( animate );
if (scene.getObjectByName('Box 1') ){
scene.getObjectByName('Box 1').material.map.offset.x += 0.001;
}
renderer.render( scene, camera );
}
</script>
You need to set the wrap and repeat properties of the texture/map and not of the material. Docs also say it's important to set needsUpdate to true, if wrap settings changed. And you are trying to set the properties before the json is loaded (in a while loop?). You should do it within the load callback.
objectLoader.load( "models/cube.json", function ( obj ) {
scene.add( obj )
obj.traverse(function(child) {
if (child instanceof THREE.Mesh) {
child.castShadow = true;
child.receiveShadow = true;
}
});
obj.name = "cube";
obj.position.set(0,0,0);
obj.scale.set(200,200,200);
var box1 = scene.getObjectByName('Box 1');
if (box1) {
var texture = box1.material.map;
texture.wrapS = THREE.RepeatWrapping;
texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set( 2, 2 );
texture.needsUpdate = true;
}
});
I am trying to load a collada file using Threejs. All works well, but if I make the object spin I can see that the rendering is not right. Check the image:
Here is the code (which was partially stolen from another example):
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
var info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '10px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.innerHTML = 'Drag to spin the cube';
container.appendChild( info );
camera = new THREE.PerspectiveCamera( 20, window.innerWidth / window.innerHeight, .1, 10000 );
camera.position.x=50;
camera.position.y=50;
camera.position.z=50;
camera.lookAt(new THREE.Vector3(0,0,0));
scene = new THREE.Scene();
renderer = new THREE.CanvasRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
var ambientLight = new THREE.AmbientLight(0x000000);
scene.add(ambientLight);
var directionalLight = new THREE.DirectionalLight(0xffffff);
directionalLight.position.set(100,50,80).normalize();
scene.add(directionalLight);
var loader = new THREE.ColladaLoader();
loader.options.convertUpAxis = true; // this rotates so it looks right
loader.load('models/VM.dae', function (result) {
cube = result.scene;
// cube.doubleSided = true;
// cube.flipSided = true;
console.log(cube);
cube.updateMatrix();
scene.add(result.scene);
animate();
});
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
cube.rotation.y += 0.01;
// plane.rotation.y = cube.rotation.y += ( targetRotation - cube.rotation.y ) * 0.05;
renderer.render( scene, camera );
}
New update:
I changed the loader to a MTL+OBJ one. The result is exactly the same:
var loader = new THREE.OBJMTLLoader();
loader.addEventListener("load", function (event) {
cube = event.content;
cube.doubleSided = true;
console.log(event);
scene.add(cube);
animate();
});
loader.load ("models/VM.obj", "models/VM.mtl");
Fiddle: http://jsfiddle.net/qMqH7/
Your model is throwing errors in the Console, I would suggest you track them down.
What you are seeing is a known limitation of CanvasRenderer related to depth-sorting. It is made worse by your geometry which has several elongated faces. The model renders correctly with WebGLRenderer.
Also, object.doubleSided has been deprecated. It has been replaced by material.side = THREE.DoubleSide. It does not appear that flag is required in this case.
three.js r.58