THREE.js Ray Intersect не удается, добавив div
My Three.js script работает отлично, когда на странице есть только один целевой div (который содержит renderer.domElement). Как только я добавлю еще один div с фиксированной высотой и шириной выше целевого div, ray.intersectObjects возвращает null. Я сомневаюсь, что вектор, который я создаю для луча, вызывает проблему. Вот код.
var vector = new THREE.Vector3( ( event.clientX / divWidth ) * 2 - 1, -( event.clientY / divHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );
var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( myObjects, true );
Любые идеи о том, как я могу это решить.
EDIT: теперь THREE.Raycaster
(three.js r.56)
Ответы
Ответ 1
Короткий ответ - вы должны принять во внимание offset
холста.
Длительный ответ зависит от того, как написан ваш код, поэтому я дам вам два ответа, которые должны охватывать базы.
Существует много возможных комбинаций, поэтому вам, возможно, придется экспериментировать. Кроме того, разные браузеры могут действовать по-разному.
Предположим, что ваш HTML-код выглядит примерно так:
#canvas {
width: 200px;
height: 200px;
margin: 100px;
padding: 0px;
position: static; /* fixed or static */
top: 100px;
left: 100px;
}
<body>
<div id="canvas">
</body>
Ваш JS выглядит примерно так:
var CANVAS_WIDTH = 200,
CANVAS_HEIGHT = 200;
var container = document.getElementById( 'canvas' );
document.body.appendChild( container );
renderer = new THREE.WebGLRenderer();
renderer.setSize( CANVAS_WIDTH, CANVAS_HEIGHT );
container.appendChild( renderer.domElement );
Метод 1. Чтобы следующий метод работал правильно, установите положение холста static; margin > 0 и padding > 0 в порядке
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.clientHeight ) * 2 + 1;
Метод 2. Для этого альтернативного метода установите фиксированную позицию холста; set top > 0, установите left > 0; заполнение должно быть 0; margin > 0 в порядке
mouse.x = ( ( event.clientX - container.offsetLeft ) / container.clientWidth ) * 2 - 1;
mouse.y = - ( ( event.clientY - container.offsetTop ) / container.clientHeight ) * 2 + 1;
Вот сценарий, если вы хотите поэкспериментировать: http://jsfiddle.net/cn7ecoaa/
ИЗМЕНИТЬ: Fiddle обновляется до трёх. js r.84
Ответ 2
enent.clientX - это смещение окна клиента, поэтому для вычисления положения мыши мы должны использовать смещение окна клиента элемента визуализации.
Используйте element.getBoundingClientRect(), чтобы получить смещение элемента по вертикали.
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / ( rect.width - rect.left ) ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
<html>
<head>
<script src="http://threejs.org/build/three.min.js"></script>
<link rel="stylesheet" href="http://libs.baidu.com/bootstrap/3.0.3/css/bootstrap.min.css" />
<style>
body {
font-family: Monospace;
background-color: #fff;
margin: 0px;
overflow: hidden;
}
#canvas {
background-color: #000;
width: 200px;
height: 200px;
border: 1px solid black;
margin: 10px;
padding: 0px;
top: 10px;
left: 100px;
}
.border {
padding:10px;
margin:10px;
}
</style>
</head>
<body>
<div class="border">
<div class="border">
<div id="canvas"></div>
</div>
</div>
<script>
// Three.js ray.intersects with offset canvas
var container, camera, scene, renderer, mesh,
objects = [],
count = 0,
CANVAS_WIDTH = 200,
CANVAS_HEIGHT = 200;
// info
info = document.createElement( 'div' );
info.style.position = 'absolute';
info.style.top = '30px';
info.style.width = '100%';
info.style.textAlign = 'center';
info.style.color = '#f00';
info.style.backgroundColor = 'transparent';
info.style.zIndex = '1';
info.style.fontFamily = 'Monospace';
info.innerHTML = 'INTERSECT Count: ' + count;
info.style.userSelect = "none";
info.style.webkitUserSelect = "none";
info.style.MozUserSelect = "none";
document.body.appendChild( info );
container = document.getElementById( 'canvas' );
renderer = new THREE.WebGLRenderer();
renderer.setSize( CANVAS_WIDTH, CANVAS_HEIGHT );
container.appendChild( renderer.domElement );
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 45, CANVAS_WIDTH / CANVAS_HEIGHT, 1, 1000 );
camera.position.y = 250;
camera.position.z = 500;
camera.lookAt( scene.position );
scene.add( camera );
scene.add( new THREE.AmbientLight( 0x222222 ) );
var light = new THREE.PointLight( 0xffffff, 1 );
camera.add( light );
mesh = new THREE.Mesh(
new THREE.BoxGeometry( 200, 200, 200, 1, 1, 1 ),
new THREE.MeshPhongMaterial( { color : 0x0080ff }
) );
scene.add( mesh );
objects.push( mesh );
// find intersections
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
// mouse listener
document.addEventListener( 'mousedown', function( event ) {
var rect = renderer.domElement.getBoundingClientRect();
mouse.x = ( ( event.clientX - rect.left ) / ( rect.width - rect.left ) ) * 2 - 1;
mouse.y = - ( ( event.clientY - rect.top ) / ( rect.bottom - rect.top) ) * 2 + 1;
raycaster.setFromCamera( mouse, camera );
intersects = raycaster.intersectObjects( objects );
if ( intersects.length > 0 ) {
info.innerHTML = 'INTERSECT Count: ' + ++count;
}
}, false );
function render() {
mesh.rotation.y += 0.01;
renderer.render( scene, camera );
}
(function animate() {
requestAnimationFrame( animate );
render();
})();
</script>
</body>
</html>
Ответ 3
WestLangley, спасибо за ваше объяснение. Это было действительно полезно, как обычно.
В моем случае у меня была диаграмма в div абсолютно позиционированной, поэтому я должен был сделать это:
var offset = $('.rightBlock').offset();
mouse.x = ( ( event.clientX - offset.left ) / renderer.domElement.width ) * 2 - 1;
mouse.y = - ( ( event.clientY - offset.top ) / renderer.domElement.height ) * 2 + 1;
Где rightBlock - мой контейнер, который использует только 70% экрана.
Ты вдохновил меня и помог мне решить эту проблемную проблему! Большое спасибо.