1.首先node.js是12.22版本的,安装three.js可以参考这篇文章
直接用Threejs入门-安装教程_安装three.js-CSDN博客
直接在终端安装three.js即可
npm install --save three
在相同目录下安装vite构建工具
npm install --save-dev vite
在项目里面看package.json中是否有"three": "^0.164.1",有就安装好了。
然后就使用吧,这里举个例子
<template><div><canvas id="three"></canvas></div>
</template><script>
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'export default {mounted() {this.initThree()},methods: {initThree() {const scene = new THREE.Scene()//There are a few different cameras in three.js. For now, let's use a PerspectiveCamera.//three.js 里有几种不同的相机,在这里,我们使用的是 PerspectiveCamera(透视摄像机)。//第一个参数是视野角度(FOV)。视野角度就是无论在什么时候,你所能在显示器上看到的场景的范围,它的单位是角度(与弧度区分开)。//第二个参数是长宽比(aspect ratio)。 也就是你用一个物体的宽除以它的高的值。比如说,当你在一个宽屏电视上播放老电影时,可以看到图像仿佛是被压扁的。/** 接下来的两个参数是近截面(near)和远截面(far)。* 当物体某些部分比摄像机的远截面远或者比近截面近的时候,该这些部分将不会被渲染到场景中。* 或许现在你不用担心这个值的影响,但未来为了获得更好的渲染性能,你将可以在你的应用程序里去设置它。* */const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)const renderer = new THREE.WebGLRenderer()/** 除了创建一个渲染器的实例之外,我们还需要在我们的应用程序里设置一个渲染器的尺寸。* 比如说,我们可以使用所需要的渲染区域的宽高,来让渲染器渲染出的场景填充满我们的应用程序。* 因此,我们可以将渲染器宽高设置为浏览器窗口宽高。* 对于性能比较敏感的应用程序来说,你可以使用 setSize 传入一个较小的值,例如 window.innerWidth/2 和 window.innerHeight/2,这将使得应用程序在渲染时,以一半的长宽尺寸渲染场景。* 如果你希望保持你的应用程序的尺寸,是以较低的分辨率来渲染,你可以在调用 setSize 时,将 updateStyle(第三个参数)设为 false。* 例如,假设你的 <canvas> 标签现在已经具有了 100% 的宽和高,调用 setSize(window.innerWidth/2, window.innerHeight/2, false) 将使得你的应用程序以四分之一的大小来进行渲染。* */renderer.setSize(window.innerWidth, window.innerHeight)document.body.appendChild(renderer.domElement)/** 最后一步很重要,我们将 renderer(渲染器)的dom元素(renderer.domElement)添加到我们的 HTML 文档中。* 这就是渲染器用来显示场景给我们看的 <canvas> 元素。* *///To create a cube, we need a BoxGeometry. This is an object that contains all the points (vertices) and fill (faces) of the cube. We'll explore this more in the future.//要创建一个立方体,我们需要一个 BoxGeometry(立方体)对象. 这个对象包含了一个立方体中所有的顶点(vertices)和面(faces)。未来我们将在这方面进行更多的探索。const geometry = new THREE.BoxGeometry(1, 1, 1)//接下来,对于这个立方体,我们需要给它一个材质,来让它有颜色。//Three.js 自带了几种材质,在这里我们使用的是 MeshBasicMaterial。/** 所有的材质都存有应用于他们的属性的对象。* 在这里为了简单起见,我们只设置一个color属性,值为 0x00ff00,也就是绿色。* 这里所做的事情,和在 CSS 或者 Photoshop 中使用十六进制(hex colors)颜色格式来设置颜色的方式一致。* */const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 })/** 第三步,我们需要一个 Mesh(网格)。* 网格包含一个几何体以及作用在此几何体上的材质,我们可以直接将网格对象放入到我们的场景中,并让它在场景中自由移动。* */const cube = new THREE.Mesh(geometry, material)/** 默认情况下,当我们调用 scene.add() 的时候,物体将会被添加到 (0,0,0) 坐标。* 但将使得摄像机和立方体彼此在一起。为了防止这种情况的发生,我们只需要将摄像机稍微向外移动一些即可。* */scene.add(cube)camera.position.z = 5//渲染场景/** 现在,如果将之前写好的代码复制到HTML文件中,你不会在页面中看到任何东西。* 这是因为我们还没有对它进行真正的渲染。* 为此,我们需要使用一个被叫做“渲染循环”(render loop)或者“动画循环”(animate loop)的东西。* */function animate() {requestAnimationFrame(animate)//使立方体动起来/** 在开始之前,如果你已经将上面的代码写入到了你所创建的文件中,你可以看到一个绿色的方块。* 让我们来做一些更加有趣的事 —— 让它旋转起来。* 将下列代码添加到 animate() 函数中 renderer.render 调用的上方:* */cube.rotation.x += 0.01cube.rotation.y += 0.01renderer.render(scene, camera)}animate()function resizeRendererToDisplaySize(renderer) {const canvas = renderer.domElementvar width = window.innerWidthvar height = window.innerHeightvar canvasPixelWidth = canvas.width / window.devicePixelRatiovar canvasPixelHeight = canvas.height / window.devicePixelRatioconst needResize = canvasPixelWidth !== width || canvasPixelHeight !== heightif (needResize) {renderer.setSize(width, height, false)}return needResize}},},
}
</script><style scoped>
#three {width: 100%;height: 100%;position: fixed;left: 0;top: 0;
}
</style>
结果如下:
在终端运行
2.2024/5/28
点云数据上传
<template><div><input type="file" id="csvFile" accept=".csv" @click="handleClick" style="pointer-events: auto" /><div style="width: 100%; height: 100vh; position: relative"><canvas id="three"></canvas><div id="overlay" style="position: absolute; top: 0; left: 0; pointer-events: auto"><div class="button-container"><!-- pointer-events: auto下面的元素相应鼠标触摸点击事件,这是默认的 --><!-- <button id="myButton">BUTTON</button>--></div></div></div></div>
</template><script>
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import Papa from 'papaparse'export default {mounted() {this.initThree()// 下面 window.addEventListene是添加一个它会添加一个事件监听器到window对象上,以监听resize事件。当浏览器窗口大小改变时,这个事件会被触发,并执行this.onWindowResize这个方法。注意,这里的this.onWindowResize应该是一个在Vue组件的methods中定义的方法,用于处理窗口大小改变时的逻辑(例如更新摄像机的纵横比或重新渲染场景)。// 将onWindowResize组件里面的方块不会随着外边框的放大缩小而发生变化window.addEventListener('resize', this.onWindowResize, false)},beforeDestroy() {window.removeEventListener('resize', this.onWindowResize, false)// 在这里添加其他清理代码,比如取消动画等},methods: {handleClick() {console.log('Input clicked!')},initThree() {const canvas = document.getElementById('three')const renderer = new THREE.WebGLRenderer({ canvas })renderer.setSize(window.innerWidth, window.innerHeight)const scene = new THREE.Scene()scene.background = new THREE.Color('#ccc')scene.environment = new THREE.Color('#ccc')// 创建一个光源,因为默认的THREE.Scene是没有光源的const light = new THREE.AmbientLight(0x404040) // soft white lightscene.add(light)// 初始化相机,设置其位置const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)camera.position.z = 5 // 把相机向后移动一些,以便能看到场景中的物体const controls = new OrbitControls(camera, renderer.domElement)controls.update()// 设置一下参数const b = 1const geometry = new THREE.BoxGeometry(b, 1, 1)const material = new THREE.MeshBasicMaterial({ color: 0xfff })// const cube = new THREE.Mesh(geometry, material)// cube.position.x += 4// scene.add(cube)// const geometry1 = new THREE.ConeGeometry(1, 1, 32)// const cone = new THREE.Mesh(geometry1, material)// scene.add(cone)const geometry2 = new THREE.SphereGeometry(1, 32, 10)// const sphere = new THREE.Mesh(geometry2, material)// scene.add(sphere)const csvFileInput = document.getElementById('csvFile')csvFileInput.addEventListener('change', function (e) {const file = e.target.files[0]if (file) {const reader = new FileReader()reader.onload = function (e) {const text = e.target.result // 读取的文件内容const lines = text.split('\n') // 按行分割// 跳过标题行(如果有)let dataLines = lines.slice(1)// 解析点和颜色数据const points = []const colors = []for (let i = 0; i < dataLines.length; i++) {const lineData = dataLines[i].split(',') // 按逗号分割每行数据if (lineData.length === 6) {// 假设每行包含7个元素(x, y, z, r, g, b, a)points.push(parseFloat(lineData[0]), parseFloat(lineData[1]), parseFloat(lineData[2]))colors.push(parseFloat(lineData[3]), parseFloat(lineData[4]), parseFloat(lineData[5]))}}// 创建Float32Arrayconst pointsArray = new Float32Array(points)const colorsArray = new Float32Array(colors)const geometry3 = new THREE.BufferGeometry()geometry3.setAttribute('position', new THREE.BufferAttribute(pointsArray, 3))if (colors) {geometry3.setAttribute('color', new THREE.BufferAttribute(colorsArray, 3)) // 假设每个颜色由 4 个浮点数表示(RGBA)}const material1 = new THREE.PointsMaterial({size: 0.05, // 点的大小vertexColors: true, // 如果使用了颜色数组,则启用此选项// 其他属性...})const pointsObject = new THREE.Points(geometry3, material1)scene.add(pointsObject)// 在这里,你可以使用WebGL或其他图形API来渲染这些数据// ...}reader.readAsText(file) // 以文本格式读取文件}})// sphere.position.x = -4camera.position.z = 5// 加载模型(这里只是一个示例,你可能需要替换为你的模型)// 渲染循环function animate() {requestAnimationFrame(animate)// cube.rotation.x += 0.01// cube.rotation.y += 0.01// cone.rotation.x += 0.01// cone.rotation.y += 0.01// sphere.rotation.x += 0.01// sphere.rotation.y += 0.01renderer.render(scene, camera)}animate()// 窗口大小变化时的处理函数this.onWindowResize = () => {camera.aspect = window.innerWidth / window.innerHeightcamera.updateProjectionMatrix()renderer.setSize(window.innerWidth, window.innerHeight)}// 如果使用OrbitControls,可以在这里初始化它// const controls = new OrbitControls(camera, renderer.domElement)},},
}
</script>
<style scoped>
#csvFile {width: 100px;height: 100px;z-index: 265; /* 确保按钮在画布之上265大于100所以能放在前面 */
}
#three {position: absolute;width: 100%;text-align: center;z-index: 100;display: block;
}
#overlay {width: 100%;height: 100%;display: flex;/* 垂直方向排列column*/flex-direction: column;align-items: center;/* center意味着子元素将在垂直方向上居中对齐。*//*justify-content: center;*/pointer-events: none;
}
#overlay button {pointer-events: auto; /* 允许按钮上的点击事件 */
}
.button-container {margin-top: 1px; /* 使得元素在垂直方向上被推到容器的底部 */align-self: flex-end; /* 在水平方向上对齐到容器的右边 */pointer-events: none; /* 这个可能不需要,除非你想要防止容器本身接收点击事件 */z-index: 267;
}
</style>