Raycast 对象以使用 Three.js 启用鼠标单击事件
Raycast an object to enbable a mouse click event with Three.js
我正在为数据库中的每个条目向场景添加对象。我有一个立方体出现在场景中以供进入,但是当我尝试添加光线投射以单击它不起作用的对象时,对象没有出现并且控制台显示 "Expression unavailable".. 我从 three.js Raycasting 网站所以不确定我做错了什么。
这里是JS代码:
var renderer, scene, container, camera;
var geometry, material;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild( renderer.domElement );
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
group = new THREE.Group();
scene.add( group )
//fetch data from database and add object for each entry
getData()
async function getData() {
var response = await fetch('/api/indexvr');
var data = await response.json();
console.log(data)
for (var i=0; i<data.length; i++) {
cube = new THREE.Mesh( geometry, material );
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 15, 15, 15 ); //camera.position.set( 5, 0, 10 );
camera.lookAt( scene.position );
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
intersects[ i ].object.material.color.set( 0xff0000 );
}
renderer.render( scene, camera );
}
window.addEventListener( 'mousemove', onMouseMove, false );
window.requestAnimationFrame(render);
HTML 有一个名为 "container" 的 div 和这个标签:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
它不会产生任何错误,它只会在控制台中显示:
所以它正在获取数据但无法渲染场景
var renderer, scene, container, camera;
var geometry, material;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild(renderer.domElement);
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
group = new THREE.Group();
scene.add(group)
//fetch data from database and add object for each entry
getData()
async function getData() {
/**
* @author TheJim01
* Replacing DB call with fake data to make it work here.
* Nancy: Please feel free to add appropriate data.
*/
// var response = await fetch('/api/indexvr');
// var data = await response.json();
var data = [{}, {}, {}, {}, {}]
console.log(data)
for (var i = 0; i < data.length; i++) {
cube = new THREE.Mesh(geometry, material);
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
camera.lookAt(scene.position);
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
renderer.render(scene, camera);
}
window.addEventListener('mousemove', onMouseMove, false);
window.requestAnimationFrame(render);
<script src="//threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
<script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
<div id="container"></div>
我认为你从来没有添加三轨道控制模块。根据 three.js 文档,我们需要在文件中单独添加轨道控制。
https://threejs.org/docs/#examples/en/controls/OrbitControls
https://www.npmjs.com/package/three-orbitcontrols
你的项目中有OrbitControls.js文件吗?
我看到有几处错误。这是否是因为您省略了部分代码,我不能说。
首先,您提供的代码没有定义几何,也没有定义 material。你暗示你正在为每个数据库结果绘制一个立方体,所以我会做一个假设并使用 BoxBufferGeometry
。您也没有定义任何灯光,所以我将只使用不需要灯光的 MeshBasicMaterial
。
有了这些,看起来您已经使用 window.requestAnimationFrame
设置渲染循环了一半,但您仍然只调用 render
一次,即使您的数据库提取是异步。换句话说,渲染可能发生在您甚至没有收到来自数据库的响应之前,因此您什么也看不到。我添加了一些样板代码来设置渲染循环,类似于 three.js 在其示例中的做法。
有趣的是,仅此而已。 raycaster 开始工作,我能够通过控制台记录结果。当场景第一次开始渲染时,我确实得到了一些误报,但那是因为还没有任何鼠标输入,所以它是从屏幕中间(第一个立方体存在的地方)进行光线投射。
通常情况下,您不会希望每一帧都进行光线投射,但我知道 VR 情况可能会有所不同(该死的烦躁的人)。
最后,我所做的最后一项更改是为每个立方体提供自己的 material(好吧,是原始立方体的克隆)。这是必要的,以确保您可以单独对每个人进行光线投射。
// Need to create geometry and material
var geometry = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
var material = new THREE.MeshBasicMaterial({
color: "green"
});
var renderer, scene, container, camera;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild(renderer.domElement);
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
group = new THREE.Group();
scene.add(group)
//fetch data from database and add object for each entry
getData()
async function getData() {
/**
* @author TheJim01
* Replacing DB call with fake data to make it work here.
* Nancy: Please feel free to add appropriate data.
*/
// var response = await fetch('/api/indexvr');
// var data = await response.json();
var data = [{}, {}, {}, {}, {}]
//console.log(data)
for (var i = 0; i < data.length; i++) {
cube = new THREE.Mesh(geometry, material.clone());
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
camera.lookAt(scene.position);
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log(intersects);
}
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
renderer.render(scene, camera);
}
window.addEventListener('mousemove', onMouseMove, false);
// Here's the bbasic render loop implementation
function animate() {
requestAnimationFrame(animate);
render();
}
animate();
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>
<div id="container"></div>
我正在为数据库中的每个条目向场景添加对象。我有一个立方体出现在场景中以供进入,但是当我尝试添加光线投射以单击它不起作用的对象时,对象没有出现并且控制台显示 "Expression unavailable".. 我从 three.js Raycasting 网站所以不确定我做错了什么。
这里是JS代码:
var renderer, scene, container, camera;
var geometry, material;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove( event ) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1;
mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild( renderer.domElement );
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color( 0xffffff );
group = new THREE.Group();
scene.add( group )
//fetch data from database and add object for each entry
getData()
async function getData() {
var response = await fetch('/api/indexvr');
var data = await response.json();
console.log(data)
for (var i=0; i<data.length; i++) {
cube = new THREE.Mesh( geometry, material );
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1000 );
camera.position.set( 15, 15, 15 ); //camera.position.set( 5, 0, 10 );
camera.lookAt( scene.position );
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera( mouse, camera );
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects( scene.children );
for ( var i = 0; i < intersects.length; i++ ) {
intersects[ i ].object.material.color.set( 0xff0000 );
}
renderer.render( scene, camera );
}
window.addEventListener( 'mousemove', onMouseMove, false );
window.requestAnimationFrame(render);
HTML 有一个名为 "container" 的 div 和这个标签:
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
它不会产生任何错误,它只会在控制台中显示:
所以它正在获取数据但无法渲染场景
var renderer, scene, container, camera;
var geometry, material;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild(renderer.domElement);
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
group = new THREE.Group();
scene.add(group)
//fetch data from database and add object for each entry
getData()
async function getData() {
/**
* @author TheJim01
* Replacing DB call with fake data to make it work here.
* Nancy: Please feel free to add appropriate data.
*/
// var response = await fetch('/api/indexvr');
// var data = await response.json();
var data = [{}, {}, {}, {}, {}]
console.log(data)
for (var i = 0; i < data.length; i++) {
cube = new THREE.Mesh(geometry, material);
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
camera.lookAt(scene.position);
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
renderer.render(scene, camera);
}
window.addEventListener('mousemove', onMouseMove, false);
window.requestAnimationFrame(render);
<script src="//threejs.org/build/three.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.js"></script>
<script src="https://unpkg.com/spritejs/dist/spritejs.min.js"></script>
<div id="container"></div>
我认为你从来没有添加三轨道控制模块。根据 three.js 文档,我们需要在文件中单独添加轨道控制。
https://threejs.org/docs/#examples/en/controls/OrbitControls
https://www.npmjs.com/package/three-orbitcontrols
你的项目中有OrbitControls.js文件吗?
我看到有几处错误。这是否是因为您省略了部分代码,我不能说。
首先,您提供的代码没有定义几何,也没有定义 material。你暗示你正在为每个数据库结果绘制一个立方体,所以我会做一个假设并使用 BoxBufferGeometry
。您也没有定义任何灯光,所以我将只使用不需要灯光的 MeshBasicMaterial
。
有了这些,看起来您已经使用 window.requestAnimationFrame
设置渲染循环了一半,但您仍然只调用 render
一次,即使您的数据库提取是异步。换句话说,渲染可能发生在您甚至没有收到来自数据库的响应之前,因此您什么也看不到。我添加了一些样板代码来设置渲染循环,类似于 three.js 在其示例中的做法。
有趣的是,仅此而已。 raycaster 开始工作,我能够通过控制台记录结果。当场景第一次开始渲染时,我确实得到了一些误报,但那是因为还没有任何鼠标输入,所以它是从屏幕中间(第一个立方体存在的地方)进行光线投射。
通常情况下,您不会希望每一帧都进行光线投射,但我知道 VR 情况可能会有所不同(该死的烦躁的人)。
最后,我所做的最后一项更改是为每个立方体提供自己的 material(好吧,是原始立方体的克隆)。这是必要的,以确保您可以单独对每个人进行光线投射。
// Need to create geometry and material
var geometry = new THREE.BoxBufferGeometry(0.5, 0.5, 0.5);
var material = new THREE.MeshBasicMaterial({
color: "green"
});
var renderer, scene, container, camera;
var controls, group;
var raycaster = new THREE.Raycaster();
var mouse = new THREE.Vector2();
init()
function onMouseMove(event) {
// calculate mouse position in normalized device coordinates
// (-1 to +1) for both components
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
}
function init() {
// init renderer
renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(window.innerWidth, window.innerHeight);
// document.body.appendChild( renderer.domElement );
container = document.getElementById('container');
container.appendChild(renderer.domElement);
// init scene
scene = new THREE.Scene();
scene.background = new THREE.Color(0xffffff);
group = new THREE.Group();
scene.add(group)
//fetch data from database and add object for each entry
getData()
async function getData() {
/**
* @author TheJim01
* Replacing DB call with fake data to make it work here.
* Nancy: Please feel free to add appropriate data.
*/
// var response = await fetch('/api/indexvr');
// var data = await response.json();
var data = [{}, {}, {}, {}, {}]
//console.log(data)
for (var i = 0; i < data.length; i++) {
cube = new THREE.Mesh(geometry, material.clone());
cube.position.x = i;
scene.add(cube);
//group.add(data)
}
}
// init camera
camera = new THREE.PerspectiveCamera(50, window.innerWidth / window.innerHeight, 1, 1000);
camera.position.set(15, 15, 15); //camera.position.set( 5, 0, 10 );
camera.lookAt(scene.position);
// controls = new OrbitControls( camera, renderer.domElement );
// controls.enableRotate = true;
}
function render() {
// update the picking ray with the camera and mouse position
raycaster.setFromCamera(mouse, camera);
// calculate objects intersecting the picking ray
var intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
console.log(intersects);
}
for (var i = 0; i < intersects.length; i++) {
intersects[i].object.material.color.set(0xff0000);
}
renderer.render(scene, camera);
}
window.addEventListener('mousemove', onMouseMove, false);
// Here's the bbasic render loop implementation
function animate() {
requestAnimationFrame(animate);
render();
}
animate();
html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/108/three.min.js"></script>
<div id="container"></div>