three.js工厂案例

news2024/11/19 3:41:29
  1. 最终效果图

在这里插入图片描述

  1. 给定html页面,作为tag标签展示
<body style="background-color: black;">
  <div id="tag">
    <div style="position:relative;width:400px;height:322px;color: #fff;">
      <img src="background.png" alt="" style="width:100%;position: absolute;left: 0px;top: 0px;">
      <div style="position:absolute;left:48px;top:36px;font-size:16px;">
        <div style="font-size:20px;font-weight: 400;">
          <span>设备A</span>
        </div>
        <div style="margin-top: 30px;">
          <span style="font-weight: 400;margin-left: 80px;font-size: 40px;color: #00ffff;">test</span>
        </div>
        <div style="margin-top: 20px;">
          <span style="color: #ccc;font-weight: 300;">管理</span><span
            style="font-weight: 400;margin-left: 30px;">admin</span>
        </div>
        <div style="margin-top: 10px;">
          <span style="color: #ccc;font-weight: 300;">工号</span><span
            style="font-weight: 400;margin-left: 30px;">123456</span>
        </div>
      </div>
      <div style="position:absolute;left:285px;top:35px;">
        <span style="color: #ffff00;">异常</span>
      </div>
    </div>
  </div>

  <!-- type="importmap"功能:.html文件中也能和nodejs开发环境中一样方式,引入npm安装的js库 -->
  <script type="importmap">
      {
    "imports": {
      "three": "../build/three.module.js",
      "three/addons/":"../three.js-r167/examples/jsm/"
    }
  }
</script>
  <script src="./index.js" type="module">
  </script>
</body>

  1. mode.js

加载3D模型

// 引入Three.js
import * as THREE from 'three';
// 引入gltf模型加载库GLTFLoader.js
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader(); //创建一个GLTF加载器
const model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
loader.load("test.glb", function (gltf) { //gltf加载成功后返回一个对象
    model.add(gltf.scene); //三维场景添加到model组对象中
})
export default model;

// 打印出模型中的所有对象
// gltf.scene.traverse(function (child) {
//   if (child.isMesh) {
//     // 打印对象名称和类型
//     console.log('对象名称:', child.name, ', 对象类型:', child.type);
//   }
// });
  1. tag.js

Three.js 的 WebGLRenderer 只负责渲染 3D 场景,使用 WebGL 在 GPU 上渲染图像。WebGL 渲染和 HTML 的渲染流程是分开的,互相不干扰。HTML 元素是由浏览器的渲染引擎渲染的,和 GPU 渲染的 3D 场景没有直接关联。

所以,普通 HTML 标签不会直接参与 Three.js 的场景渲染,也不会随 3D 场景的变化(如相机位置、旋转等)自动更新位置。

因此引入CSS2DRenderer 专门渲染 HTML/CSS 标签,允许在 WebGL 场景中直接显示和操作 DOM 元素,如显示标注或文本信息。

// 引入CSS2模型对象CSS2DObject
import {
  CSS2DObject
} from 'three/addons/renderers/CSS2DRenderer.js';
const div = document.getElementById('tag');
div.style.top = '-161px'; //指示线端点和标注点重合
// HTML元素转化为threejs的CSS2模型对象
const tag = new CSS2DObject(div);
export default tag;
  1. index.js

    1. 将模型渲染到场景中【内容基本不变,一般只改一下光源,相机位置】
const scene = new THREE.Scene();
//模型对象添加到场景中
scene.add(model);

//辅助观察的坐标系
const axesHelper = new THREE.AxesHelper(100);
scene.add(axesHelper);

//光源设置
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5000, 60, 50);
scene.add(directionalLight);
const ambient = new THREE.AmbientLight(0xffffff, 0.9);
scene.add(ambient);

//渲染器和相机
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(30, width / height, 1, 3000);
camera.position.set(82, 53, 165);
camera.lookAt(0, 0, 0);

const renderer = new THREE.WebGLRenderer({
  // 抗锯齿优化
  antialias:true
});

// 防止输出模糊
renderer.setPixelRatio(window.devicePixelRatio)
renderer.setSize(width, height);
document.body.appendChild(renderer.domElement);

//解决加载gltf格式模型颜色偏差问题
//设置后处理,该方法无效
renderer.outputEncoding = THREE.sRGBEncoding;

// 渲染循环
function render() {
    renderer.render(scene, camera);
    requestAnimationFrame(render);
}
render();

// 设置相机控件轨道控制器OrbitControls
const controls = new OrbitControls(camera, renderer.domElement);

