three.js鼠标移入模型边缘高亮选中效果(结合react,方法封装)

1,020 阅读4分钟

three.js + react 实现鼠标移入模型高亮选中效果,方法封装

1.png

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(效果组合器)对象,在该对象上添加后期处理通道,用于模型高亮
    const composer = new EffectComposer(renderer);
    // 新建一个场景通道
    const renderPass = new RenderPass(scene, camera);
    composer.addPass(renderPass);
     // 模型边缘发光通道
    const 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() {
    requestAnimationFrame(renderFn);
    if (isComposer && composer) {
        // 组合渲染器,渲染高亮
        composer.render(T);
    } else {
        // 用相机渲染一个场景
        renderer.render(scene, camera);
    }
}

6、点击模型边缘高亮

// 监听鼠标移动事件、设置高亮,composerData需要高亮数据
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; // 是否组合渲染,现实选中高光效果

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;
        };
    }, []);
    {/* canvas盒子 */}
    return <div className='ui_model_box' ref={datahubBox}></div>;
});
export default ThreeModel;