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

news2024/10/5 23:53:14

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;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1069200.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

机器学习基础-数据分析:房价预测

mac设置中文字体 #要设置下面两行才能显示中文 Arial Unicode MS 为字体 plt.rcParams[font.sans-serif] [Arial Unicode MS] #设置图片大小 plt.figure(figsize(20, 11), dpi200)pie官方文档 总体代码 python import pandas as pd import numpy as np import matplotlib.…

Qt/C++原创推流工具/支持多种流媒体服务/ZLMediaKit/srs/mediamtx等

一、前言 1.1 功能特点 支持各种本地视频文件和网络视频文件。支持各种网络视频流&#xff0c;网络摄像头&#xff0c;协议包括rtsp、rtmp、http。支持将本地摄像头设备推流&#xff0c;可指定分辨率和帧率等。支持将本地桌面推流&#xff0c;可指定屏幕区域和帧率等。自动启…

【大数据 | 综合实践】大数据技术基础综合项目 - 基于GitHub API的数据采集与分析平台

&#x1f935;‍♂️ 个人主页: AI_magician &#x1f4e1;主页地址&#xff1a; 作者简介&#xff1a;CSDN内容合伙人&#xff0c;全栈领域优质创作者。 &#x1f468;‍&#x1f4bb;景愿&#xff1a;旨在于能和更多的热爱计算机的伙伴一起成长&#xff01;&#xff01;&…

在供应链管理中,如何做好库存分析?库存分析有哪些监控指标?

在供应链管理中&#xff0c;库存分析是其重要的一环。库存分析的方法繁杂且广泛&#xff0c;选择正确的方法才能更好的进行库存分析&#xff0c;下面就为大家盘点一些常用的库存分析方法和监控指标&#xff0c;全程干货&#xff0c;建议收藏&#xff01; 01 如何进行库存分析&…

【MySQL】基本查询(三)聚合函数+group by

文章目录 一. 聚合函数二. group by子句结束语 建立如下表 //创建表结构 mysql> create table exam_result(-> id int unsigned primary key auto_increment,-> name varchar(20) not null comment 同学姓名,-> chinese float default 0.0 comment 语文成绩,->…

08_selenium实战——学习平台公开数据批量获取

0、:前言 该实战任务是对某视频平台中’标题’、 ‘点赞数量’、 ‘投币数量’、‘收藏数量’、‘播放次数’、以及前五条评论进行爬取。要求1:可以控制爬取视频的主题(爬取主题搜索之后的内容)要求2:可以控制爬取视频的数量要求3:对于评论数不足5条的用0填充评论内容爬虫…

vue启动项目,npm run dev出现error:0308010C:digital envelope routines::unsupported

运行vue项目&#xff0c;npm run dev的时候出现不支持错误error:0308010C:digital envelope routines::unsupported。 在网上找了很多&#xff0c;大部分都是因为版本问题&#xff0c;修改环境之类的&#xff0c;原因是对的但是大多还是没能解决。经过摸索终于解决了。 方法如…

第九课 排序

文章目录 第九课 排序排序算法lc912.排序数组--中等题目描述代码展示 lc1122.数组的相对排序--简单题目描述代码展示 lc56.合并区间--中等题目描述代码展示 lc215.数组中的第k个最大元素--中等题目描述代码展示 acwing104.货仓选址--简单题目描述代码展示 lc493.翻转树--困难题…

保护 Web 服务器安全性

面向公众的系统&#xff08;如 Web 服务器&#xff09;经常成为攻击者的目标&#xff0c;如果这些业务关键资源没有得到适当的保护&#xff0c;可能会导致安全攻击&#xff0c;从而导致巨大的财务后果&#xff0c;并在客户中失去良好的声誉。 什么是网络服务器审核 当有人想要…

无线振弦采集仪在岩土工程中如何远程监测和远程维护

无线振弦采集仪在岩土工程中如何远程监测和远程维护 随着岩土工程施工的不断发展和科技水平的不断提高&#xff0c;远程监测和远程维护设备也得到了广泛关注和应用。无线振弦采集仪是一种广泛应用于岩土工程中的测量仪器&#xff0c;在现代化施工中扮演着重要的角色。本文将就…

ChromeDriver驱动最新版下载

下载地址ChromeDriver - WebDriver for Chrome - Downloads selenium.common.exceptions.SessionNotCreatedException: Message: session not created: This version of ChromeDriver only supports Chrome version 113 Current browser version is 117.0.5938.150 with binar…

2、模块传参和依赖

一、模块传参 使用函数 module_param(name,type,perm); 将指定的全局变量设置成模块参数 /* name:全局变量名 type&#xff1a;使用符号 实际类型 传参方式bool bool insmod xxx.ko 变量名0 或 1invbool bool insmod xx…

运营商sdwan优缺点及sdwan服务商优势

SD-WAN(软件定义广域网)作为一种重要的网络解决方案&#xff0c;已经受到了广泛的关注和采用。然而&#xff0c; 无论是由传统运营商提供的SD-WAN还是专门的SD-WAN服务提供商&#xff0c;都存在各自的优缺点。 运营商提供的SD-WAN的缺点&#xff1a; 1. 有限的灵活性&#xf…

数据库查询详解

数据库查询操作 前置&#xff1a;首先我们创建一个练习的数据库 /* SQLyog Professional v12.09 (64 bit) MySQL - 5.6.40-log : Database - studentsys ********************************************************************* *//*!40101 SET NAMES utf8 */;/*!40101 SET …

【uniapp】自定义导航栏时,设置安全距离,适配不同机型

1、在pages.json中&#xff0c;给对应的页面设置自定义导航栏样式 {"path": "pages/index/index","style": {"navigationStyle": "custom","navigationBarTextStyle": "white","navigationBarTitl…

智慧电力物联网系统引领电力行业数字化发展

智慧电力物联网系统是以提高用户侧电力运行安全、降低运维成本为目的的一套电力运维管理系统。综合分析采用智慧物联网、人工智能等现代化经济信息网络技术&#xff0c;配置智能采集终端、小安神童值班机器人或边缘网关&#xff0c;实现对企事业用户供配电系统的数字化远程监控…

网关、网桥、路由器和交换机之【李逵与李鬼】

概念 网关 网关简单来说是连接两个网络的设备,现在很多局域网都是采用路由器来接入网络,因此现在网关通常指的就是路由器的IP。网关可用于家庭或者小型企业,连接局域网和Internet,也有用于工业应用的。 网桥 网桥也叫桥接器,是连接两个局域网的一种存储/转发设备,它能…

Python字符串处理:掌握文本的艺术

更多资料获取 &#x1f913; 作者主页&#xff1a;涛哥聊Python &#x1f4da; 个人网站&#xff1a;涛哥聊Python 在Python编程中&#xff0c;字符串是一种不可或缺的数据类型&#xff0c;用于表示文本和字符数据。本文将深入探讨Python字符串的各个方面&#xff0c;从基础概…

网络安全(黑客技术)—小白自学笔记

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟入…

缓冲流 java

字节缓冲池的默认大小 &#xff08;8192/byte&#xff09;字节输入输出流 字节缓冲输入接口也是 InputStream 读字节 实现类BufferedInputStream 字节缓冲输出接口也是 OutputStream 写字节 实现类BufferedOutputStream package BufferFlow;import CopysIO.Myconnectio…