// 画布跟随窗口变化
window.onresize = function () {
    renderer.setSize(window.innerWidth, window.innerHeight);
    camera.aspect = window.innerWidth / window.innerHeight;
    camera.updateProjectionMatrix();
};

光源的颜色,强度可以通过GUI来直接控制;

其中DirectionalLight为定向光源,用于模拟太阳光或类似的强烈光源,可以产生阴影,且光线有方向性,适用于需要精确阴影的场景;

AmbientLight环境光用于为场景提供均匀的基础光照,所有物体都被均匀照亮,无阴影效果。

import {GUI} from 'three/addons/libs/lil-gui.module.min.js'
// GUI 设置
const gui = new GUI();
const lightFolder = gui.addFolder('光源设置');
const ambientLightFolder = gui.addFolder('环境光设置');

// 光源控制
const lightParams = {
  color: directionalLight.color.getHex(),
  intensity: directionalLight.intensity
};
lightFolder.addColor(lightParams, 'color').name('颜色').onChange((value) => {
  directionalLight.color.setHex(value);
});
lightFolder.add(lightParams, 'intensity', 0, 2).name('光强').onChange((value) => {
  directionalLight.intensity = value;
});

// 环境光控制
const ambientParams = {
  color: ambient.color.getHex(),
  intensity: ambient.intensity
};
ambientLightFolder.addColor(ambientParams, 'color').name('颜色').onChange((value) => {
  ambient.color.setHex(value);
});
ambientLightFolder.add(ambientParams, 'intensity', 0, 2).name('光强').onChange((value) => {
  ambient.intensity = value;
});

对应的也可以控制相机的位置(x,y,z轴),视角等参数,方便在camera.position.set(94, 58, 107);设置相机位置的时候给出合适的值

// GUI 设置相机控制
const cameraFolder = gui.addFolder('相机设置');

// 控制相机位置
cameraFolder.add(camera.position, 'x', -500, 500).name('相机 X 轴');
cameraFolder.add(camera.position, 'y', -500, 500).name('相机 Y 轴');
cameraFolder.add(camera.position, 'z', -500, 500).name('相机 Z 轴');

// 控制相机视角 (fov)
cameraFolder.add(camera, 'fov', 1, 100).name('视角').onChange(() => {
  camera.updateProjectionMatrix(); // 更新相机投影矩阵
});

// 控制相机的近/远剪裁面
cameraFolder.add(camera, 'near', 0.1, 1000).name('近剪裁面').onChange(() => {
  camera.updateProjectionMatrix(); // 更新相机投影矩阵
});
cameraFolder.add(camera, 'far', 1, 5000).name('远剪裁面').onChange(() => {
  camera.updateProjectionMatrix(); // 更新相机投影矩阵
});

2. 添加`EffectComposer`后处理

基本渲染WebGLRendererrender 方法用于将 three.js 场景渲染到屏幕上,直接得到渲染结果。renderer.render(scene, camera);

后期处理的需求: 如果需要对渲染结果应用一些后期处理效果(如模糊、颜色校正、景深等),EffectComposer 能够在渲染之后对图像进行一系列处理效果。

// 创建后处理对象EffectComposer,WebGL渲染器作为参数
const composer = new EffectComposer(renderer);
// 创建一个渲染器通道,场景和相机作为参数
const renderPass = new RenderPass(scene, camera);
// 设置renderPass通道
composer.addPass(renderPass);

// 渲染循环
function render() {
  // 后处理渲染
    composer.render();
    requestAnimationFrame(render);
}
render();
3. 添加描边光效后处理`OutlinePass`
// 创建outlinePass通道
// OutlinePass第一个参数v2的尺寸和canvas画布保持一致
const v2 = new THREE.Vector2(window.innerWidth, window.innerHeight);
const outlinePass = new OutlinePass(v2, scene, camera);
outlinePass.visibleEdgeColor.set(0x00ffff); //模型描边颜色,默认白色 
outlinePass.edgeThickness = 4; //高亮发光描边厚度
outlinePass.edgeStrength = 6; //高亮描边发光强度
outlinePass.pulsePeriod = 2;//模型闪烁频率控制,默认0不闪烁
outlinePass.usePatternTexture = false; // 禁用纹理以获得纯线的效果

composer.addPass(outlinePass);

场景中添加outlinePass之后变得很暗,这是因为当使用threejs后处理功能后,renderer.outputEncoding = THREE.sRGBEncoding会失效,出现颜色偏差。 本质上涉及到线性空间sRGB 空间之间的转换问题;

gltf模型中包含的颜色被导出一般为sRGB颜色空间,而three.js是在线性空间中渲染模型材质,因为three.js会将sRGB转化为Linear;而为了在显示器上正确显示颜色,不出现偏差,又需要将线性空间转化会sRGB空间;

