THREE.js射线相交在添加div后失败

20

当页面上只有一个目标div(包含renderer.domElement)时,我的Three.js脚本运行良好。但是一旦我在目标div上方添加了另一个具有固定高度和宽度的div,ray.intersectObjects就会返回null。我怀疑创建ray的向量可能是问题的原因。以下是代码。

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 );

你有什么想法可以解决这个问题吗?

编辑:它现在是THREE.Raycaster(three.js r.56)

4个回答

47

简短的回答是你需要考虑画布的 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.setPixelRatio( window.devicePixelRatio );
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 对于这种替代方法,将画布位置设置为fixed;设置顶部> 0,设置左侧> 0;填充必须为0;边距> 0可以。

mouse.x = ( ( event.clientX - container.offsetLeft ) / container.clientWidth ) * 2 - 1;
mouse.y = - ( ( event.clientY - container.offsetTop ) / container.clientHeight ) * 2 + 1;

如果您想进行实验,这里有一个 jsFiddle: https://jsfiddle.net/uf4c0ws9/

编辑:Fiddle已更新为 three.js r.150


3
在某些设备(如Retina显示器)中,鼠标x和y计算的分子可能需要乘以renderer.devicePixelRatio才能得到正确的结果。 - Charlotte

17

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>


6
通过修改鼠标的x分母,我得到了良好的结果。 mouse.x = ((event.clientX - rect.left) / (rect.right - rect.left)) * 2 - 1; - beepscore
@beepscore 你的评论是正确的,对我帮助很大,谢谢。 - PeterB

4

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%。

你启发了我并帮助我解决了这个问题!非常感谢。


这不适用于几何缩小时,会出现偏移,你知道如何修复吗? - Displee

0

我有一个带有绝对定位的 div(滑块),里面嵌套了一个画布。

<div class='slider'><div id="canvas-container"></div></div>

const canvasContainer: HTMLElement = document.getElementById('canvas-container');

this.renderer.setSize(canvasContainer.clientWidth, canvasContainer.clientHeight);
canvasContainer.appendChild(this.renderer.domElement);


const renderer = this.renderer.domElement.getBoundingClientRect();
this.mouse.x = ( ( e.clientX - renderer.left ) / ( renderer.right - renderer.left ) ) * 2 - 1;
this.mouse.y = - ( ( e.clientY - renderer.top ) / ( renderer.bottom - renderer.top) ) * 2 + 1;

this.raycaster.intersectObject(this.model, true);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接