Three.js fragment shader color transition - three.js

I'm trying to animate the color transition for ShaderMaterial.
I want to have 5 colors, and switch them every few seconds for a whole object (wave of particles).
Here is my JS code
var material = new THREE.ShaderMaterial( {
uniforms: {
color: { value: new THREE.Color('violet') },
},
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent
});
And fragment shader part:
uniform vec3 color;
void main() {
if ( length( gl_PointCoord - vec2( 0.5, 0.5 ) ) > 0.475 ) discard;
gl_FragColor = vec4(color, 1.0 );
}
I have just a little idea of how three.js works. Any suggestions highly appreciated! :)
All code is here: codepen.io/agrhff/pen/MWKEqQy

As an option, you can use setInterval():
body {
background-color: black;
overflow: hidden;
margin: 0;
}
<script type="x-shader/x-vertex" id="vertexshader">
attribute float scale;
void main() {
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
gl_PointSize = scale * ( 300.0 / - mvPosition.z );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script type="x-shader/x-fragment" id="fragmentshader">
uniform vec3 color;
void main() {
if ( length( gl_PointCoord - 0.5 ) > 0.475 ) discard;
gl_FragColor = vec4(color, 1.0 );
}
</script>
<script type="module">
import * as THREE from 'https://threejs.org/build/three.module.js';
import Stats from 'https://threejs.org/examples/jsm/libs/stats.module.js';
var SEPARATION = 100, AMOUNTX = 150, AMOUNTY = 100;
var container, stats;
var camera, scene, renderer;
var particles, count = 0;
var mouseX = 0, mouseY = -500;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
var colors = [
new THREE.Color(0xffff00),
new THREE.Color(0xff00ff),
new THREE.Color(0x00ffff),
new THREE.Color(0xffffff),
new THREE.Color(0x888888)
];
var colIdx = 0;
init();
animate();
function init() {
container = document.createElement( 'div' );
container.classList.add("mystyle");
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.z = 1000;
scene = new THREE.Scene();
//
var numParticles = AMOUNTX * AMOUNTY;
var positions = new Float32Array( numParticles * 3 );
var scales = new Float32Array( numParticles );
var i = 0, j = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
positions[ i ] = ix * SEPARATION - ( ( AMOUNTX * SEPARATION ) / 2 ); // x
positions[ i + 1 ] = 0; // y
positions[ i + 2 ] = iy * SEPARATION - ( ( AMOUNTY * SEPARATION ) / 2 ); // z
scales[ j ] = 1;
i += 3;
j ++;
}
}
var geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.BufferAttribute( positions, 3 ) );
geometry.setAttribute( 'scale', new THREE.BufferAttribute( scales, 1 ) );
var material = new THREE.ShaderMaterial( {
uniforms: {
color: { value: new THREE.Color('violet') }
},
vertexShader: document.getElementById( 'vertexshader' ).textContent,
fragmentShader: document.getElementById( 'fragmentshader' ).textContent
} );
//
particles = new THREE.Points( geometry, material);
scene.add( particles );
//
renderer = new THREE.WebGLRenderer( { antialias: true, alpha: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
// change the colours, one a second
setInterval(function(){
colIdx = (colIdx + 1) % 5;;
material.uniforms.color.value.copy(colors[colIdx]);
}, 1000);
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
// mouseY = event.clientY - windowHalfY;
}
function onDocumentTouchStart( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
// mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
mouseX = event.touches[ 0 ].pageX - windowHalfX;
// mouseY = event.touches[ 0 ].pageY - windowHalfY;
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y );
camera.lookAt( scene.position );
var positions = particles.geometry.attributes.position.array;
var scales = particles.geometry.attributes.scale.array;
var i = 0, j = 0;
for ( var ix = 0; ix < AMOUNTX; ix ++ ) {
for ( var iy = 0; iy < AMOUNTY; iy ++ ) {
positions[ i + 1 ] = ( Math.sin( ( ix + count ) * 0.3 ) * 50 ) +
( Math.sin( ( iy + count ) * 0.5 ) * 50 );
scales[ j ] = ( Math.sin( ( ix + count ) * 0.3 ) + 1 ) * 8 +
( Math.sin( ( iy + count ) * 0.5 ) + 1 ) * 8;
i += 3;
j ++;
}
}
particles.geometry.attributes.position.needsUpdate = true;
particles.geometry.attributes.scale.needsUpdate = true;
renderer.render( scene, camera );
count += 0.1;
}
</script>

Related

InstancedMesh with unique texture per instance