three.js中可以通过伽马矫正来解决gltf模型后处理时候,颜色偏差的问题

// 伽马校正后处理Shader
import {GammaCorrectionShader} from 'three/addons/shaders/GammaCorrectionShader.js';
// ShaderPass功能:使用后处理Shader创建后处理通道
import {ShaderPass} from 'three/addons/postprocessing/ShaderPass.js';

const gammaPass= new ShaderPass(GammaCorrectionShader);
composer.addPass(gammaPass);

报错:着色器环境中未找到sRGBTransferOETF函数,当前版本148,没有去看其余版本是否包含此函数,选择自定义伽马矫正

// 自定义伽马矫正着色器
const gammaCorrectionShader = {
  uniforms: {
      "tDiffuse": { value: null },
      "gammaFactor": { value: 2.2 }
  },
  vertexShader: `
      varying vec2 vUv;

      void main() {
          vUv = uv;
          gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
      }
  `,
  fragmentShader: `
      varying vec2 vUv;
      uniform sampler2D tDiffuse;
      uniform float gammaFactor;

      void main() {
          vec4 tex = texture2D(tDiffuse, vUv);
          gl_FragColor = vec4(pow(tex.rgb, vec3(1.0 / gammaFactor)), tex.a);
      }
  `
};
4. 添加抗锯齿效果(左抗锯齿前,右后)
// SMAA抗锯齿通道
import {SMAAPass} from 'three/addons/postprocessing/SMAAPass.js';
//获取.setPixelRatio()设置的设备像素比
const pixelRatio = renderer.getPixelRatio();
// width、height是canva画布的宽高度
const smaaPass = new SMAAPass(width * pixelRatio, height * pixelRatio);

composer.addPass(smaaPass);//抗锯齿效果

5. 创建CSS2D渲染器,将标签渲染到webgl页面上
// 创建一个CSS2渲染器CSS2DRenderer
const css2Renderer = new CSS2DRenderer();
css2Renderer.setSize(width, height);
// HTML标签<div id="tag"></div>外面父元素叠加到canvas画布上且重合
css2Renderer.domElement.style.position = 'absolute';
css2Renderer.domElement.style.top = '0px';
//设置.pointerEvents=none,解决HTML元素标签对threejs canvas画布鼠标事件的遮挡
css2Renderer.domElement.style.pointerEvents = 'none';
// 将CSS2DRenderer的DOM元素添加到html的body中,显示在3D画布的上方。
document.body.appendChild(css2Renderer.domElement);

// 渲染循环
function render() {
    css2Renderer.render(scene,camera);
    // renderer.render(scene, camera);
    composer.render();
    requestAnimationFrame(render);
}
render();
6. 鼠标点击事件
    1. `THREE.Raycaster` 是 Three.js 提供的一个用于进行射线投射的工具。用于 3D 空间中检测物体交互的方法,通常用于实现点击、碰撞检测、选择对象等功能。简单来说就是从一个点(发射点)沿某个方向发射射线,并检测射线与场景中物体的交点。
    2. 获取罐的模型对象,可以看到下面有两个子节点设备A和设备B

    3. 设置mesh对象的ancestors属性为它所在的 Group 对象
    4. 创建射线,并且把鼠标点击获取的坐标和相机传入参数,可视化射线,查看射线击中了哪个物体以及交点的位置  
    5. 后续的就是js的逻辑,判断点击获取到的对象,根据对应名字,找到标注A,标注B,将tag标签添加到模型的相应位置,并添加发光描边效果

// 点击事件
let chooseObj = null;

addEventListener('click', function (event) {
  const px = event.offsetX;
  const py = event.offsetY;

  // 屏幕坐标转标准设备坐标
  const x = (px / window.innerWidth) * 2 - 1;
  const y = -(py / window.innerHeight) * 2 + 1;

  // 创建射线
  const raycaster = new THREE.Raycaster();
  raycaster.setFromCamera(new THREE.Vector2(x, y), camera);

  // 获取存储罐模型对象
  const tank = model.getObjectByName('罐');

  // 遍历并设置所有子对象的祖先属性
  for (let i = 0; i < tank.children.length; i++) {
    const group = tank.children[i];
    group.traverse(function (obj) {
      if (obj.isMesh) {
        obj.ancestors = group;
      }
    });
  }

  // 射线交叉检测
  const intersects = raycaster.intersectObjects(tank.children);
  if (intersects.length > 0) {
    const intersectedObject = intersects[0].object.ancestors;

    // 设置发光描边
    outlinePass.selectedObjects = [intersectedObject];

    // 获取模型对应的标注对象
    const labelObject = model.getObjectByName(intersectedObject.name + '标注');
    if (labelObject) {
      // 将标签添加到模型标注对象上
      labelObject.add(tag);
    }

    // 更新当前选中的对象
    chooseObj = intersectedObject;
  } else {
    // 如果有已经选中的对象,移除发光描边和标签
    if (chooseObj) {
      outlinePass.selectedObjects = []; // 清除发光描边

      const labelObject = model.getObjectByName(chooseObj.name + '标注');
      if (labelObject) {
        // 从模型的标注对象中移除标签
        labelObject.remove(tag);
      }

      // 重置选中的对象
      chooseObj = null;
    }
  }
});
  1. CSS3DRenderer

