THREE.js Ray Intersect fails by adding div
The short answer is you have to take into consideration the offset
of the canvas.
The long answer depends on how your code is written, so I'll give you two answers, which should cover the bases.
There are a lot of possible combinations, so you may have to experiment. Also, different browsers may act differently.
Assume your HTML is something like this:
#canvas {
width: 200px;
height: 200px;
margin: 100px;
padding: 0px;
position: static; /* fixed or static */
top: 100px;
left: 100px;
}
<body>
<div id="canvas">
</body>
Your JS is something like this:
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 );
Method 1 For the following method to work correctly, set the canvas position static; margin > 0 and padding > 0 are OK
mouse.x = ( ( event.clientX - renderer.domElement.offsetLeft ) / renderer.domElement.clientWidth ) * 2 - 1;
mouse.y = - ( ( event.clientY - renderer.domElement.offsetTop ) / renderer.domElement.clientHeight ) * 2 + 1;
Method 2 For this alternate method, set the canvas position fixed; set top > 0, set left > 0; padding must be 0; margin > 0 is OK
mouse.x = ( ( event.clientX - container.offsetLeft ) / container.clientWidth ) * 2 - 1;
mouse.y = - ( ( event.clientY - container.offsetTop ) / container.clientHeight ) * 2 + 1;
Here is a Fiddle if you want to experiment: http://jsfiddle.net/cn7ecoaa/
EDIT: Fiddle updated to three.js r.84
Three.js Inaccurate Raycaster
Your CSS will affect your raycasting calculations. One thing you can do is set
body {
margin: 0px;
}
For more information, see THREE.js Ray Intersect fails by adding div.
You also have to handle window resizing correctly. The typical pattern looks like this:
function onWindowResize() {
var aspect = window.innerWidth / window.innerHeight;
camera.left = - frustumSize * aspect / 2;
camera.right = frustumSize * aspect / 2;
camera.top = frustumSize / 2;
camera.bottom = - frustumSize / 2;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
}
Study the three.js examples. In particular, see http://threejs.org/examples/webgl_interactive_cubes_ortho.html.
Also, read this answer, which describes how to properly instantiate an orthographic camera.
three.js r.80
ThreeJS Raycaster doesn't find intersected objects if canvas isn't full screen
If your flow is broken (your container is floated or manually positioned), then clientX
/clientY
will report "bad" values (relative to the page, rather than to the container like you might expect). The properties offsetX
/offsetY
account for this by reporting values relative to the container on which the event was fired.
According to MDN, support is limited, but my testing just now (01/22/2018) showed the latest versions of Chrome, Firefox, Edge, and IE all reported the correct values*. Just note that some browsers report integer values, while others may report double values.
*This may not hold true for Safari or mobile browsers. YMMV.
Threejs / Raycast doesn't compute intersection with my cube
It seems your code works by using a latest version of three.js
. I've just refactored/simplified it a bit.
let camera, scene, renderer;
let mesh;
let raycaster, pointer = new THREE.Vector2();
init();
animate();
function init() {
camera = new THREE.PerspectiveCamera(35, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.z = 40;
scene = new THREE.Scene();
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({
color: "red"
});
mesh = new THREE.Mesh(geometry, material);
mesh.position.set(0, 10, 0);
scene.add(mesh);
renderer = new THREE.WebGLRenderer({antialias: true});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
raycaster = new THREE.Raycaster();
renderer.domElement.addEventListener('pointerdown', raycast);
}
function raycast(e) {
pointer.x = (e.clientX / window.innerWidth) * 2 - 1;
pointer.y = -(e.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(pointer, camera);
const intersects = raycaster.intersectObject(scene);
for (let i = 0; i < intersects.length; i++) {
console.log(intersects[i]);
}
}
function animate() {
requestAnimationFrame(animate);
mesh.rotation.x += 0.01;
mesh.rotation.y += 0.02;
renderer.render(scene, camera);
}
body {
margin: 0;
}
<script src="https://cdn.jsdelivr.net/npm/three@0.140.2/build/three.min.js"></script>
Related Topics
How to Reload Current Page Without Losing Any Form Data
How to Convert Characters to HTML Entities Using Plain JavaScript
Appending HTML String to the Dom
Jquery Load() Only Working in Firefox
Simulation Background-Size: Cover in Canvas
How to Store Arbitrary Data For Some HTML Tags
How to Open a Pdf File in an ≪Iframe≫
Good Tutorial For Using Html5 History API (Pushstate)
How to Get the Pure Text Without HTML Element Using JavaScript
Resize Image With JavaScript Canvas (Smoothly)
How to Change CSS Property Using JavaScript
How to Validate Inputs Dynamically Created Using Ng-Repeat, Ng-Show (Angular)
Double Quote in JavaScript String