点云可视化2
点云可视化2
three.js
全部使用的本地文件,需要下载的文件有: /lib/lil-gui.module.min.js /lib/main.js /lib/mainfetch.js /lib/OrbitControls.js /lib/PCDLoader.js /lib/PCLCutter.js /lib/PCLFilter.js /lib/PCLKeyPoints.js /lib/three.core.js /lib/three.module.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<html>
<head>
<meta charset="utf-8">
<title>PCD visulize</title>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<!-- <script type="importmap">
{
"imports": {
"three": "https://cdn.jsdelivr.net/npm/three@0.158.0/build/three.module.js",
"three/addons/": "https://cdn.jsdelivr.net/npm/three@0.158.0/examples/jsm/"
}
}
</script> -->
<script type="importmap">
{
"imports": {
"three": "./lib/three.module.js",
"three/addons/": "./lib/"
}
}
</script>
<script type="module" src="lib/mainfetch.js"> </script>
<!-- <script type="module" src="lib/main.js"> </script> -->
</body>
</html>
通过读取pcd文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
import * as THREE from "./three.module.js";
import { OrbitControls } from "./OrbitControls.js";
import { PCDLoader } from "./PCDLoader.js";
import { GUI } from "./lil-gui.module.min.js";
// 获取 URL 参数函数
function getUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
const url = urlParams.get("url");
const port = urlParams.get("port");
console.log("URL:", url);
console.log("Port:", port);
}
// 调用函数
getUrlParams();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
80,
window.innerWidth / window.innerHeight,
0.01,
10000000
);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
const isRotation = { bool: false };
var gui = new GUI();
var attributesFolder = gui.addFolder("点云设置");
gui.domElement.style.left = "0px";
function resetGUI() {
// 删除之前的GUI
gui.destroy();
// 创建一个新的GUI实例
gui = new GUI();
attributesFolder = gui.addFolder("点云设置");
gui.domElement.style.left = "0px";
}
var helpCamera = [];
for (let i = 0; i < 4; i++) {
helpCamera[i] = new THREE.PerspectiveCamera(60, 1, 0.1, 0.4);
scene.add(helpCamera[i]);
}
const transformMatrix0 = new THREE.Matrix4();
transformMatrix0.set(
0.9635227966591445,
-0.0298251417806896,
-0.2659591721221557,
-3.1861460134378618,
0.04168012934974072,
0.9983679551673119,
0.03904091331448917,
-0.0658694912288581,
0.264360714054735,
-0.04870202267670474,
0.963193400024973,
1.701830863209624117,
0,
0,
0,
1
);
helpCamera[0].applyMatrix4(transformMatrix0);
const transformMatrix1 = new THREE.Matrix4();
transformMatrix1.set(
0.8671344194352608,
-0.01285630331924969,
-0.4979082386300075,
-1.981515886805006,
0.03166906549661311,
0.9990671872561505,
0.02935686697614572,
-0.0212592897059282,
0.4970663626933977,
-0.04122463842227529,
0.8667326925100427,
2.75149718348900723,
0,
0,
0,
1
);
helpCamera[1].applyMatrix4(transformMatrix1);
const transformMatrix2 = new THREE.Matrix4();
transformMatrix2.set(
0.7024094659673048,
-0.007144654873624021,
-0.711737238049452,
-2.685856668225444,
0.09031055886130245,
0.9927625554048429,
0.07916130079909767,
-0.0514197827631538,
0.7060204990492023,
-0.1198810347502172,
0.6979710541487608,
2.332535510893329,
0,
0,
0,
1
);
helpCamera[2].applyMatrix4(transformMatrix2);
const transformMatrix3 = new THREE.Matrix4();
transformMatrix3.set(
0.5308375671028522,
0.00925889315102485,
-0.8474230054995811,
-3.381832006499801,
0.1320681431688673,
0.9868199683489367,
0.09351125936341173,
-0.0917595736102196,
0.8371197542241209,
-0.1615568722321077,
0.5226183063406084,
1.036010012067961,
0,
0,
0,
1
);
helpCamera[3].applyMatrix4(transformMatrix3);
// 初始点云生成
const loader = new PCDLoader();
loader.load("./static/ronghe.pcd", function (points) {
points.geometry.center();
points.geometry.rotateX(Math.PI);
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.08,
vertexColors: true,
});
const pointCloud = new THREE.Points(points.geometry, material);
scene.add(pointCloud);
for (let i = 0; i < helpCamera.length; i++) {
helpCamera[i].lookAt(pointCloud.position);
}
// 计算点云的边界框
const box = new THREE.Box3().setFromObject(pointCloud);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
// 根据点云的大小调整相机的初始位置
camera.position.copy(center);
camera.position.z += size.length() / 3; // 距离点云的适当距离,放大视角
camera.lookAt(center); // 确保相机始终朝向点云中心
const folder = attributesFolder.addFolder(`点云 0`);
const text = {
pointsNum: points.geometry.attributes.position.count,
file: "初始pcd",
};
folder.add(text, "file").name("文件");
folder.add(text, "pointsNum").name("点数");
folder.add(material, "size", 0, 2).name("点大小");
folder.addColor(material, "color").name("点颜色");
folder
.add(material, "vertexColors")
.name("显示顶点颜色")
.onChange(function () {
material.needsUpdate = true; // 手动更新材质
});
//加载图片
// const textureLoader = new THREE.TextureLoader();
// const texture = textureLoader.load("../static/bg.png");
// const planeGeometry = new THREE.PlaneGeometry(300, 180); // 创建一个平面几何体,大小可以调整
// const planeMaterial = new THREE.MeshBasicMaterial({
// map: texture,
// side: THREE.DoubleSide,
// });
// const plane = new THREE.Mesh(planeGeometry, planeMaterial);
// // 设置平面位置
// plane.position.set(10, 0, -15); // 可以根据需要调整平面的位置
// plane.rotation.z = -Math.PI / 72;
// scene.add(plane);
});
const input = document.createElement("input");
const button = document.createElement("button");
button.textContent = "截图";
button.style.position = "absolute";
button.style.top = "10px";
button.style.right = "10px";
button.onclick = function () {
input.click();
};
document.body.appendChild(button);
// 点击按钮后,截取当前画面并保存为图片
button.onclick = function () {
// 确保场景已渲染
renderer.render(scene, camera);
const imageURL = renderer.domElement.toDataURL("image/png"); // 获取渲染画布的图片数据
const link = document.createElement("a");
link.href = imageURL; // 设置下载链接
link.download = "screenshot.png"; // 设置下载的文件名
link.click(); // 触发下载
};
// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
// 重置渲染器输出画布,相机
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
function animate() {
if (isRotation.bool) {
scene.rotation.y += 0.005;
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
通过mock接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import * as THREE from "./three.module.js";
import { OrbitControls } from "./OrbitControls.js";
import { PCDLoader } from "./PCDLoader.js";
import { GUI } from "./lil-gui.module.min.js";
// 获取 URL 参数函数
function getUrlParams() {
const urlParams = new URLSearchParams(window.location.search);
const url = urlParams.get("url");
const port = urlParams.get("port");
console.log("URL:", url);
console.log("Port:", port);
}
// 调用函数
getUrlParams();
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
80,
window.innerWidth / window.innerHeight,
0.01,
10000000
);
camera.position.set(0, 0, 10);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
const isRotation = { bool: false };
var gui = new GUI();
var attributesFolder = gui.addFolder("点云设置");
gui.domElement.style.left = "0px";
function loadPointCloudFromAPI(apiUrl) {
// 使用 fetch 获取 PCD 文件
fetch(apiUrl)
.then((response) => {
if (!response.ok) {
throw new Error("网络响应失败");
}
return response.arrayBuffer(); // 获取二进制数据
})
.then((data) => {
console.log("加载的原始二进制数据:", data); // 打印原始二进制数据
// 使用 PCDLoader 的 load 方法来加载数据
const loader = new PCDLoader();
loader.load(
URL.createObjectURL(new Blob([data])), // 将二进制数据转换为 URL 对象
function (points) {
console.log("点云数据加载成功:", points); // 打印点云数据
if (!points || !points.geometry) {
console.error("点云加载失败,未获取到几何数据");
return;
}
points.geometry.center();
points.geometry.rotateX(Math.PI);
const material = new THREE.PointsMaterial({
color: 0xffffff,
size: 0.08,
vertexColors: true,
});
const pointCloud = new THREE.Points(points.geometry, material);
scene.add(pointCloud); // 添加到场景
// 为每个辅助相机设置视角
for (let i = 0; i < helpCamera.length; i++) {
helpCamera[i].lookAt(pointCloud.position);
}
// 计算点云的边界框
const box = new THREE.Box3().setFromObject(pointCloud);
const center = box.getCenter(new THREE.Vector3());
const size = box.getSize(new THREE.Vector3());
// 根据点云的大小调整相机的初始位置
camera.position.copy(center);
camera.position.z += size.length() / 3; // 距离点云的适当距离,放大视角
camera.lookAt(center); // 确保相机始终朝向点云中心
// 添加GUI控制面板
const folder = attributesFolder.addFolder(`点云 0`);
const text = {
pointsNum: points.geometry.attributes.position.count,
file: "API 获取的 pcd",
};
folder.add(text, "file").name("文件");
folder.add(text, "pointsNum").name("点数");
folder.add(material, "size", 0, 2).name("点大小");
folder.addColor(material, "color").name("点颜色");
folder
.add(material, "vertexColors")
.name("显示顶点颜色")
.onChange(function () {
material.needsUpdate = true; // 手动更新材质
});
},
undefined, // 进度回调,这里可以留空
function (error) {
console.error("PCD 加载时出错:", error); // 捕获加载过程中的错误
}
);
})
.catch(function (error) {
console.error("加载 PCD 数据失败:", error);
});
}
loadPointCloudFromAPI("http://192.168.2.207:3000/pcd"); // 替换为你的 API URL
const input = document.createElement("input");
const button = document.createElement("button");
button.textContent = "截图";
button.style.position = "absolute";
button.style.top = "10px";
button.style.right = "10px";
button.onclick = function () {
input.click();
};
document.body.appendChild(button);
// 点击按钮后,截取当前画面并保存为图片
button.onclick = function () {
// 确保场景已渲染
renderer.render(scene, camera);
const imageURL = renderer.domElement.toDataURL("image/png"); // 获取渲染画布的图片数据
const link = document.createElement("a");
link.href = imageURL; // 设置下载链接
link.download = "screenshot.png"; // 设置下载的文件名
link.click(); // 触发下载
};
// onresize 事件会在窗口被调整大小时发生
window.onresize = function () {
// 重置渲染器输出画布,相机
renderer.setSize(window.innerWidth, window.innerHeight);
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
};
function animate() {
if (isRotation.bool) {
scene.rotation.y += 0.005;
}
renderer.render(scene, camera);
requestAnimationFrame(animate);
}
animate();
另附mock接口
需要下载express
npm install express
npm install cors ———–>解决跨域的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const express = require("express");
const fs = require("fs");
const cors = require("cors");
const path = require("path");
const app = express();
const port = 3000;
// 使用 CORS 中间件
app.use(cors());
// 模拟返回 PCD 文件的接口
app.get("/pcd", (req, res) => {
// 读取本地的 PCD 文件
const pcdFilePath = path.join(__dirname, "../static/ronghe.pcd"); // 替换为你实际的 PCD 文件路径
fs.readFile(pcdFilePath, (err, data) => {
if (err) {
res.status(500).send("服务器错误");
return;
}
res.setHeader("Content-Type", "application/octet-stream");
res.send(data);
});
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器正在运行,端口:${port}`);
});
This post is licensed under CC BY 4.0 by the author.