目前的CSS2DRenderer渲染出来的tag标签,无法随着场景相机同步缩放,因此在此基础上,进行3D渲染,基本上把CSS2DRenderer替换成CSS3DRenderer,再设置tag缩放比例,调整位置就行;

tag.scale.set(0.1,0.1,0.1)

给tag加上style:backface-visibility: hidden;即可在模型背面不显示该tag

  1. CSS3DSprite

CSS3DRenderer是一个普通的 3D HTML 对象,跟随 3D 场景中的旋转和缩放,但不会自动面向相机。 CSS3DSprite会始终面向相机,因此更适合用作 3D 场景中的标签或注释。

只需要const tag = new CSS3DSprite(div);将模型转化成精灵对象即可

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

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

相关文章

Vue3+TS项目封装一个公共的el-table组件二次封装

前言 支持动态传入列&#xff0c;列内容可以指定插槽&#xff0c;指定格式化显示 样式没太写&#xff0c;主要分享基础功能封装 效果 Table组件代码BaseTable.vue <template><el-table :data"data" border><template v-for"col in columns&q…

基于SpringBoot+Vue+MySQL的画师约稿平台系统

系统展示 用户界面 画师界面 管理员界面 系统背景 基于SpringBootVueMySQL的画师约稿平台系统的背景&#xff0c;主要源于数字艺术行业的快速发展与画师、客户双方需求的日益增长。在传统的约稿方式中&#xff0c;往往存在沟通效率低下、交易过程不透明等问题&#xff0c;这限制…

Pandas读取某列、某行数据——loc、iloc区别

loc&#xff1a;通过行、列的名称或标签来索引 iloc&#xff1a;通过行、列的索引位置来寻找数据 首先&#xff0c;我们先创建一个DataFrame生成数据 import pandas as pddata {a:[1,2,3,4,5],b:[6,7,8,9,10],c:[11,12,13,14,15] } data pd.DataFrame(data) print(data) 运行…

IO模型---BIO、NIO、IO多路复用、AIO详解

本篇将想给详细解释一下什么是BIO、NIO、IO多路复用以及AIO~ 同步的阻塞(BIO)和非阻塞(NIO)的区别 BIO&#xff1a;线程发来IO请求后&#xff0c;一直阻塞着IO线程&#xff0c;需要缓冲区这边数据准备好之后&#xff0c;才会进行下一步的操作。 举个&#x1f330;&#xff1…

2024秋季云曦开学考

web ezezssrf 打开环境&#xff0c;代码审计 看起来有点多&#xff0c;要绕过五层 第一层&#xff1a;存在弱比较&#xff0c;使用数组或0e绕过 yunxi[]1&wlgf[]2 yunxis878926199a&wlgfs155964671a 第二层&#xff1a;存在强比较&#xff0c;此处使用string限制…

c++(继承、模板进阶)

一、模板进阶 1、非类型模板参数 模板参数分类类型形参与非类型形参。 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或者typename之类的参数类型名称。 非类型形参&#xff0c;就是用一个常量作为类(函数)模板的一个参数&#xff0c;在类(函数)模板中…

装饰器模式decorator

学习笔记&#xff0c;原文链接 https://refactoringguru.cn/design-patterns/decorator 将对象放入包含行为的特殊封装对象中来为原对象绑定新的行为 调用过程 当你调用 encoded.writeData(salaryRecords); 时&#xff0c;控制流首先进入 CompressionDecorator 的 writeData …

微信小程序原生支持TS、LESS、SASS能力探究

文章目录 原生支持开始使用旧项目新建项目TS声明文件更新 功能说明less 使用全局变量sass 使用全局变量 可以参考原文 在之前开发小程序中&#xff0c;无法使用 less/sass 等 css 预编译语言&#xff0c;也无法使用 TS 进行开发&#xff0c;但在最新的编辑器版本中&#xff0c…

