当前位置: 首页 > article >正文

在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.kler.cn/a/553570.html

相关文章:

  • 【Docker】《一文深度解析Docker核心技术:镜像、存储卷与网络架构原理》
  • itemgetter() 是 Python operator 模块中的一个函数,主要用于从 字典、列表、元组等数据结构中取值
  • Redis_基础
  • Jeesite5:Star24k,Spring Boot 3.3+Vue3实战开源项目,架构深度拆解!让企业级项目开发效率提升300的秘密武器
  • ubuntu22.04安装tigervnc使用vnc进行访问
  • 《Python在数据可视化中的应用与实践》
  • 【黑马点评优化】2-Canel实现多级缓存(Redis+Caffeine)同步
  • 负载均衡 方式
  • 【Elasticsearch】搜索时分片路由
  • Go入门之函数
  • 高并发系统架构设计全链路指南
  • 自制AirTag,支持安卓/鸿蒙/PC/Home Assistant,无需拥有iPhone
  • 应用程序中的网络协议:原理、应用与挑战
  • Java 大视界 -- 企业数字化转型中的 Java 大数据战略与实践(93)
  • 什么是网关,网关的作用是什么?网络安全零基础入门到精通实战教程!
  • 当Qt遇见IOCP:用C++打造高并发服务器
  • 【物种分布】基于R语言、MaxEnt模型融合技术的物种分布模拟、参数优化方法、结果分析制图与论文写作
  • 51单片机学习之旅——在LCD1602上显示时钟
  • FPGA实现GTY光口视频转USB3.0传输,基于FT601+Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
  • DeepSeek-R1:通过强化学习激励大型语言模型的推理能力