I am looking for a way to draw several objects with unique textures. I came across this old question about instancedMesh where someone got the multiple instances with different textures but on desktop, textures have weird artifacts. Initially I thought something must be wrong with that demo but everything seems fine to me, I also tried to use mix functions in place of conditionals but textures still have artifacts.
I have been looking for different ways to draw multiple unique geometries so merging geometries isn't an option, but most results I get are for multiple objects with merged geometry. Would be great if someone can offer some guidance.
var camera, scene, renderer, stats;
var mesh;
var amount = parseInt( window.location.search.substr( 1 ) ) || 10;
var count = Math.pow( amount, 3 );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2( 1, 1 );
var rotationTheta = 0.1;
var rotationMatrix = new THREE.Matrix4().makeRotationY( rotationTheta );
var instanceMatrix = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( amount, amount, amount );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xffffff, 0x000088 );
light.position.set( - 1, 1.5, 1 );
scene.add( light );
var light = new THREE.HemisphereLight( 0xffffff, 0x880000, 0.5 );
light.position.set( - 1, - 1.5, - 1 );
scene.add( light );
var geometry = new THREE.BoxBufferGeometry( .5, .5, .5, 1, 1, 1 );
var material = [
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/square-outline-textured.png' ) } ),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/golfball.jpg' ) } ),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/metal.jpg' ) } ),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/roughness_map.jpg' ) } ),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/tri_pattern.jpg' ) } ),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/water.jpg' ) } ),
];
material.forEach((m,side)=>{
if ( side!=2 ) return;
m.onBeforeCompile = ( shader ) => {
shader.uniforms.textures = { 'type': 'tv', value: [
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/crate.gif' ),
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/equirectangular.png' ),
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/colors.png' )
] };
shader.vertexShader = shader.vertexShader.replace(
'#define STANDARD',
`#define STANDARD
varying vec3 vTint;
varying float vTextureIndex;`
).replace(
'#include <common>',
`#include <common>
attribute vec3 tint;
attribute float textureIndex;`
).replace(
'#include <project_vertex>',
`#include <project_vertex>
vTint = tint;
vTextureIndex=textureIndex;`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#define STANDARD',
`#define STANDARD
uniform sampler2D textures[3];
varying vec3 vTint;
varying float vTextureIndex;`
)
.replace(
'#include <fog_fragment>',
`#include <fog_fragment>
int texIdx = int(vTextureIndex);
vec4 col;
if (texIdx == 0) {
col = texture2D(textures[0], vUv );
} else if ( texIdx==1) {
col = texture2D(textures[1], vUv );
} else if ( texIdx==2) {
col = texture2D(textures[2], vUv );
}
gl_FragColor = col;
// gl_FragColor.rgb *= vTint;`
)
;
}
});
mesh = new THREE.InstancedMesh( geometry, material, count );
var i = 0;
var offset = ( amount - 1 ) / 2;
var transform = new THREE.Object3D();
var textures = [];
for ( var x = 0; x < amount; x ++ ) {
for ( var y = 0; y < amount; y ++ ) {
for ( var z = 0; z < amount; z ++ ) {
transform.position.set( offset - x, offset - y, offset - z );
transform.updateMatrix();
mesh.setMatrixAt( i ++, transform.matrix );
textures.push(Math.random()<0.3 ? 0 : (Math.random()<0.5 ? 1 : 2));
}
}
}
geometry.setAttribute( 'textureIndex',
new THREE.InstancedBufferAttribute( new Float32Array(textures), 1 ) );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
new THREE.OrbitControls( camera, renderer.domElement );
stats = new Stats();
document.body.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
document.addEventListener( 'mousemove', onMouseMove, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
raycaster.setFromCamera( mouse, camera );
var intersection = raycaster.intersectObject( mesh );
// console.log('intersection', intersection.length);
if ( intersection.length > 0 ) {
mesh.getMatrixAt( intersection[ 0 ].instanceId, instanceMatrix );
matrix.multiplyMatrices( instanceMatrix, rotationMatrix );
mesh.setMatrixAt( intersection[ 0 ].instanceId, matrix );
mesh.instanceMatrix.needsUpdate = true;
}
renderer.render( scene, camera );
stats.update();
}
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
<script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
I believe your issue comes from converting a float to an int, and then using that to create branches. This bug shows up only in a few GPUs, not all of them. I got it to work by keeping vTextureIndex as float, sampling all 3 textures and multiplying each by 1 if the textureIndex matches, or multiplying by 0 if the textureIndex does not match.
I basically replaced these lines:
int texIdx = int(vTextureIndex);
vec4 col;
if (texIdx == 0) {
col = texture2D(textures[0], vUv );
} else if ( texIdx==1) {
col = texture2D(textures[1], vUv );
} else if ( texIdx==2) {
col = texture2D(textures[2], vUv );
}
with this approach:
float x = vTextureIndex;
vec4 col;
col = texture2D(textures[0], vUv ) * step(-0.1, x) * step(x, 0.1);
col += texture2D(textures[1], vUv ) * step(0.9, x) * step(x, 1.1);
col += texture2D(textures[2], vUv ) * step(1.9, x) * step(x, 2.1);
If textureIndex is 0, the first texture is multiplied by 1, the others by 0
If textureIndex is 1, the second texture is multiplied by 1, the others by 0
If textureIndex is 2, the third texture is multiplied by 1, the others by 0
var camera, scene, renderer, stats;
var mesh;
var amount = parseInt( window.location.search.substr( 1 ) ) || 10;
var count = Math.pow( amount, 3 );
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2( 1, 1 );
var rotationTheta = 0.1;
var rotationMatrix = new THREE.Matrix4().makeRotationY( rotationTheta );
var instanceMatrix = new THREE.Matrix4();
var matrix = new THREE.Matrix4();
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( amount, amount, amount );
camera.lookAt( 0, 0, 0 );
scene = new THREE.Scene();
var light = new THREE.HemisphereLight( 0xffffff, 0x666666 );
light.position.set( - 1, 1.5, 1 );
scene.add( light );
var light = new THREE.HemisphereLight( 0xffffff, 0x666666, 0.5 );
light.position.set( - 1, - 1.5, - 1 );
scene.add( light );
var geometry = new THREE.BoxBufferGeometry( .5, .5, .5, 1, 1, 1 );
var material = [
new THREE.MeshStandardMaterial({color: 0xff9900}),
new THREE.MeshStandardMaterial({color: 0xff0099}),
new THREE.MeshStandardMaterial( { map: new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/metal.jpg' ) } ),
new THREE.MeshStandardMaterial({color: 0x9900ff}),
new THREE.MeshStandardMaterial({color: 0x0099ff}),
new THREE.MeshStandardMaterial({color: 0x99ff00}),
];
material.forEach((m,side)=>{
if ( side!=2 ) return;
m.onBeforeCompile = ( shader ) => {
shader.uniforms.textures = { 'type': 'tv', value: [
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/crate.gif' ),
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/sprite0.png' ),
new THREE.TextureLoader().load( 'https://threejs.org/examples/textures/sprite.png' )
] };
shader.vertexShader = shader.vertexShader.replace(
'#define STANDARD',
`#define STANDARD
varying vec3 vTint;
varying float vTextureIndex;`
).replace(
'#include <common>',
`#include <common>
attribute vec3 tint;
attribute float textureIndex;`
).replace(
'#include <project_vertex>',
`#include <project_vertex>
vTint = tint;
vTextureIndex=textureIndex;`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#define STANDARD',
`#define STANDARD
uniform sampler2D textures[3];
varying vec3 vTint;
varying float vTextureIndex;`
)
.replace(
'#include <fog_fragment>',
`#include <fog_fragment>
float x = vTextureIndex;
vec4 col;
col = texture2D(textures[0], vUv ) * step(-0.1, x) * step(x, 0.1);
col += texture2D(textures[1], vUv ) * step(0.9, x) * step(x, 1.1);
col += texture2D(textures[2], vUv ) * step(1.9, x) * step(x, 2.1);
gl_FragColor = col;
`
)
;
}
});
mesh = new THREE.InstancedMesh( geometry, material, count );
var i = 0;
var offset = ( amount - 1 ) / 2;
var transform = new THREE.Object3D();
var textures = [];
for ( var x = 0; x < amount; x ++ ) {
for ( var y = 0; y < amount; y ++ ) {
for ( var z = 0; z < amount; z ++ ) {
transform.position.set( offset - x, offset - y, offset - z );
transform.updateMatrix();
mesh.setMatrixAt( i ++, transform.matrix );
textures.push(Math.random()<0.3 ? 0 : (Math.random()<0.5 ? 1 : 2));
}
}
}
geometry.setAttribute( 'textureIndex',
new THREE.InstancedBufferAttribute( new Float32Array(textures), 1 ) );
scene.add( mesh );
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
new THREE.OrbitControls( camera, renderer.domElement );
stats = new Stats();
document.body.appendChild( stats.dom );
window.addEventListener( 'resize', onWindowResize, false );
document.addEventListener( 'mousemove', onMouseMove, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onMouseMove( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function render() {
raycaster.setFromCamera( mouse, camera );
var intersection = raycaster.intersectObject( mesh );
// console.log('intersection', intersection.length);
if ( intersection.length > 0 ) {
mesh.getMatrixAt( intersection[ 0 ].instanceId, instanceMatrix );
matrix.multiplyMatrices( instanceMatrix, rotationMatrix );
mesh.setMatrixAt( intersection[ 0 ].instanceId, matrix );
mesh.instanceMatrix.needsUpdate = true;
}
renderer.render( scene, camera );
stats.update();
}
<script src="https://cdn.jsdelivr.net/npm/three#0.140.0/build/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.140.0/examples/js/libs/stats.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three#0.140.0/examples/js/controls/OrbitControls.js"></script>

three.js, tween camera and mousemove event

I'm trying my hands on three.js
I am moving the camera using a tween, and it works quite good.
At the end of the animation, however, the camera jumps back to its initial position.
I found out that the mousemove event was causing that behavior.
How can i fix this problem and keep both the tween movement and the mouse move?
I have constructed my three.js based on this example;
Mousemove declared inside render function
function render() {
camera.position.x += ( mouseX - camera.position.x ) * 0.04;
camera.position.y += ( - mouseY - camera.position.y ) * 0.04;
camera.lookAt( scene.position );
TWEEN.update();
renderer.render( scene, camera );
}
Tween movement
function setupTween (position, target, duration) {
TWEEN.removeAll();
new TWEEN.Tween (position)
.to (target, duration)
.easing (TWEEN.Easing.Quadratic.InOut)
.onUpdate (
function() {
// copy incoming position into camera position
camera.position.copy (position);
})
.start();
};
tween function source
UPDATE
Complete working code:
<script>
var container,
i,
camera,
scene,
renderer,
particles,
geometry,
materials = [],
color,
sprite,
size,
mouseX = 0,
mouseY = 0,
isTweening,
windowHalfX = window.innerWidth / 2,
windowHalfY = window.innerHeight / 2;
// +++++ three.js +++++
// +++++ +++++ +++++ +++++ +++++
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.5, 2000 );
camera.position.set (0,0,1900);
scene = new THREE.Scene();
scene.fog = new THREE.FogExp2( 0x000000, 0.0005 );
geometry = new THREE.Geometry();
var textureLoader = new THREE.TextureLoader();
for ( i = 0; i < 1000; i ++ ) {
var vertex = new THREE.Vector3();
vertex.x = Math.random() * 2000 - 1000;
vertex.y = Math.random() * 2000 - 1000;
vertex.z = Math.random() * 2000 - 1000;
geometry.vertices.push( vertex );
}
sprite = textureLoader.load( "circle.png" );
color = [0.90, 0.05, 0.8];
size = 8.5;
materials = new THREE.PointsMaterial( { size: size, map: sprite, blending: THREE.AdditiveBlending, depthTest: false, transparent : false } );
materials.color.setHSL( color[0], color[1], color[2] );
particles = new THREE.Points( geometry, materials );
scene.add( particles );
renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = event.clientX - windowHalfX;
mouseY = event.clientY - windowHalfY;
}
function animate() {
requestAnimationFrame( animate );
render();
}
function startTween() {
isTweening = false;
var target = new THREE.Vector3(getRandomNumber(), getRandomNumber(), getRandomNumber());
new TWEEN.Tween (camera.position.clone())
.to (target, 1000)
.easing (TWEEN.Easing.Quadratic.InOut)
.onUpdate( function() {
camera.position.copy(this);
})
.onStart ( function() {
isTweening = true;
})
.onComplete ( function() {
isTweening = false;
})
.start();
}
function getRandomNumber() {
// get a number between -1000 and -500 and 500 and 1000
return ( Math.random() * 500 + 500 ) * ( Math.random() < 0.5 ? -1 : 1 );
}
function render() {
if(!isTweening && (mouseX || mouseY)) {
// more a generic approach, not just transforming x and y (maybe it needs to be improved a bit)
var upVector = camera.up.clone().transformDirection(camera.matrix);
var forwardVector = new THREE.Vector3().subVectors(scene.position, camera.position).normalize();
var rightVector = new THREE.Vector3().crossVectors(forwardVector, upVector);
camera.translateOnAxis(rightVector, mouseX);
camera.translateOnAxis(upVector, -mouseY);
mouseX = mouseY = 0;
}
camera.lookAt( scene.position );
TWEEN.update();
renderer.render( scene, camera );
}
init();
animate();
setTimeout(function(){
startTween();
},2500);
</script>
I think, you should only update the position by the mousemove event, when it's not tweening. So you need to check if its currently tweening or not.
var isTweening = false;
new TWEEN.Tween (camera.position)
.to (target, duration)
.easing (TWEEN.Easing.Quadratic.InOut)
.onStart ( function() {
isTweening = true;
})
.onComplete ( function() {
isTweening = false;
})
.start();
// in your render loop
function render() {
if (!isTweening) {
camera.position.x += ( mouseX - camera.position.x ) * 0.04;
camera.position.y += ( - mouseY - camera.position.y ) * 0.04;
}
camera.lookAt( scene.position );
TWEEN.update();
renderer.render( scene, camera );
}
You don't need to set an onUpdate function and copy the new position to camera.position. You can just pass over camera.position to the tween and it will work.
EDIT:
I didn't see the link to example. Now, I know which kind of navigation is used (which is actually used in a lot of three.js examples). It's not the mousemove event that is causing your problem, it's this kind of calculating the new camera position (camera.position.x += ( mouseX - camera.position.x ) * 0.04;). So, I changed the code of the example a bit, especially the navigation. Here are the important parts:
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
function onDocumentMouseMove( event ) {
mouseX = (event.movementX * 0.5) || event.mozMovementX || event.webkitMovementX || 0;
mouseY = (event.movementY * 0.5) || event.mozMovementY || event.webkitMovementY || 0;
}
function render() {
if(!isTweening && (mouseX || mouseY)) {
// more a generic approach, not just transforming x and y (maybe it needs to be improved a bit)
var upVector = camera.up.clone().transformDirection(camera.matrix);
var forwardVector = new THREE.Vector3().subVectors(scene.position, camera.position).normalize();
var rightVector = new THREE.Vector3().crossVectors(forwardVector, upVector);
camera.translateOnAxis(rightVector, mouseX);
camera.translateOnAxis(upVector, -mouseY);
mouseX = mouseY = 0;
}
camera.lookAt( scene.position );
TWEEN.update();
}
function startTween() {
isTweening = false;
var target = new THREE.Vector3(getRandomNumber(), getRandomNumber(), getRandomNumber());
new TWEEN.Tween (camera.position.clone())
.to (target, 1000)
.easing (TWEEN.Easing.Quadratic.InOut)
.onUpdate( function() {
camera.position.copy(this);
})
.onStart ( function() {
isTweening = true;
})
.onComplete ( function() {
isTweening = false;
})
.start();
}
function getRandomNumber() {
// get a number between -1000 and -500 and 500 and 1000
return ( Math.random() * 500 + 500 ) * ( Math.random() < 0.5 ? -1 : 1 );
}
And you are right about TWEEN.onUpdate: you need to copy the new values to camera.position. My earlier approach do also work, but then all of the functionality of THREE.Vector3 gets lost. I didn't realize that until now.

How to control the data.gui.js that do not affect the object(Three.js r66)

I write a trackball to control the object rotation, everything goes fine. However, when I add the gui component to the program, and when I put the mouse to change the gui, the object is moving. Because my trackball project the screen coordinate to the virtual ball, when my mouse is on the gui component, it is still in the screen, and it makes the object move. How to avoid that? I try t find the reason about the trackball three.js has, and do not find the a result. Why his trackball do not affact the gui object?
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - geometry - cube</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/libs/stats.min.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var cube, plane;
var mouseDown = false;
var rotateStartP = new THREE.Vector3(0,0,1);
var rotateEndP = new THREE.Vector3(0,0,1);
var lastPosX;
var lastPosY;
var targetRotationY = 0;
var targetRotationX = 0;
var quater;
//var rotateQuaternion = new THREE.Quaternion();
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
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( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.y = 150;
camera.position.z = 500;
scene = new THREE.Scene();
// Cube
var geometry = new THREE.CubeGeometry( 200, 200, 200 );
for ( var i = 0; i < geometry.faces.length; i += 2 ) {
var hex = Math.random() * 0xffffff;
geometry.faces[ i ].color.setHex( hex );
geometry.faces[ i + 1 ].color.setHex( hex );
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );
cube = new THREE.Mesh( geometry, material );
cube.position.y = 150;
scene.add( cube );
// Plane
var geometry = new THREE.PlaneGeometry( 200, 200 );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
var material = new THREE.MeshBasicMaterial( { color: 0xe0e0e0, overdraw: 0.5 } );
plane = new THREE.Mesh( geometry, material );
scene.add( plane );
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//GUI
var controls = new function () {
this.xx = false;
this.yy = 512;
this.onChange = function () {
}
};
var gui = new dat.GUI();
gui.add(controls, 'xx').onChange(controls.onChange);
gui.add(controls, 'yy', 1, 10).step(1).onChange(controls.onChange);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
document.addEventListener( 'touchstart', onDocumentTouchStart, false );
document.addEventListener( 'touchmove', onDocumentTouchMove, false );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function onDocumentMouseDown( event ) {
event.preventDefault();
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
document.addEventListener( 'mouseout', onDocumentMouseOut, false );
mouseDown = true;
rotateStartP = projectOnTrackball(event.clientX, event.clientY);
}
function onDocumentMouseMove( event ) {
if(!mouseDown)
{
return;
}
rotateEndP = projectOnTrackball(event.clientX, event.clientY);
}
function getMouseOnScreen( pageX, pageY) {
return new THREE.Vector2.set(pageX / window.innerWidth ,pageY / window.innerHeight);
}
function projectOnTrackball(pageX, pageY) // The screen coordinate[(0,0)on the left-top] convert to the
//trackball coordinate [(0,0) on the center of the page]
{
var mouseOnBall = new THREE.Vector3();
mouseOnBall.set(
( pageX - window.innerWidth * 0.5 ) / (window.innerWidth * .5),
( window.innerHeight * 0.5 - pageY ) / ( window.innerHeight * .5),
0.0
);
var length = mouseOnBall.length();
if (length > 1.0) {
mouseOnBall.normalize();
}
else {
mouseOnBall.z = Math.sqrt(1.0 - length * length);
}
return mouseOnBall;
}
function rotateMatrix(rotateStart, rotateEnd)
{
var axis = new THREE.Vector3(),
quaternion = new THREE.Quaternion();
var angle = Math.acos( rotateStart.dot( rotateEnd ) / rotateStart.length() / rotateEnd.length() );
if ( angle )
{
axis.crossVectors( rotateStart, rotateEnd ).normalize();
angle *= 0.01; //Here we could define rotate speed
quaternion.setFromAxisAngle( axis, angle );
}
return quaternion;
}
function onDocumentMouseUp( event ) {
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
mouseDown = false;
rotateStartP = rotateEndP;
}
function onDocumentMouseOut( event ) {
document.removeEventListener( 'mousemove', onDocumentMouseMove, false );
document.removeEventListener( 'mouseup', onDocumentMouseUp, false );
document.removeEventListener( 'mouseout', onDocumentMouseOut, false );
}
function onDocumentTouchStart( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
/*
mouseXOnMouseDown = event.touches[ 0 ].pageX - windowHalfX;
targetRotationOnMouseDownX = targetRotationX;
mouseYOnMouseDown = event.touches[ 0 ].pageY - windowHalfY;
targetRotationOnMouseDownY = targetRotationY; */
}
}
function onDocumentTouchMove( event ) {
if ( event.touches.length === 1 ) {
event.preventDefault();
/*
mouseX = event.touches[ 0 ].pageX - windowHalfX;
targetRotationX = targetRotationOnMouseDownX + ( mouseX - mouseXOnMouseDown ) * 0.05;
mouseY = event.touches[ 0 ].pageY - windowHalfY;
targetRotationY = targetRotationOnMouseDownY + ( mouseY - mouseYOnMouseDown ) * 0.05; */
}
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
//if(rotateStartP != rotateEndP) {
//rotateQuaternion = rotateMatrix(rotateStartP, rotateEndP);
//quater=cube.quaternion;
//quater.multiplyQuaternions(rotateQuaternion, quater);
//quater.multiply(rotateQuaternion);
//quater.normalize();
var rotateQuaternion = rotateMatrix(rotateStartP, rotateEndP);
quater=cube.quaternion;
quater.multiplyQuaternions(rotateQuaternion,quater);
quater.normalize();
cube.setRotationFromQuaternion(quater);
// }
renderer.render( scene, camera );
}
</script>
</body>
</html>
Change my code to ObjectTrackball.js
The code is as follows:
TrackballControls = function ( object, domElement ) {
var _this = this;
_this.quater = object.quaternion;
_this.object = object;
_this.domElement = ( domElement !== undefined ) ? domElement : document;
_this.zoomValue = 0;
_this.mouseDown = true;
_this.rotateStartP = new THREE.Vector3();
_this.rotateEndP = new THREE.Vector3();
// events
var changeEvent = { type: 'change' };
// methods
this.handleEvent = function ( event ) {
if ( typeof this[ event.type ] == 'function' ) {
this[ event.type ]( event );
}
};
this.update = function () {
var rotateQuaternion = rotateMatrix(_this.rotateStartP, _this.rotateEndP);
_this.quater = _this.object.quaternion;
_this.quater.multiplyQuaternions(rotateQuaternion,_this.quater);
_this.quater.normalize();
_this.object.setRotationFromQuaternion(_this.quater);
_this.object.position.z += _this.zoomValue;
_this.zoomValue = 0;
};
function mousedown( event ) {
event.preventDefault();
_this.mouseDown = true;
_this.rotateStartP = projectOnTrackball(event.clientX, event.clientY);
document.addEventListener( 'mousemove', mousemove, false );
document.addEventListener( 'mouseup', mouseup, false );
}
function getMouseOnScreen( pageX, pageY) {
return new THREE.Vector2.set(pageX / window.innerWidth ,pageY / window.innerHeight);
}
function projectOnTrackball(pageX, pageY) // The screen coordinate[(0,0)on the left-top] convert to the
//trackball coordinate [(0,0) on the center of the page]
{
var mouseOnBall = new THREE.Vector3();
mouseOnBall.set(
( pageX - window.innerWidth * 0.5 ) / (window.innerWidth * .5),
( window.innerHeight * 0.5 - pageY ) / ( window.innerHeight * .5),
0.0
);
var length = mouseOnBall.length();
if (length > 1.0) {
mouseOnBall.normalize();
}
else {
mouseOnBall.z = Math.sqrt(1.0 - length * length);
}
return mouseOnBall;
}
function rotateMatrix(rotateStart, rotateEnd)
{
var axis = new THREE.Vector3(),
quaternion = new THREE.Quaternion();
var angle = Math.acos( rotateStart.dot( rotateEnd ) / rotateStart.length() / rotateEnd.length() );
if ( angle )
{
axis.crossVectors( rotateStart, rotateEnd ).normalize();
angle *= 0.01; //Here we could define rotate speed
quaternion.setFromAxisAngle( axis, angle );
}
return quaternion;
}
function mousemove( event ) {
if(!_this.mouseDown)
{
return;
}
_this.rotateEndP = projectOnTrackball(event.clientX, event.clientY);
}
function mouseup( event ) {
_this.mouseDown = false;
_this.rotateStartP = _this.rotateEndP;
document.removeEventListener( 'mousemove', mousemove );
document.removeEventListener( 'mouseup', mouseup );
}
function mousewheel( event ) {
event.preventDefault();
event.stopPropagation();
var delta = 0;
if ( event.wheelDelta ) { // WebKit / Opera / Explorer 9
delta = event.wheelDelta / 40;
} else if ( event.detail ) { // Firefox
delta = - event.detail / 3;
}
_this.zoomValue += delta;
}
this.domElement.addEventListener( 'mousedown', mousedown, false );
this.domElement.addEventListener( 'mousewheel', mousewheel, false );
this.domElement.addEventListener( 'DOMMouseScroll', mousewheel, false ); // firefox
};
TrackballControls.prototype = Object.create( THREE.EventDispatcher.prototype );
Code of The Object control application (Three.js r66)
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js canvas - geometry - cube</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #f0f0f0;
margin: 0px;
overflow: hidden;
}
</style>
</head>
<body>
<script src="../build/three.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/controls/ObjectTrackballControl.js"></script>
<script>
var container, stats;
var camera, scene, renderer;
var cube, plane;
var control;
var mouseDown = false;
var rotateStartP = new THREE.Vector3(0,0,1);
var rotateEndP = new THREE.Vector3(0,0,1);
var lastPosX;
var lastPosY;
var targetRotationY = 0;
var targetRotationX = 0;
var quater;
//var rotateQuaternion = new THREE.Quaternion();
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
init();
animate();
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( 70, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.y = 150;
camera.position.z = 500;
scene = new THREE.Scene();
// Cube
var geometry = new THREE.CubeGeometry( 200, 200, 200 );
for ( var i = 0; i < geometry.faces.length; i += 2 ) {
var hex = Math.random() * 0xffffff;
geometry.faces[ i ].color.setHex( hex );
geometry.faces[ i + 1 ].color.setHex( hex );
}
var material = new THREE.MeshBasicMaterial( { vertexColors: THREE.FaceColors, overdraw: 0.5 } );
cube = new THREE.Mesh( geometry, material );
cube.position.y = 150;
scene.add( cube );
control = new TrackballControls(cube);
// Plane
var geometry = new THREE.PlaneGeometry( 200, 200 );
geometry.applyMatrix( new THREE.Matrix4().makeRotationX( - Math.PI / 2 ) );
var material = new THREE.MeshBasicMaterial( { color: 0xe0e0e0, overdraw: 0.5 } );
plane = new THREE.Mesh( geometry, material );
scene.add( plane );
renderer = new THREE.CanvasRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
//GUI
var controls = new function () {
this.xx = false;
this.yy = 512;
this.onChange = function () {
}
};
var gui = new dat.GUI();
gui.add(controls, 'xx').onChange(controls.onChange);
gui.add(controls, 'yy', 1, 10).step(1).onChange(controls.onChange);
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
container.appendChild( stats.domElement );
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
control.update();
renderer.render( scene, camera );
}
</script>
</body>
</html>

Why find intersections does not work with THREE.UTF8Loader?

I clone https://github.com/mrdoob/three.js.git, and I did 2 changes for sample webgl_loader_utf8.html
Add CubeGeometry
Support find intersections
When I clicked Cubes can find intersections, but clicked utf8 models(for instance, ben or hand models) can't find intersections. Any ideas about this? Many thanks!
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - io - UTF8 loader</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
z-index: 100;
display:block;
}
#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
</style>
</head>
<body>
<div id="info">
three.js -
UTF8 format loader test -
models from The Utah 3D Animation Repository
<div id="show"></div>
</div>
<script src="../build/three.min.js"></script>
<script src="js/loaders/UTF8Loader.js"></script>
<script src="js/loaders/MTLLoader.js"></script>
<script src="js/Detector.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/libs/tween.min.js"></script>
<script>
var SCREEN_WIDTH = window.innerWidth;
var SCREEN_HEIGHT = window.innerHeight;
var FLOOR = -150;
var container, stats;
var camera, scene, renderer;
var projector, raycaster;
var mouse = new THREE.Vector2();
var mesh, zmesh, geometry;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
document.addEventListener('mousemove', onDocumentMouseMove, false);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
var show = document.getElementById("show");
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
camera = new THREE.PerspectiveCamera( 20, SCREEN_WIDTH / SCREEN_HEIGHT, 1, 2000 );
camera.position.z = 800;
scene = new THREE.Scene();
scene.fog = new THREE.Fog( 0x000000, 800, 2000 );
var path = "textures/cube/SwedishRoyalCastle/";
var format = '.jpg';
var urls = [
path + 'px' + format, path + 'nx' + format,
path + 'py' + format, path + 'ny' + format,
path + 'pz' + format, path + 'nz' + format
];
reflectionCube = THREE.ImageUtils.loadTextureCube( urls );
// LIGHTS
var ambient = new THREE.AmbientLight( 0x222222 );
scene.add( ambient );
var directionalLight = new THREE.DirectionalLight( 0xffffff, 1.1 );
directionalLight.position.set( 0, 20, 300 );
scene.add( directionalLight );
directionalLight.castShadow = true;
//directionalLight.shadowCameraVisible = true;
directionalLight.shadowMapWidth = 2048;
directionalLight.shadowMaHeight = 2048;
var d = 150;
directionalLight.shadowCameraLeft = -d * 1.2;
directionalLight.shadowCameraRight = d * 1.2;
directionalLight.shadowCameraTop = d;
directionalLight.shadowCameraBottom = -d;
directionalLight.shadowCameraNear = 200;
directionalLight.shadowCameraFar = 500;
// RENDERER
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setSize( SCREEN_WIDTH, SCREEN_HEIGHT );
renderer.setClearColor( scene.fog.color, 1 );
renderer.domElement.style.position = "relative";
container.appendChild( renderer.domElement );
//
/**
renderer.gammaInput = true;
renderer.gammaOutput = true;
renderer.physicallyBasedShading = true;
renderer.shadowMapEnabled = true;
renderer.shadowMapType = THREE.PCFShadowMap;
**/
// STATS
stats = new Stats();
stats.domElement.style.position = 'absolute';
stats.domElement.style.top = '0px';
stats.domElement.style.zIndex = 100;
container.appendChild( stats.domElement );
var start = Date.now();
var loader = new THREE.UTF8Loader();
loader.load( "models/utf8/hand.js", function ( object ) {
var end = Date.now();
console.log( "hand", end - start, "ms" );
var s = 350;
object.scale.set( s, s, s );
object.position.x = 125;
object.position.y = -125;
//scene.add( object );
object.traverse( function( node ) {
node.castShadow = true;
node.receiveShadow = true;
if ( node.material && node.material.name === "skin" ) {
node.material.wrapAround = true;
node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
}
} );
}, { normalizeRGB: true } );
loader.load( "models/utf8/ben_dds.js", function ( object ) {
var end = Date.now();
console.log( "ben", end - start, "ms" );
var s = 350;
object.scale.set( s, s, s );
object.position.x = -125;
object.position.y = -125;
scene.add( object );
object.traverse( function( node ) {
node.castShadow = true;
node.receiveShadow = true;
if ( node.material && ( node.material.name === "head" || node.material.name === "skinbody" ) ) {
node.material.wrapAround = true;
node.material.wrapRGB.set( 0.6, 0.2, 0.1 );
}
} );
}, { normalizeRGB: true } );
var geometry = new THREE.CubeGeometry( 20, 20, 20 );
for ( var i = 0; i < 20; i ++ ) {
var object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );
object.position.x = Math.random() * 800 - 400;
object.position.y = Math.random() * 800 - 400;
object.position.z = Math.random() * 800 - 400;
object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;
object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;
scene.add( object );
}
projector = new THREE.Projector();
raycaster = new THREE.Raycaster();
//
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove( event ) {
mouseX = ( event.clientX - windowHalfX );
mouseY = ( event.clientY - windowHalfY );
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
//
function animate() {
requestAnimationFrame( animate );
render();
stats.update();
}
function render() {
camera.position.x += ( mouseX - camera.position.x ) * .05;
camera.position.y += ( - mouseY - camera.position.y ) * .05;
camera.lookAt( scene.position );
renderer.render( scene, camera );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
show.innerText = "";
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var raycaster = new THREE.Raycaster( camera.position, vector.sub( camera.position ).normalize() );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
show.innerText = "intersects=" + intersects.length + " at " + Date.now();
console.log(intersects.length);
}
}
</script>
</body>
</html>
You need to add the recursive flag to raycaster.intersectObjects().
var intersects = raycaster.intersectObjects( scene.children, true );
three.js r.58