面向对象程序设计之模板进阶(C++)

在之前我出过一篇博客介绍了模版的初阶:面向对象程序设计(C)模版初阶&#xff0c;接下来我们将进行模版的进阶学习&#xff0c;介绍关于更多模版的知识 1.非类型模版参数 模板参数分类类型形参与非类型形参 类型形参即&#xff1a;出现在模板参数列表中&#xff0c;跟在class或…

如何处理DDOS攻击问题

随着信息技术的飞速发展&#xff0c;网络已成为现代社会不可或缺的一部分&#xff0c;极大地便利了个人社交和商业活动。然而&#xff0c;网络空间在创造无限机遇的同时&#xff0c;也潜藏着诸多威胁&#xff0c;其中分布式拒绝服务攻击&#xff08;DDoS&#xff0c;Distribute…

利用Idea远程调试

注意&#xff1a;远程调试不要应用在正式环境中&#xff0c;即便是测试环境也只建议在开发网段使用 在实际的开发过程中&#xff0c;为了验证测试环境的一些问题&#xff0c;且问题在本地不可复现&#xff0c;可以使用远程debug的形式来进行问题定位&#xff0c;而不用循环进行…

如何使用智能合约铸造 NFT —— 以 NftMarket 合约为例

系列文章目录 使用Pinata在IPFS上存储NFT图片的实践&#x1f6aa; scaffold-eth-2使用详细教程&#x1f6aa; 文章目录 系列文章目录前言一、使用到的 OpenZeppelin 库1.1. ERC721 合约1.2. ERC721URIStorage 合约1.3. Counters 合约 二、编写合约代码2.1. 准备NFT元数据2.2. …

【重学 MySQL】二十七、七种 join 连接

【重学 MySQL】二十七、七种 join 连接 union 的使用UNION 的基本用法示例UNION ALL 的用法 七种 join 连接代码实现语法格式小结 union 的使用 UNION 在 SQL 中用于合并两个或多个 SELECT 语句的结果集&#xff0c;并默认去除重复的行。如果希望包含重复行&#xff0c;可以使…

设计模式-行为型模式-访问者模式

访问者模式难以实现&#xff0c;且应用该模式可能会导致代码可读性变差&#xff0c;可维护性变差&#xff0c;除非必要&#xff0c;不建议使用&#xff1b; 1.访问者模式定义 允许在运行时将一个或多个操作应用于一组对象&#xff0c;将操作与对象结构分离&#xff1b; 访问者…

K8s1.28 部署Dashboard获取登录信息

Kubernetes Dashboard 是一个基于 Web 的用户界面&#xff0c;用户可以通过它管理和监控 Kubernetes 集群。它提供了对容器化应用程序的概览、集群资源的状态查看、以及对服务和容器的简单操作管理。 配置 Dashboard 访问的方式&#xff1a; Kubernetes 中的服务类型默认是 C…

语音识别相关概念

声音如何保存成数字信号&#xff1f; 声音是听觉对声波产生的感知&#xff0c;而声波是一种在时间和振幅上连续的模拟量&#xff0c;本质是介质的振动&#xff0c;&#xff0c;比如空气的振动。那么只需要把这个振动信号记录下来&#xff0c;并用一串数字来表达振动信号振动的…

中学生考试成绩在线查询系统

时代在发展&#xff0c;社会在进步&#xff0c;传统的成绩发布方式已经显得力不从心了。老师们&#xff0c;是时候尝试一种更高效、更安全的成绩查询方式了。 还在为如何保护学生隐私而头疼&#xff1f;还在担心成绩的公平性和准确性&#xff1f;易查分小程序将这些这些问题都将…

vue+IntersectionObserver + scrollIntoView 实现电梯导航

一、电梯导航 电梯导航也被称为锚点导航&#xff0c;当点击锚点元素时&#xff0c;页面内相应标记的元素滚动到视口。而且页面内元素滚动时相应锚点也会高亮。电梯导航一般把锚点放在左右两侧&#xff0c;类似电梯一样。 二、scrollIntoView() 介绍 scrollIntoView() 方法会…

加密软件有哪些数据防护功能?

1.文件透明加密&#xff1a;采用透明加密技术&#xff0c;自动对指定类型的敏感文件进行实时加密&#xff0c;确保数据在存储和传输过程中的安全性。 2.权限管理与访问控制&#xff1a;通过细粒度的权限管理&#xff0c;控制员工对敏感数据的访问权限&#xff0c;包括读取、修…

基于SpringBoot+Vue的预制菜平台系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…