Three.js 具有光线投射和相交测试的鼠标事件
Three.js mouse events with raycasting and intersect testing
我是一个完整的 Three.js 新手,总体上是 javascript 的新手。我正在尝试测试一些鼠标事件交互。我尝试了很多东西,但无法进行任何互动,所以我只是从下一页中抓取一些 Javascript,调整它使其成为 'MouseDown',而不是 'MouseOver'事件并通常对其进行简化,以便没有旋转等:-
https://threejs.org/examples/#webgl_interactive_cubes
但是,生成的页面有一个我不明白的问题。每当页面刷新时,立方体就会自动变成红色,但这应该只有在点击它时才会发生。如果我减小立方体的大小,那么问题就解决了。
我做了一个 console.log(INTERSECTIONS)
并发现刷新时该值为“5”,但此后每次我单击多维数据集时,该值为“1”。因为我实际上还没有点击任何东西,所以我希望刷新时该值为“0”?知道了这一点,我可以轻松地添加一个额外的 'if' 语句,如下所示:
if (intersects.length > 4)
{
INTERSECTED.material.emissive.setHex( 0x111111 );
}
这在一定程度上解决了问题,但我仍然想知道为什么首先会出现此问题?
我如何进一步简化此代码,以便我只获得一个基本模板,我可以从中创建未来的鼠标单击事件类型页面?例如,如果我希望立方体在点击时旋转,目前这似乎不起作用?
代码如下:
<script src="js/three.js"></script>
<script>
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100;
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';
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0xffffff, 0.2 ) );
var light = new THREE.DirectionalLight( 0xffffff, 2 );
light.position.set( 30, 10, 1 ).normalize();
scene.add( light );
var cubeGeometry = new THREE.BoxGeometry(20,20,20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0x999999, wireframe: false});
var object = new THREE.Mesh(cubeGeometry, cubeMaterial);
object.position.x = 0;
object.position.y = 0;
object.position.z = 0;
scene.add( object );
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function animate() {
requestAnimationFrame( animate );
render();
}
camera.position.x = -30;
camera.position.y = 30;
camera.position.z = 30;
camera.lookAt( scene.position );
camera.updateMatrixWorld();
function render()
{
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
console.log(intersects.length);
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
renderer.render( scene, camera );
}
</script>
当您声明 var mouse = new THREE.Vector2()
时,这意味着您创建了一个值为 {x:0, y:0}
的矢量(这意味着您的鼠标位于屏幕中央)。然后用 init()
初始化场景,在那里设置立方体,然后用 animate()
.
开始渲染
现在,将您的鼠标放在屏幕中央,并在每个渲染器上(在您的 render()
函数中)检查交叉点。这意味着当您的 mouse
在屏幕中间时,您将获得正交的结果。当你点击立方体外的某个地方时,你设置了一个新的鼠标点,因此相交结果是负数。
如果您想通过鼠标点击进行交互,那么最好将检查交集的代码块从 render()
移动到 onDocumentMouseDown(event)
。
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
console.log(intersects.length);
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
所以只有在单击场景时才会检查交叉点。
jsfiddle 例子
我是一个完整的 Three.js 新手,总体上是 javascript 的新手。我正在尝试测试一些鼠标事件交互。我尝试了很多东西,但无法进行任何互动,所以我只是从下一页中抓取一些 Javascript,调整它使其成为 'MouseDown',而不是 'MouseOver'事件并通常对其进行简化,以便没有旋转等:-
https://threejs.org/examples/#webgl_interactive_cubes
但是,生成的页面有一个我不明白的问题。每当页面刷新时,立方体就会自动变成红色,但这应该只有在点击它时才会发生。如果我减小立方体的大小,那么问题就解决了。
我做了一个 console.log(INTERSECTIONS)
并发现刷新时该值为“5”,但此后每次我单击多维数据集时,该值为“1”。因为我实际上还没有点击任何东西,所以我希望刷新时该值为“0”?知道了这一点,我可以轻松地添加一个额外的 'if' 语句,如下所示:
if (intersects.length > 4)
{
INTERSECTED.material.emissive.setHex( 0x111111 );
}
这在一定程度上解决了问题,但我仍然想知道为什么首先会出现此问题?
我如何进一步简化此代码,以便我只获得一个基本模板,我可以从中创建未来的鼠标单击事件类型页面?例如,如果我希望立方体在点击时旋转,目前这似乎不起作用?
代码如下:
<script src="js/three.js"></script>
<script>
var camera, scene, raycaster, renderer;
var mouse = new THREE.Vector2(), INTERSECTED;
var radius = 100;
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';
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10000 );
scene = new THREE.Scene();
scene.add( new THREE.AmbientLight( 0xffffff, 0.2 ) );
var light = new THREE.DirectionalLight( 0xffffff, 2 );
light.position.set( 30, 10, 1 ).normalize();
scene.add( light );
var cubeGeometry = new THREE.BoxGeometry(20,20,20);
var cubeMaterial = new THREE.MeshLambertMaterial({color: 0x999999, wireframe: false});
var object = new THREE.Mesh(cubeGeometry, cubeMaterial);
object.position.x = 0;
object.position.y = 0;
object.position.z = 0;
scene.add( object );
raycaster = new THREE.Raycaster();
renderer = new THREE.WebGLRenderer();
renderer.setClearColor( 0xf0f0f0 );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.sortObjects = false;
container.appendChild(renderer.domElement);
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
function onDocumentMouseDown( event ) {
event.preventDefault();
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function animate() {
requestAnimationFrame( animate );
render();
}
camera.position.x = -30;
camera.position.y = 30;
camera.position.z = 30;
camera.lookAt( scene.position );
camera.updateMatrixWorld();
function render()
{
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
console.log(intersects.length);
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
renderer.render( scene, camera );
}
</script>
当您声明 var mouse = new THREE.Vector2()
时,这意味着您创建了一个值为 {x:0, y:0}
的矢量(这意味着您的鼠标位于屏幕中央)。然后用 init()
初始化场景,在那里设置立方体,然后用 animate()
.
现在,将您的鼠标放在屏幕中央,并在每个渲染器上(在您的 render()
函数中)检查交叉点。这意味着当您的 mouse
在屏幕中间时,您将获得正交的结果。当你点击立方体外的某个地方时,你设置了一个新的鼠标点,因此相交结果是负数。
如果您想通过鼠标点击进行交互,那么最好将检查交集的代码块从 render()
移动到 onDocumentMouseDown(event)
。
// find intersections
raycaster.setFromCamera( mouse, camera );
var intersects = raycaster.intersectObjects( scene.children );
if ( intersects.length > 0 ) {
if ( INTERSECTED != intersects[ 0 ].object ) {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );
console.log(intersects.length);
}
} else {
if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );
INTERSECTED = null;
}
所以只有在单击场景时才会检查交叉点。
jsfiddle 例子