在Vue项目中使用three.js在前端页面展示PLY文件或STL文件

前言:这是一个3d打印局域网管理系统的需求

一、安装three.js

three.js官网:https://threejs.org/docs/#manual/en/introduction/Installation

我用的是yarn,官网用的是npm

二、使用three.js

1.在script部分导入three.js

import * as THREE from 'three';

2.基础使用

=》创建场景:场景是three.js中用于存储所有3D对象的容器。它相当于一个虚拟的3D空间,所有对象(如几何体、灯光等)都会被添加到场景中。

const scene = new THREE.Scene();//创建场景

=》创建相机:相机用于定义用户观察场景的视角。这里使用的是PerspectiveCamera,它模拟了真实世界的透视效果。

//创建相机
const camera = new THREE.PerspectiveCamera(75,window.innerWidth/window.innerHeight,0.1,1000);

它的参数分别是:

->FOV(Field of View):视野角度,单位是度。这里设置为75,表示相机能看到的范围。

->宽高比(Aspect Ratio):通常是容器的宽度除以高度。这里使用window.innerWidth / window.innerHeight,确保在场景不同屏幕尺寸下不会变形。

->近裁剪面(Near Clipping Plane):距离相机最近的渲染范围,小于这个值的物体不会被渲染。这里设置为0.1。

->远裁剪面(Far Clipping Plane):距离相机最远的渲染范围,大于这个值的物体不会被渲染。这里设置为1000。

=》创建渲染器:渲染器的作用是将场景渲染到屏幕上。这里使用的是WebGLRenderer,它利用WebGL技术在浏览器中渲染3D图形。setSize方法设置了渲染器的尺寸,通常与浏览器窗口的大小一致。最后,将渲染器的domElement(一个<canvas>元素)添加到HTML文档中。

//创建渲染器
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth,window.innerHeight);
document.body.appendChild(renderer.domElement);

=》创建立方体:

//创建立方体
const geometry = new THREE.BoxGeometry(1,1,1);
const material = new THREE.MeshBasicMaterial({color:0x00ff00});
const cube = new THREE.Mesh(geometry,material);
scene.add(cube); 

->几何体(Geometry):定义了对象的形状。这里使用BoxGeometry创建了一个边长为1的立方体。

->材质(Material):定义了对象的外观。这里使用MeshBasicMaterial,并设置颜色为绿色(0x00ff00)。

->网格(Mesh):将几何体和材质组合在一起,形成一个可渲染的对象。最后,将立方体添加到场景中。

=》调整相机位置:默认情况下,相机和立方体都在场景的原点(0, 0, 0)。为了避免它们重叠,将相机沿Z轴向后移动5个单位。

//调整相机位置
camera.position.z = 5;

=》动画循环:

//动画循环
function animate(){cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene,camera);}
renderer.setAnimationLoop(animate);

->动画逻辑:在animate函数中,每帧更新立方体的旋转角度(rotation.x和rotation.y),使其围绕X轴和Y轴旋转。

->渲染场景:调用renderer.render(scene, camera)将场景渲染到屏幕上。

->循环调用:renderer.setAnimationLoop(animate)会自动调用animate函数,并在浏览器刷新时重新渲染场景,通常每秒60次。

3.综合使用

这里创建一个文件名为Three.vue的vue文件

