点云是 3D 的一个常见业务场景。
它是通过一堆点来展示三维物体。
这个 Three.js 也提供了相应的 API
https://threejs.org/docs/index.html?q=MeshSurfaceSampler#examples/zh/math/MeshSurfaceSampler

我们来试一下:
npx create-vite point-cloud

进入项目,安装依赖:
pnpm install
pnpm install --save three
pnpm install --save-dev @types/three
先写下基础代码:
改下 src/main.js
import './style.css';
import * as THREE from 'three';
import {
OrbitControls
} from 'three/addons/controls/OrbitControls.js';
import mesh from './mesh.js';
const scene = new THREE.Scene();
scene.add(mesh);
const light = new THREE.DirectionalLight(0xffffff);
light.position.set(500, 300, 600);
scene.add(light);
const light2 = new THREE.AmbientLight();
scene.add(light2);
const axesHelper = new THREE.AxesHelper(1000);
scene.add(axesHelper);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 1, 10000);
camera.position.set(0, 500, 500);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer({
antialias: true
});
renderer.setSize(width, height)
function render() {
renderer.render(scene, camera);
requestAnimationFrame(render);
}
render();
document.body.append(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement);
创建 Scene、Light、Camera、Renderer
改下 style.css
body {
margin: 0;
}
然后创建 mesh.js
import * as THREE from 'three';
const geometry = new THREE.BoxGeometry(100, 100, 100);
const material = new THREE.MeshPhongMaterial({
color: 'orange'
})
const mesh = new THREE.Mesh(geometry, material);
export default mesh;
先画个立方体
跑一下:
npm run dev


如何用点云的方式展示这个立方体呢?
这就需要用到上面那个 API 了:

它是在一个物体表面采样拿到一些点,然后我们用 BufferGeometry 把这些点绘制出来就可以了。

我们用采样器在立方体表面取 1000 个点,然后用 BufferGeometry + Points 把它画出来:
const sampler = new MeshSurfaceSampler(mesh).build();
const vertices = [];
const position = new THREE.Vector3();
for (let i = 0; i < 1000; i++) {
sampler.sample(position);
vertices.push(position.x, position.y, position.z);
}
const bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(vertices, 3),
);
const pointMaterial = new THREE.PointsMaterial({
size: 0.5,
color: 'orange'
});
const pointCloud = new THREE.Points(bufferGeometry, pointMaterial);
export default pointCloud;
看下效果:

这明显不是立方体的顶点,而是从立方体表面采样取的点。
这就是点云的效果。
我们换个复杂点的模型试一下:
用Three.js 的源码仓库的马的模型:


这里要下载 .gltf 和 .bin 文件
创建 mesh2.js
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
const loader = new GLTFLoader();
const mesh = new THREE.Group();
loader.load("./Horse.gltf", function (gltf) {
console.log(gltf);
mesh.add(gltf.scene);
gltf.scene.scale.setScalar(50);
})
export default mesh;
引入下:


用点云的方式来展示:

首先,MeshSurfaceSampler 需要传入一个 Mesh
我们遍历模型,找一个 mesh 出来:

gltf.scene.traverse(obj => {
if(obj.isMesh) {
console.log(obj.name, obj);
}
})

这个其实我们之前测过,一个是马的身体,一个是马尾马鬃马蹄这些。
把每个 mesh 都做取样:

用点云画出来:
import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
import { MeshSurfaceSampler } from 'three/examples/jsm/Addons.js';
const loader = new GLTFLoader();
const mesh = new THREE.Group();
const vertices = [];
loader.load("./Horse.gltf", function (gltf) {
console.log(gltf);
// mesh.add(gltf.scene);
gltf.scene.scale.setScalar(50);
gltf.scene.traverse(obj => {
if(obj.isMesh) {
console.log(obj.name, obj);
const sampler = new MeshSurfaceSampler(obj).build();
const position = new THREE.Vector3();
for (let i = 0; i < 1000; i++) {
sampler.sample(position);
vertices.push(position.x, position.y, position.z);
}
}
})
const bufferGeometry = new THREE.BufferGeometry();
bufferGeometry.setAttribute(
'position',
new THREE.Float32BufferAttribute(vertices, 3),
);
const pointMaterial = new THREE.PointsMaterial({
size: 0.5,
color: 'orange'
});
const pointCloud = new THREE.Points(bufferGeometry, pointMaterial);
mesh.add(pointCloud);
})
export default mesh;
看下效果:

可以看到,点云绘制出来了,但是缩放没生效。
我们这样写:

position.multiplyScalar(50);
在取样之后对采样点的数据做放缩。

现在就是放大之后的点云效果了。
我们取样点更多一些:


点云效果就更明显了。
案例代码上传了小册仓库
总结
这节我们学了下点云。
Three.js 提供了 MeshSurfaceSampler 的 api 对物体表面进行采样。
采样的点用 BufferGeometry + Points 画出来就是点云效果。
当然,点云效果可以做一些更复杂的处理,下节我们继续深入。