three.js点击模型实现模型边缘高亮选中效果

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(效果组合器)对象,在该对象上添加后期处理通道,用于模型高亮
    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;
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值