Skip to content

270. 表面采样器更常见的用法

Published:

前面用表面采样器 Mesh 实现了点云效果:

2025-10-11 19.20.11.gif

但并不是说这个是点云,一般点云是由大量的点的数据渲染出来的。

我们这个只是在物体表面通过采样点来实现的这种效果。

表面采样器可以用来做更多的效果

image.png

比如山坡上长满鲜花,这个位置是不是随机的?

那就可以用 MeshSurfaceSampler 在山坡表面采样,然后在采样位置种上鲜花。

比如这个官方的例子:

https://threejs.org/examples/#webgl_instancing_scatter

2025-10-19 20.50.11.gif

你想想如果自己实现在这个物体表面的随机位置找一些点,是不是很麻烦?

这种场景,用表面采样器就很适合。

我们来写一下:

npx create-vite point-cloud2

image.png

进入项目,安装依赖:

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.DodecahedronGeometry(100);
const material = new THREE.MeshPhongMaterial({
    color: 'orange'
})
const mesh = new THREE.Mesh(geometry, material);

export default mesh;

画个十二面缓冲几何体

跑一下:

npm run dev

image.png

2025-10-19 21.06.08.gif

然后用 MeshSurfaceSampler 在表面采样 1000 个点:

image.png

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: 1,
    color: 'blue'
});
const pointCloud = new THREE.Points(bufferGeometry, pointMaterial);
mesh.add(pointCloud);

用点模型画出来。

2025-10-19 21.53.55.gif

这里当然也可以不用点模型,而是直接在那些采样点的位置创建一些物体:

image.png


const cubeGeometry = new THREE.BoxGeometry(2, 2, 30);
const cubeMaterial = new THREE.MeshPhongMaterial({
    color: 'blue'
});

const cubesGroup = new THREE.Group();

for (let i = 0; i < vertices.length; i += 3) {
    const cube = new THREE.Mesh(cubeGeometry, cubeMaterial);
    const position = new THREE.Vector3(vertices[i], vertices[i + 1], vertices[i + 2]);
    cube.position.copy(position);
    
    cubesGroup.add(cube);
}

mesh.add(cubesGroup);

这里我们在每个采样点的位置创建一个立方体。

看下效果:

2025-10-19 22.01.13.gif

立方体是都绘制出来了,但是方向不是朝外的。

这里每个点的方向其实就是 0,0,0 到那个位置的向量的方向,也就是 verticles 数组里的每个向量。

image.png

const normal = position.clone().normalize();
cube.lookAt(normal);

这里我们把向量 normalize 归一化成单位向量,就是方向。

然后 cube.lookAt 这个方向向量,就是把立方体方向调成这个朝向。

2025-10-19 22.04.02.gif

这样我们就在采样点的位置画了一个个立方体。

案例代码上传了小册仓库

总结

前面我们用表面采样器实现过点云效果,但它其实更主要的作用是在采样点绘制一些物体,比如漫山遍野的鲜花。

这节我们在采样点画了一些立方体,要注意的是方向向量就是每个采样点向量的 normalize 的结果,让物体 lookAt 那个向量就可以了。

后面遇到需要在物体表面采样的需求,都可以用 MeshSurfaceSampler 来做。

评论