首页 前端知识 MarsCode赋能Three.js:如何轻松实现3D模型点云效果

MarsCode赋能Three.js:如何轻松实现3D模型点云效果

2025-03-03 13:03:47 前端知识 前端哥 604 463 我要收藏

前言

点云技术正成为三维视觉开发的热门方向,而 MarsCode 作为强大的交互逻辑工具,与 Three.js 的采样器结合,为复杂点云效果的实现提供了高效解决方案。本文将重点展示如何借助 MarsCode 快速实现动物点云效果,解析其在数据处理与渲染中的核心作用,为开发者打开点云艺术的新思路。文章末尾会放源码地址

项目预览:

  • 本项目使用豆包在线IDE MarsCode IDE 开发
    在这里插入图片描述

一. 项目初始化

使用html/css/js 模版

在这里插入图片描述

项目初始化详情(默认安装了vite),点击顶部运行按钮或使用命令行npm run start即可启动项目

在这里插入图片描述

安装项目依赖, package.json概览

{
"name": "web-test",
"version": "1.0.0",
"description": "",
"scripts": {
"start": "vite --host --port=8000"
},
"devDependencies": {
"vite": "^5.2.12",
"vite-plugin-full-reload": "^1.1.0"
},
"dependencies": {
"three": "0.163.0"
}
}
复制

二. 代码实现

1. threejs初始化配置

初始化场景、相机、渲染器和控制器,代码较为基础,不多赘述

import * as THREE from 'three';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { MeshSurfaceSampler } from 'three/examples/jsm/math/MeshSurfaceSampler.js';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
const renderer = new THREE.WebGLRenderer();
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const control = new OrbitControls(
camera,
renderer.domElement,
);
复制

2. 模型加载

  • 本文使用模型为obj格式,所以使用OBJLoader加载模型资源
  • 使用MeshSurfaceSampler初始化模型采样器
  • 使用采样器的sample方法提取点坐标并存储,方便后续点云绘制
// 添加模型
function addModel() {
new OBJLoader().load(
'/models/Elephant.obj',
(obj) => {
const model = obj.children[0];
model.material = new THREE.MeshBasicMaterial({
wireframe: true,
color: new THREE.Color('#7264B5'),
transparent: true,
opacity: 5,
});
sampler = new MeshSurfaceSampler(model).build();
const tempPosition = new THREE.Vector3();
// 采样点坐标存储
for (let i = 0; i < particleNums; i++) {
sampler.sample(tempPosition);
vertices.push(tempPosition.x, tempPosition.y, tempPosition.z);
}
addParticle(vertices);
}
);
}
复制

3. 点云绘制

通过上述采样器获取的点坐标利用BufferGeometry绘制点云。

// 点云绘制
function addParticle(vertices) {
let colors = [];
const palette = [
new THREE.Color('#88C9B9'), // 青绿色
new THREE.Color('#673AB7'), // 深紫色
new THREE.Color('#009688'), // 深绿色
new THREE.Color('#9C27B0'), // 深紫色
new THREE.Color('#FFC107'), // 深橙色
new THREE.Color('#03A9F4'), // 深蓝色
new THREE.Color('#4CAF50'), // 深绿色
new THREE.Color('#FF5722'), // 深橙色
];
const pointGeometry = new THREE.BufferGeometry();
for (let i = 0; i < particleNums; i++) {
const color = palette[Math.floor(Math.random() * palette.length)];
colors.push(color.r, color.g, color.b);
}
pointGeometry.setAttribute(
'color',
new THREE.Float32BufferAttribute(colors, 3),
);
pointGeometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(vertices, 3),
);
const pointMaterial = new THREE.PointsMaterial({
size: 0.1,
alphaTest: 0.2,
vertexColors: true,
});
const particles = new THREE.Points(pointGeometry, pointMaterial);
scene.add(particles);
}
复制
3.1 详解BufferGeometry

BufferGeometry的核心属性是attributes中的position,uv和color

在这里插入图片描述

  • position:存储三角面坐标,存储方式为Float32Array,相对于普通的数组读写效率会优秀很多

例如创建一个简单的矩形,我们需要俩个三角面,每个三角面需要三个点坐标,一个矩形由两个三角面构成所以需要六个点坐标。

const vertices= new Float32Array( [
-1.0, -1.0, 1.0,
-1.0, 1.0, 1.0,
1.0, 1.0, 1.0,
1.0, 1.0, -1.0,
1.0, 1.0, -1.0,
-1.0, 1.0, 1.0
] );
// itemSize = 3 因为每个顶点都是一个三元组。
geometry.setAttribute( 'position', new THREE.BufferAttribute( vertices, 3 ) );
复制

ps:观察仔细的同学应该能发现其实有俩个点是重复共用的,这个时候可以通过设置索引缓冲区来共用顶点,从而降低顶点数量的生成,优化渲染效率。

geometry.setIndex(new THREE.BufferAttribute(indices, 1));
复制
  • uv:定义纹理坐标,大致同上,不过纹理uv坐标是俩俩一组
  • color:颜色数组,注意点数量会和position保持一致,每三个一组,position中是xyz,而color中是rgb,和position一一对应关系

4. 绘制点云之间的连线

通过采样器选取一个初始点,随机选取和初始点距离不超过30的点绘制两点之间的连线,直至没有孤立点

// 添加线条
function addLines() {
if (sampler) {
const tempPosition = new THREE.Vector3();
const first = previousPoint ? previousPoint.clone() : new THREE.Vector3();
const lineGeometry = new THREE.BufferGeometry();
const lineMaterial = new THREE.LineBasicMaterial({
color: new THREE.Color("#808080"),
opacity: 0.5,
});
let line = new THREE.Line(lineGeometry, lineMaterial);
!previousPoint && sampler.sample(first);
previousPoint = first.clone();
let pointFound = false;
while (!pointFound) {
sampler.sample(tempPosition);
if (tempPosition.distanceTo(previousPoint) < 30) {
lineGeometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(
[
first.x, first.y, first.z,
tempPosition.x, tempPosition.y, tempPosition.z
],
3,
),
);
previousPoint = tempPosition.clone();
pointFound = true;
}
}
scene.add(line);
}
}
复制

此时我发现我的代码好像缺少点注释, 于是就问了下豆包MarcodeAI

const particleNums = 15000;
const vertices = [];
let sampler = null;
let previousPoint;
let lineIndex = 0;
let angle = 0;
复制

回答的很不错,对代码的理解很充分,专有名称也能很好的解释出来,很棒👍️👍

在这里插入图片描述

同时还有一个功能出乎我的意料,可以一键将我的代码替换成豆包回答的代码,省去手动cv,这个交互很方便👍️

在这里插入图片描述

三. 项目提交至仓库

豆包支持代码上传到github,配置好认证信息就可以提交啦!
在这里插入图片描述

四. 结语

如果大家感兴趣可以点击下方链接自行体验一下,欢迎大家在评论区交流,希望可以一键三连,感谢。

豆包体验地址: marscode

代码仓库地址: github

转载请注明出处或者链接地址:https://www.qianduange.cn//article/22387.html
标签
评论
发布的文章

如何修改电脑mac地址?

2025-03-03 13:03:33

C 数组:深入解析与应用

2025-03-03 13:03:28

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!