three.js+react实现点击模型实现高亮选中效果
1、创建一个场景
let scene, camera, renderer, controls;
let stats = null; // 检测动画运行时的帧数
let clock = new THREE.Clock(); // getDelta()方法获得两帧的时间间隔
let FPS = 30;
let renderT = 1 / FPS;
let timeS = 0;
const ThreeModel = observer(() => {
// 设置灯光
function setLight() {
let hemiLightTop = new THREE.HemisphereLight(0xffffff, 0xffffff, 0.5);
let hemiLightBottom = new THREE.HemisphereLight(0xffffff, 0.5);
let lightTop = new THREE.DirectionalLight(0xffffff, 0.1);
let lightAfter = new THREE.DirectionalLight(0xffffff, 0.5);
hemiLightTop.position.set(0, 2000, 0);
hemiLightBottom.position.set(0, 0, 0);
lightTop.position.set(4, 6, 4);
lightAfter.position.set(0, 0, 2000);
scene.add(hemiLightTop);
scene.add(hemiLightBottom);
scene.add(lightTop);
scene.add(lightAfter);
lightTop.castShadow = true;// 光源开启阴影
lightTop.shadow.mapSize = new THREE.Vector2(1024, 1024);
lightTop.shadow.bias = -0.001;
}
useEffect(() => {
// 初始化页面canvas,初始化场景
// 定义场景
scene = new THREE.Scene();
// 灯光
setLight();
// 获取盒子宽高设置相机和渲染区域大小
let width = datahubBox.current.offsetWidth;
let height = datahubBox.current.offsetHeight;
let k = width / height;
// 定义相机
camera = new THREE.PerspectiveCamera(45, k, 0.25, 100000);
camera.position.set(-547, 15224, 2195);
camera.lookAt(scene.position);
// 创建一个webGL对象
renderer = new THREE.WebGLRenderer({
//增加下面两个属性,可以抗锯齿
antialias: true,
alpha: true,
logarithmicDepthBuffer: true // 解决模型闪烁问题
});
renderer.setSize(width, height); // 设置渲染区域尺寸
renderer.setClearColor(0x23284D, 0.0); // 设置颜色透明度
// 首先渲染器开启阴影
renderer.shadowMap.enabled = true;
// 修改渲染模式
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.textureEncoding = THREE.sRGBEncoding;
// 挂载到DOM节点
datahubBox.current.appendChild(renderer.domElement);
// 监听鼠标事件
controls = new THREE.OrbitControls(camera, renderer.domElement);
//- 拖拽惯性
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 控制上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI / 2;
// 限制缩放范围
controls.minDistance = 0;
controls.maxDistance = cameraDistanceMax;
}, []);
{/* canvas盒子 */}
return <div className='ui_model_box' ref={datahubBox}></div>;
});
export default ThreeModel;
2、高亮需要导入库
import * as three from 'three';
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer';
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass';
import { OutlinePass } from 'three/examples/jsm/postprocessing/OutlinePass';
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass';
import { GammaCorrectionShader } from 'three/examples/jsm/shaders/GammaCorrectionShader';
3、定义一个模型边缘高亮
/**
* 模型移入高亮
* @param { 区域宽度 } width
* @param { 区域高度 } height
* @param { 场景对象 } scene
* @param { 摄像机对象} camera
* @param { 渲染回调} renderer
*/
function setModelComposer(width, height, scene, camera, renderer) {
// 创建一个EffectComposer(效果组合器)对象,在该对象上添加后期处理通道,用于模型高亮
let composer = new EffectComposer(renderer);
// 新建一个场景通道,
let renderPass = new RenderPass(scene, camera);
composer.addPass(renderPass);
// 模型边缘发光通道
let outlinePass = new OutlinePass(new THREE.Vector2(width, height), scene, camera);
outlinePass.visibleEdgeColor.set('#00FF00'); // 呼吸显示颜色
outlinePass.hiddenEdgeColor.set('#00FF00');// 呼吸消失颜色
outlinePass.edgeStrength = 5; // 边框的亮度强度
outlinePass.edgeGlow = 0.5; // 光晕[0,1]
outlinePass.edgeThickness = 3;// 边缘宽度
outlinePass.pulsePeriod = 2; // 呼吸闪烁速度
outlinePass.renderToScreen = true; // 设置这个参数的目的是马上将当前的内容输出
composer.addPass(outlinePass);
//保持outputEncoding = sRGBEncoding,自定义着色器通道作为参数
let effectCopy = new ShaderPass(GammaCorrectionShader);
effectCopy.renderToScreen = true;
composer.addPass(effectCopy);
composer.selectedObjectEffect = function (objs) {
let selectedObjects = [];
selectedObjects.push(objs);
outlinePass.selectedObjects = selectedObjects;
};
return composer;
}
4、高亮设置
composer = setModelComposer(width, height, scene, camera, renderer);
5、循环渲染
// 渲染函数
function renderFn() {
controls.update(T);
requestAnimationFrame(renderFn);
THREE.TWEEN.update(); // 补间动画执行
// 查看性能
if (stats) {
stats.update();
}
if (isComposer && composer) {
// 组合渲染器,渲染高亮
composer.render(T);
} else {
// 用相机渲染一个场景
renderer.render(scene, camera);
}
}
6、点击模型边缘高亮
// 监听鼠标移动事件、设置高亮
datahubBox.current.addEventListener('mousemove', (event) => {
let selectObj = getCanvasIntersects(event, composerData, camera, datahubBox.current);
if (selectObj && selectObj.length > 0) {
isComposer = true;
composer.selectedObjectEffect(selectObj[0].object);
} else {
isComposer = false;
}
});
我想要实现的是子模型高亮,所以我这里取子模型的object。
注意:传入的参数是一个数组,传入那些模型,那些模型就能高亮。
每次点击前需要清空composer。
getCanvasIntersects方法参考链接
完整代码
let scene, camera, renderer, controls, composer;
let isComposer = false; // 是否组合渲染,现实选中高光效果
let stats = null; // 检测动画运行时的帧数
let clock = new THREE.Clock(); // getDelta()方法获得两帧的时间间隔
let FPS = 30;
let renderT = 1 / FPS;
let timeS = 0;
const ThreeModel = observer(() => {
// 渲染函数
function renderFn() {
if (!controls) {
return;
}
let T = clock.getDelta();
timeS = timeS + T;
controls.update(T);
requestAnimationFrame(renderFn);
THREE.TWEEN.update(); // 补间动画执行
// 查看性能
if (stats) {
stats.update();
}
// 限制每秒渲染帧数
if (timeS > renderT) {
if (isComposer && composer) {
// 组合渲染器,渲染高亮
composer.render(T);
} else {
// 用相机渲染一个场景
renderer.render(scene, camera);
}
timeS = 0;
}
}
useEffect(() => {
// 初始化页面canvas,初始化场景
// 定义场景
scene = new THREE.Scene();
// 灯光
setLight();
// 检测动画运行时的帧数。
stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '300px';
stats.domElement.style.top = '0px';
// 获取盒子宽高设置相机和渲染区域大小
let width = datahubBox.current.offsetWidth;
let height = datahubBox.current.offsetHeight;
let k = width / height;
// 定义相机
camera = new THREE.PerspectiveCamera(45, k, 0.25, 100000);
camera.position.set(-547, 15224, 2195);
camera.lookAt(scene.position);
// 创建一个webGL对象
renderer = new THREE.WebGLRenderer({
//增加下面两个属性,可以抗锯齿
antialias: true,
alpha: true,
logarithmicDepthBuffer: true // 解决模型闪烁问题
});
renderer.setSize(width, height); // 设置渲染区域尺寸
renderer.setClearColor(0x23284D, 0.0); // 设置颜色透明度
// 首先渲染器开启阴影
renderer.shadowMap.enabled = true;
// 修改渲染模式
renderer.setPixelRatio(window.devicePixelRatio);
renderer.outputEncoding = THREE.sRGBEncoding;
renderer.textureEncoding = THREE.sRGBEncoding;
// 挂载到DOM节点
datahubBox.current.appendChild(renderer.domElement);
// 监听鼠标事件
controls = new THREE.OrbitControls(camera, renderer.domElement);
//- 拖拽惯性
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 控制上下旋转范围
controls.minPolarAngle = 0;
controls.maxPolarAngle = Math.PI / 2;
// 限制缩放范围
controls.minDistance = 0;
controls.maxDistance = cameraDistanceMax;
// 高亮设置
composer = setModelComposer(width, height, scene, camera, renderer);
// 渲染
renderFn();
}, []);
useEffect(() => {
// 重置数据
return () => {
scene = null;
camera = null;
renderer = null;
controls = null;
composer = null;
isComposer = false;
stats = null;
clock = new THREE.Clock();
FPS = 30;
renderT = 1 / FPS;
timeS = 0;
};
}, []);
{/* canvas盒子 */}
return <div className='ui_model_box' ref={datahubBox}></div>;
});
export default ThreeModel;