<template><div ref="threeJsContainer" class="three-js-container"></div></template><script>import * as THREE from 'three';export default {name: 'ThreeJsComponent',mounted() {this.initThreeJs();},methods: {initThreeJs() {// 创建场景const scene = new THREE.Scene();// 创建相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);camera.position.z = 5;// 创建渲染器const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);// 将渲染器的 canvas 添加到 Vue 组件的 div 中this.$refs.threeJsContainer.appendChild(renderer.domElement);// 创建立方体const geometry = new THREE.BoxGeometry(1, 1, 1);const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 });const cube = new THREE.Mesh(geometry, material);scene.add(cube);// 动画循环const animate = () => {cube.rotation.x += 0.01;cube.rotation.y += 0.01;renderer.render(scene, camera);};renderer.setAnimationLoop(animate);}}};</script><style scoped>.three-js-container {width: 100vw;height: 100vh;}</style>

在App组件中导入并使用这个组件,不出意外就可看到一个旋转的绿色正方体

4.展示ply文件

我下载了blender软件,导出了一个ply文件,这个ply文件放在项目的public文件夹

先导入所需的文件

import * as THREE from 'three';
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js';
<template><h1>我是PLY</h1><div ref="threeContainer" class="three-container"></div><h1>你呢</h1>
</template><script>
import * as THREE from 'three';
import { PLYLoader } from 'three/examples/jsm/loaders/PLYLoader.js';export default {name: 'PLY',mounted() {this.initThree();},methods: {initThree() {console.log("ply");// 创建场景const scene = new THREE.Scene();// 创建相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(0, 0, 10); // 调整相机位置camera.lookAt(scene.position); // 让相机朝向场景中心// 添加光源const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);directionalLight.position.set(1, 1, 1);scene.add(directionalLight);// 创建渲染器const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);// 将渲染器的 canvas 添加到 Vue 组件的 div 中this.$refs.threeContainer.appendChild(renderer.domElement);// 加载 .ply 文件let mesh; // 声明一个变量用于存储加载的网格const loader = new PLYLoader();loader.load('/2.ply', function (geometry) {// 计算顶点法线geometry.computeVertexNormals();const material = new THREE.MeshStandardMaterial({ color: 0xffffff });mesh = new THREE.Mesh(geometry, material); // 将加载的网格存储到变量中scene.add(mesh);});// 动画循环const animate = () => {if (mesh) {// 每帧让物体绕 x 轴和 y 轴旋转mesh.rotation.x += 0.01;mesh.rotation.y += 0.01;}renderer.render(scene, camera);};renderer.setAnimationLoop(animate);}}
};
</script>
<style scoped>
.three-container {width: 100%;height: 100%;
}
</style>

5.展示stl文件

文件是一个同学发给我的,一个城堡

其实这个是和ply同理的,只是导入的文件不同

import * as THREE from 'three';import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';
<template><h1>我是STL</h1><div ref="threeContainer" class="three-container"></div><h1>你呢</h1></template><script>import * as THREE from 'three';import { STLLoader } from 'three/examples/jsm/loaders/STLLoader.js';export default {name: 'STL',mounted() {this.initThree();},methods: {initThree() {console.log("stl");// 创建场景const scene = new THREE.Scene();// 创建相机const camera = new THREE.PerspectiveCamera(75,window.innerWidth / window.innerHeight,0.1,1000);camera.position.set(0, 0, 10); // 调整相机位置camera.lookAt(scene.position); // 让相机朝向场景中心// 添加光源const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);scene.add(ambientLight);const directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);directionalLight.position.set(1, 1, 1);scene.add(directionalLight);// 创建渲染器const renderer = new THREE.WebGLRenderer();renderer.setSize(window.innerWidth, window.innerHeight);// 将渲染器的 canvas 添加到 Vue 组件的 div 中this.$refs.threeContainer.appendChild(renderer.domElement);// 加载 .stl 文件let mesh; // 用于存储加载的模型const loader = new STLLoader(); // 使用 STLLoader 加载 STL 文件loader.load('/chengbao.stl', function (geometry) {// STL 文件通常不需要计算顶点法线,因为它们是三角形网格// 居中几何体(确保模型的几何中心在原点)geometry.center();// 创建渐变材质const uniforms = {iTime: { value: 0 },iResolution: { value: new THREE.Vector2(window.innerWidth, window.innerHeight) }};const material = new THREE.ShaderMaterial({uniforms: uniforms,vertexShader: `varying vec2 vUv;void main() {vUv = uv;gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);}`,fragmentShader: `precision highp float;uniform float iTime;uniform vec2 iResolution;varying vec2 vUv;// 渐变颜色定义const vec3 colors[5] = vec3[](vec3(0., 0., 0.6),vec3(0., 1., 1.),vec3(0.0, 1.0, 0.),vec3(1.0, 1.0, 0.),vec3(1.0, 0.0, 0.));const float points[5] = float[](0.0, 0.15, 0.5, 0.65, 1.0);vec3 gradian(vec3 c1, vec3 c2, float a) {return mix(c1, c2, a);}vec3 heat4(float weight) {if (weight <= points[0]) return colors[0];if (weight >= points[4]) return colors[4];for (int i = 1; i < 5; i++) {if (weight < points[i]) {float a = (weight - points[i - 1]) / (points[i] - points[i - 1]);return gradian(colors[i - 1], colors[i], a);}}return vec3(0.0);}void main() {float weight = (vUv.x + vUv.y + iTime) / 10.0; // 根据时间动态调整权重vec3 color = heat4(weight);float alpha = sin(iTime) * 0.5 + 0.5; // 透明度在 0 到 1 之间变化gl_FragColor = vec4(color, alpha);}`,transparent: true // 启用透明度});mesh = new THREE.Mesh(geometry, material); // 创建网格mesh.position.set(0, 0, 0); // 确保模型在场景中心mesh.scale.set(0.1, 0.1, 0.1); // 调整模型大小(根据需要)scene.add(mesh); // 将模型添加到场景中});// 鼠标滑动控制渐变速度和模型旋转let lastTime = performance.now();let lastMouseX = 0;let lastMouseY = 0;renderer.domElement.addEventListener('mousemove', (event) => {const now = performance.now();const deltaTime = (now - lastTime) / 1000; // 时间差(秒)const mouseX = event.clientX;const mouseY = event.clientY;const deltaX = mouseX - lastMouseX;const deltaY = mouseY - lastMouseY;const speed = Math.sqrt(deltaX * deltaX + deltaY * deltaY); // 鼠标滑动速度const speedFactor = speed * deltaTime; // 速度因子if (mesh) {mesh.material.uniforms.iTime.value += speedFactor; // 根据鼠标滑动速度调整时间mesh.rotation.y += deltaX * 0.001; // 水平旋转mesh.rotation.x -= deltaY * 0.001; // 垂直旋转}lastTime = now;lastMouseX = mouseX;lastMouseY = mouseY;});// 动画循环const animate = () => {renderer.render(scene, camera); // 渲染场景};renderer.setAnimationLoop(animate); // 设置动画循环}}};</script><style scoped>.three-container {width: 100%;height: 100%;}</style>

我这个做了更花哨的效果,可以渐变

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

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

相关文章

DeepSeek 助力 Vue 开发:打造丝滑的右键菜单(RightClickMenu)

前言&#xff1a;哈喽&#xff0c;大家好&#xff0c;今天给大家分享一篇文章&#xff01;并提供具体代码帮助大家深入理解&#xff0c;彻底掌握&#xff01;创作不易&#xff0c;如果能帮助到大家或者给大家一些灵感和启发&#xff0c;欢迎收藏关注哦 &#x1f495; 目录 Deep…

大数据学习(46) - Flink按键分区处理函数

&&大数据学习&& &#x1f525;系列专栏&#xff1a; &#x1f451;哲学语录: 承认自己的无知&#xff0c;乃是开启智慧的大门 &#x1f496;如果觉得博主的文章还不错的话&#xff0c;请点赞&#x1f44d;收藏⭐️留言&#x1f4dd;支持一下博主哦&#x1f91…

10分钟上手DeepSeek开发:SpringBoot + Vue2快速构建AI对话系统

作者&#xff1a;后端小肥肠 目录 1. 前言 为什么选择DeepSeek&#xff1f; 本文技术栈 2. 环境准备 2.1. 后端项目初始化 2.2. 前端项目初始化 3. 后端服务开发 3.1. 配置文件 3.2. 核心服务实现 4. 前端服务开发 4.1. 聊天组件ChatWindow.vue开发 5. 效果展示及源…

Transformer多头注意力并行计算原理与工业级实现:从数学推导到PyTorch工程优化

一、核心数学原理剖析 1.1 多头注意力矩阵分解 Q XW^Q ∈ R^{nd_k} K XW^K ∈ R^{nd_k} V XW^V ∈ R^{nd_v} 多头分解公式&#xff1a; head_i Attention(QW_i^Q, KW_i^K, VW_i^V) 其中 W_i^Q ∈ R^{d_kd_k/h}, W_i^K ∈ R^{d_kd_k/h}, W_i^V ∈ R^{d_vd_v/h} (h为头数…

通过监督微调提升多语言大语言模型性能

引言 澳鹏助力一家全球科技公司提升其大语言模型&#xff08;LLM&#xff09;的性能。通过提供结构化的人工反馈形式的大语言模型训练数据&#xff0c;让该模型在30多种语言、70多种方言中的表现得到优化。众包人员们进行多轮对话&#xff0c;并依据回复的相关性、连贯性、准确…

大数据开发治理平台~DataWorks(核心功能汇总)

目录 数据集成 功能概述 使用限制 功能相关补充说明 数据开发 功能概述 数据建模 功能概述 核心技术与架构 数据分析 功能概述 数据治理 数据地图 功能概述 数据质量 功能概述 数据治理资产 功能概述 使用限制 数据服务 功能概述 数据集成 DataWorks的数据…

用Nginx打造防盗链护盾

用Nginx打造防盗链护盾 一、你的网站正在"为他人做嫁衣"&#xff1f; 想象一下这个场景&#xff1a; 你精心拍摄的摄影作品、录制的课程视频、设计的原创素材&#xff0c;被其他网站直接盗用链接。 更气人的是——当用户在他们网站查看这些资源时&#xff0c;消耗的…

STM32 看门狗

目录 背景 独立看门狗&#xff08;IWDG&#xff09; 寄存器访问保护 窗口看门狗&#xff08;WWDG&#xff09; 程序 独立看门狗 设置独立看门狗程序 第一步、使能对独立看门狗寄存器的写操作 第二步、设置预分频和重装载值 第三步、喂狗 第四步、使能独立看门狗 喂狗…

Kubernetes的Ingress 资源是什么?

在Kubernetes中&#xff0c;Ingress资源是一种用于管理集群外部对内部服务访问的API对象&#xff0c;主要用于将不同的外部请求路由到集群内的不同服务&#xff0c;以下是关于它的详细介绍&#xff1a; 定义与作用 Ingress资源定义了从集群外部到内部服务的HTTP和HTTPS路由规…

vue3-03初学vue3中的配置项setup(Composition API (组合API组件中所用到的:数据、方法等,均要配置在setup中)

1.关于setup Vue3.0中一个新的配置项&#xff0c;值为一个函数.setup是所有Composition API (组合API)“表演的舞台”m组件中所用到的:数据、方法等等&#xff0c;均要配置在setup中。 2..setup函数使用 setup函数的两种返回值 1.若返回一个对象&#xff0c;则对象中的属性、…

【go语言规范】 使用函数式选项 Functional Options 模式处理可选配置

如何处理可选配置&#xff1f; Config Struct 方式 (config-struct/main.go) 这是最简单的方式&#xff0c;使用一个配置结构体&#xff1a; 定义了一个简单的 Config 结构体&#xff0c;包含 Port 字段创建服务器时直接传入配置对象优点&#xff1a;简单直接缺点&#xff1a…

leetcode 2585. 获得分数的方法数

题目如下 数据范围 莫要被困难的外衣骗了&#xff0c;本题就是有数量限制的完全背包问题。显然我们可以令 f(x,y)为当有x种题目时分数为y时的方法数 令某种题目的数量为k 那么方法数应该是 f(x,y) f(x - 1,y - k * (分值))其中(0 < k < 题目数量)通过代码 class So…

深入理解JavaScript中的异步编程与Promise

一、引言 在JavaScript的世界中&#xff0c;异步编程是一个核心概念&#xff0c;尤其是在处理网络请求、文件操作或任何可能阻塞主线程的任务时。本文将深入探讨JavaScript中的异步编程模型&#xff0c;特别是Promise对象的使用。 二、异步编程基础 2.1 什么是异步编程&…

VS Code 如何搭建C/C++开发环境

目录 1.VS Code是什么 2. VS Code的下载和安装 2.1 下载和安装 2.2.1 下载 2.2.2 安装 2.2 环境的介绍 2.3 安装中文插件 3. VS Code配置C/C开发环境 3.1 下载和配置MinGW-w64编译器套件 3.1.1 下载 3.1.2 配置 3.2 安装C/C插件 3.3 重启VSCode 4. 在VSCode上编写…

如何查询网站是否被百度蜘蛛收录?

一、使用site命令查询 这是最直接的方法。在百度搜索框中输入“site:你的网站域名”&#xff0c;例如“site:example.com”&#xff08;请将“example.com”替换为你实际的网站域名&#xff09;。如果搜索结果显示了你的网站页面&#xff0c;并且显示了收录的页面数量&#xf…

数仓搭建:DWS层(服务数据层)

DWS层示例: 搭建日主题宽表 需求 维度 步骤 在hive中建数据库dws >>建表 CREATE DATABASE if NOT EXISTS DWS; 建表sql CREATE TABLE yp_dws.dws_sale_daycount( --维度 city_id string COMMENT 城市id, city_name string COMMENT 城市name, trade_area_id string COMME…

伪类选择器

作用&#xff1a;选中特殊状态的元素 一、动态伪类 1. :link 超链接 未被访问 的状态。 2. :visited 超链接 访问过 的状态。 3. :hover 鼠标 悬停 在元素上的状态。 4. :active 元素 激活 的状态。 什么是激活&#xff1f; —— 按下鼠标不松开。 注意点&#xf…

Kubernetes:EKS 中 Istio Ingress Gateway 负载均衡器配置及常见问题解析

引言 在云原生时代&#xff0c;Kubernetes 已经成为容器编排的事实标准。AWS EKS (Elastic Kubernetes Service) 作为一项完全托管的 Kubernetes 服务&#xff0c;简化了在 AWS 上运行 Kubernetes 的复杂性。Istio 作为服务网格领域的佼佼者&#xff0c;为微服务提供了流量管理…

Docker安装Kafka(不依赖ZooKeeper)

创建docker-compose.yaml version: "3.9" #版本号 services:kafka:image: apache/kafka:3.9.0container_name: kafkahostname: kafkaports:- 9092:9092 # 容器内部之间使用的监听端口- 9094:9094 # 容器外部访问监听端口environment:KAFKA_NODE_ID: 1KAFKA_PROCES…

挪车小程序挪车二维码php+uniapp

一款基于FastAdminThinkPHP开发的匿名通知车主挪车微信小程序&#xff0c;采用匿名通话的方式&#xff0c;用户只能在有效期内拨打车主电话&#xff0c;过期失效&#xff0c;从而保护车主和用户隐私。提供微信小程序端和服务端源码&#xff0c;支持私有化部署。 更新日志 V1.0…