射线拾取和轮廓高亮是Three.js中两项非常有用的技术,分别用于检测用户与3D对象的交互以及对选中的物体进行高亮显示。通过本文的介绍,你可以在自己的Three.js项目中实现这些功能,增强用户体验和交互效果。
1.场景创建
在开始实现射线拾取和轮廓高亮之前,我们需要先设置一个基本的Three.js场景。以下是创建一个包含立方体的简单场景的代码。
示例代码
import * as THREE from 'three';
// 创建场景
const scene = new THREE.Scene();
// 创建相机
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// 创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 设置相机位置
camera.position.z = 5;
// 渲染循环
function animate() {
requestAnimationFrame(animate);
renderer.render(scene, camera);
}
animate();
2.Raycaster射线拾取
射线拾取是一种用于检测3D空间中光线与对象交点的技术。通过在场景中发射一条从相机出发 并穿过鼠标点击位置的射线,我们可以检测到射线与哪些对象相交,从而实现用户与3D对象的交互。
为了实现射线拾取,我们需要监听鼠标点击事件,并在事件触发时创建一个射线投射器(THREE.Raycaster
),然后计算射线与场景中对象的交点。
示例代码
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 将鼠标位置转换为归一化设备坐标(NDC)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// 通过相机和鼠标位置更新射线
raycaster.setFromCamera(mouse, camera);
// 计算物体和射线的交点
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
// 如果有交点,更改第一个交点物体的颜色
intersects[0].object.material.color.set(0xff0000);
}
}
window.addEventListener('click', onMouseClick);
3.OutlinePass轮廓高亮
为了提高用户体验和交互效果,常常需要对选中的物体进行高亮显示。Three.js提供了一个名为OutlinePass
的功能,它通过为选中的3D对象添加一层发光的边缘(轮廓),使得这些对象在场景中更加显眼。该效果常用于游戏、数据可视化和虚拟现实应用中,以帮助用户识别和选择物体。
OutlinePass
提供了多种配置选项,可以调整轮廓的外观效果:
edgeStrength
: 控制轮廓的强度。edgeGlow
: 控制轮廓的光晕效果。edgeThickness
: 控制轮廓的厚度。visibleEdgeColor
: 设置轮廓的颜色。hiddenEdgeColor
: 设置隐藏边缘的颜色。
示例代码
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
// 假设已有的场景、相机和渲染器
const scene = existingScene;
const camera = existingCamera;
const renderer = existingRenderer;
// 创建后期处理Composer
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
outlinePass.edgeStrength = 2.5; // 轮廓的强度
outlinePass.edgeGlow = 0.5; // 轮廓的光晕
outlinePass.edgeThickness = 2.0; // 轮廓的厚度
outlinePass.visibleEdgeColor.set('#ff0000'); // 轮廓的颜色
outlinePass.hiddenEdgeColor.set('#190a05'); // 隐藏边缘的颜色
composer.addPass(outlinePass);
// 直接设置需要高亮的物体
const objectToHighlight = existingObject; // 需要高亮的物体
outlinePass.selectedObjects = [objectToHighlight];
function animate() {
requestAnimationFrame(animate);
composer.render();
}
animate();
4.将射线拾取与轮廓高亮结合
为了将射线拾取与轮廓高亮结合起来,我们可以在鼠标点击事件中,将选中的物体传递给OutlinePass
,从而实现点击物体时的高亮效果。以下是完整的代码示例。
示例代码
import * as THREE from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass.js';
// 创建场景
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 创建一个立方体
const geometry = new THREE.BoxGeometry();
const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });
const cube = new THREE.Mesh(geometry, material);
scene.add(cube);
// 设置相机位置
camera.position.z = 5;
// 创建后期处理Composer
const composer = new EffectComposer(renderer);
const renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
const outlinePass = new OutlinePass(new THREE.Vector2(window.innerWidth, window.innerHeight), scene, camera);
outlinePass.edgeStrength = 2.5; // 轮廓的强度
outlinePass.edgeGlow = 0.5; // 轮廓的光晕
outlinePass.edgeThickness = 2.0; // 轮廓的厚度
outlinePass.visibleEdgeColor.set('#ff0000'); // 轮廓的颜色
outlinePass.hiddenEdgeColor.set('#190a05'); // 隐藏边缘的颜色
composer.addPass(outlinePass);
// 创建射线投射器
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 将鼠标位置转换为归一化设备坐标(NDC)
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = - (event.clientY / window.innerHeight) * 2 + 1;
// 通过相机和鼠标位置更新射线
raycaster.setFromCamera(mouse, camera);
// 计算物体和射线的交点
const intersects = raycaster.intersectObjects(scene.children);
if (intersects.length > 0) {
// 设置轮廓高亮的物体
outlinePass.selectedObjects = [intersects[0].object];
} else {
// 如果没有选中的物体,清空轮廓高亮
outlinePass.selectedObjects = [];
}
}
// 添加鼠标点击事件监听器
window.addEventListener('click', onMouseClick);
function animate() {
requestAnimationFrame(animate);
composer.render();
}
animate();