Three Js Object3D Button Group Detect Single Object Click While Mouse Movement Causes Object3D Button Group Zoomi

I am trying to detect a cube click in an Object3D group of Cubes. I have viewed, and tried to incorporate the examples and tutorials found at:
http://mrdoob.github.com/three.js/examples/webgl_interactive_cubes.html
and
http://mrdoob.github.com/three.js/examples/canvas_interactive_cubes.html
Also, I have consulted the posts on this site at:
Three.js - how to detect what shape was selected? after drag
and
how to Get CLICKED element in THREE.js
But for some reason, it's still not working. Can anyone please tell me what I'm doing wrong?
Here is my code, thanks:
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<title>Double Stamp It No Erasies</title>
<style>
html {
background: url(Images/ComicBookExplosionBackground.jpg) no-repeat center center fixed;
-webkit-background-size: cover;
-moz-background-size: cover;
-o-background-size: cover;
background-size: cover;
}
body {
}
</style>
<script src="ThreeJs/build/three.min.js"></script>
</head>
<body onLoad="onLoad();" style="">
<div id="container" style="width:100%; height:100%; position:absolute;"></div>
<script>
var container, ButtonsCamera, ButtonsScene, ButtonsRenderer, ButtonsGeometry, ButtonsGroup;
var mouseX = 0, mouseY = 0;
var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;
/****************************** CLICK START **********************************/
var mouse = { x: 0, y: 0 }, projector, INTERSECTED;
var objects = [];
/****************************** CLICK END **********************************/
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
//document.addEventListener( 'mousedown', onDocumentMouseDown, false );
init();
animate();
function init() {
container = document.createElement( 'div' );
document.body.appendChild( container );
ButtonsCamera = new THREE.PerspectiveCamera( 60, window.innerWidth / window.innerHeight, 1, 10000 );
ButtonsCamera.position.z = 500;
ButtonsScene = new THREE.Scene();
ButtonsScene.fog = new THREE.Fog( 0xffffff, 1, 10000 );
/*************************** STACKOVERFLOW 1ST ANSWER START **********************************/
var ButtonsGeometry = new THREE.CubeGeometry( 100, 100, 100 );
var ButtonsMaterial = new THREE.MeshFaceMaterial( [
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } ),
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } ),
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } ),
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } ),
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } ),
new THREE.MeshBasicMaterial( { map: THREE.ImageUtils.loadTexture( 'Images/Twitter.jpg' ) } )] );
/*************************** STACKOVERFLOW 1ST ANSWER END **********************************/
ButtonsGroup = new THREE.Object3D();
for ( var i = 0; i < 100; i ++ ) {
var ButtonsMesh;
if(i == 0)
{
ButtonsMesh = new THREE.Mesh( ButtonsGeometry, ButtonsMaterial );
}
else
{
ButtonsMesh = new THREE.Mesh( ButtonsGeometry, ButtonsMaterial );
}
ButtonsMesh.position.x = Math.random() * 2000 - 1000;
ButtonsMesh.position.y = Math.random() * 2000 - 1000;
ButtonsMesh.position.z = Math.random() * 2000 - 1000;
ButtonsMesh.rotation.x = Math.random() * 360 * ( Math.PI / 180 );
ButtonsMesh.rotation.y = Math.random() * 360 * ( Math.PI / 180 );
ButtonsMesh.matrixAutoUpdate = false;
ButtonsMesh.updateMatrix();
ButtonsGroup.add( ButtonsMesh );
}
ButtonsScene.add( ButtonsGroup );
/****************************** CLICK START **********************************/
objects.push( ButtonsMesh );
projector = new THREE.Projector();
/****************************** CLICK END **********************************/
ButtonsRenderer = new THREE.WebGLRenderer();
ButtonsRenderer.setSize( window.innerWidth, window.innerHeight );
ButtonsRenderer.sortObjects = false;
container.appendChild( ButtonsRenderer.domElement );
window.addEventListener( 'resize', onWindowResize, false );
/****************************** CLICK START **********************************/
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
/****************************** CLICK END **********************************/
}
/****************************** CLICK START **********************************/
function onDocumentMouseDown( event ) {
//alert('clicky');
event.preventDefault();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1, - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, ButtonsCamera );
var raycaster = new THREE.Raycaster( ButtonsCamera.position, vector.subSelf( ButtonsCamera.position ).normalize() );
var intersects = raycaster.intersectObjects( objects);
if ( intersects.length > 0 ) {
intersects[ 0 ].object.material.color.setHex( Math.random() * 0xffffff );
var particle = new THREE.Particle( particleMaterial );
particle.position = intersects[ 0 ].point;
particle.scale.x = particle.scale.y = 8;
ButtonsScene.add( particle );
}
/*
// Parse all the faces
for ( var i in intersects ) {
intersects[ i ].face.material[ 0 ].color.setHex( Math.random() * 0xffffff | 0x80000000 );
}
*/
}
/****************************** CLICK END **********************************/
function onWindowResize() {
windowHalfX = window.innerWidth / 2;
windowHalfY = window.innerHeight / 2;
ButtonsCamera.aspect = window.innerWidth / window.innerHeight;
ButtonsCamera.updateProjectionMatrix();
ButtonsRenderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseMove(event) {
mouseX = ( event.clientX - windowHalfX ) * 10;
mouseY = ( event.clientY - windowHalfY ) * 10;
}
function animate() {
requestAnimationFrame( animate );
render();
ButtonsStats.update();
}
/****************************** CLICK START **********************************/
var radius = 100;
var theta = 0;
/****************************** CLICK END **********************************/
function render() {
var ButtonsTime = Date.now() * 0.001;
var rx = Math.sin( ButtonsTime * 0.7 ) * 0.5,
ry = Math.sin( ButtonsTime * 0.3 ) * 0.5,
rz = Math.sin( ButtonsTime * 0.2 ) * 0.5;
ButtonsCamera.position.x += ( mouseX - ButtonsCamera.position.x ) * .05;
ButtonsCamera.position.y += ( - mouseY - ButtonsCamera.position.y ) * .05;
ButtonsCamera.lookAt( ButtonsScene.position );
ButtonsGroup.rotation.x = rx;
ButtonsGroup.rotation.y = ry;
ButtonsGroup.rotation.z = rz;
ButtonsRenderer.render( ButtonsScene, ButtonsCamera );
}
</script>
</body>
</html>
Hey I hope I am not too late but anyway the solution to your problem is
a misplaced statement and a deprecated method.
You only have one object in your objects array that is why when you click on a
random box the raycaster is unlikely to detect an intersection.
Move the array push call into the for loop in order to add every object into
the array instead of the last object created.
for (var i = 0; i < 100; i++) {
var ButtonsMesh;
if (i == 0)
{
ButtonsMesh = new THREE.Mesh(ButtonsGeometry, ButtonsMaterial);
}
else
{
ButtonsMesh = new THREE.Mesh(ButtonsGeometry, ButtonsMaterial);
}
ButtonsMesh.position.x = Math.random() * 2000 - 1000;
ButtonsMesh.position.y = Math.random() * 2000 - 1000;
ButtonsMesh.position.z = Math.random() * 2000 - 1000;
ButtonsMesh.rotation.x = Math.random() * 360 * (Math.PI / 180);
ButtonsMesh.rotation.y = Math.random() * 360 * (Math.PI / 180);
ButtonsMesh.matrixAutoUpdate = false;
ButtonsMesh.updateMatrix();
ButtonsGroup.add(ButtonsMesh);
objects.push(ButtonsMesh);
}
The second problem is that newer versions of THREE don't use subSelf(),
it is replaced by sub(). So make the change to the Raycaster definition.
var raycaster = new THREE.Raycaster(ButtonsCamera.position,
vector.sub(ButtonsCamera.position).normalize());
That should solve your problems but there are more errors in your code
but they are all trivial.
I hope this helps and here is a working version: http://jsbin.com/uhihoq/1/edit
Peace!

Resources