有了顶点数据,可以构成各种几何体 geometry。
加上材质 material,就可以渲染成点模型 Points、线模型 Line、网格模型 Mesh。
其中最常用的是网格模型 Mesh。
除了这些之外,还有一种特殊的模型,精灵模型 Sprite。
它特殊在两个点:
- 没有 geometry,默认都是 1 * 1 的矩形平面
- 永远面向摄像头
提到矩形平面,大家会想到 PlaneGeometry,但 Sprite 和它不同,矩形平面几何体可以调整相机位置看到背面、侧面,而 Sprite 永远面向屏幕。
我们试一下就知道了:
npx create-vite sprite

进入项目,安装依赖:
npm install
npm install --save three
npm 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 axesHelper = new THREE.AxesHelper(50);
scene.add(axesHelper);
const width = window.innerWidth;
const height = window.innerHeight;
const camera = new THREE.PerspectiveCamera(60, width / height, 0.1, 1000);
camera.position.set(0, 0, 10);
camera.lookAt(0, 0, 0);
const renderer = new THREE.WebGLRenderer();
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、Camera、Renderer。
因为 Sprite 是 1 * 1 的矩形平面,所以这里相机放在 0, 0, 5 的位置。
改下 style.css
body {
margin: 0;
}
然后创建 src/mesh.js
import * as THREE from 'three';
const group = new THREE.Group();
const spriteMaterial = new THREE.SpriteMaterial({
color: 'orange'
});
const sprite = new THREE.Sprite(spriteMaterial);
group.add(sprite);
export default group;
创建 Sprite,它只有材质,没有几何体(因为它固定是矩形平面)。
看看效果:
npm run dev


可以看到,确实是一个矩形平面。
那它和 PlaneGeometry 的区别在哪呢?
我们加个 PlaneGeometry 对比下:

const geometry = new THREE.PlaneGeometry(1, 1);
const mesh = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({
color: 'lightblue'
}));
mesh.position.y = 3;
group.add(mesh);

可以看到,PlaneGeometry 是可以随着 camera 位置的变化,看到侧面、反面的,而 Sprite 永远正对相机。
那这个特性有什么用呢?
我们一般用 Sprite 来做一些标注。
类似这种:

给 Sprite 放大一下,贴上颜色贴图,悬浮在要标注的物体上方。
我们来试一下:
创建 src/mesh2.js
import * as THREE from 'three';
function createMesh(color, x) {
const geometry = new THREE.DodecahedronGeometry(1);
const material = new THREE.MeshBasicMaterial({
color: color
});
const mesh = new THREE.Mesh(geometry, material);
mesh.position.x = x;
return mesh;
}
const mesh = createMesh('orange', 0);
const mesh2 = createMesh('skyblue', 5);
const mesh3 = createMesh('lightgreen', -5);
const group = new THREE.Group();
group.add(mesh);
group.add(mesh2);
group.add(mesh3);
export default group;
这里用上节的那三个十二面体,改成 MeshBasicMaterial,调一下大小。


然后在上面加三个 Sprite。
先找个贴图:

放到 public 目录下,然后创建 Sprite:

const loader = new THREE.TextureLoader();
function createSprite(x, y) {
const texture = loader.load('./sprite.png');
const spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
const sprite = new THREE.Sprite(spriteMaterial);
sprite.position.x = x;
sprite.position.y = y;
return sprite;
}
const sprite1 = createSprite(0, 1.5);
const sprite2 = createSprite(5, 1.5);
const sprite3 = createSprite(-5, 1.5);
group.add(sprite1);
group.add(sprite2);
group.add(sprite3);
分别在 3 个 mesh 上添加 3 个 Sprite。

可以看到,不管怎么旋转,Sprite 的贴图都始终正对相机。
这样,把 Sprite 加到目标物体上,就可以实现这种标注效果:

此外,Sprite 还可以用来做下雨下雪效果:
首先,找个雪花的透明背景图片放在 public 目录下:

创建 src/mesh3.js
import * as THREE from 'three';
const loader = new THREE.TextureLoader();
const texture = loader.load("./snow.png");
const spriteMaterial = new THREE.SpriteMaterial({
map: texture
});
const group = new THREE.Group();
for (let i = 0; i < 10000; i ++) {
const sprite = new THREE.Sprite(spriteMaterial);
const x = 1000 * Math.random();
const y = 1000 * Math.random();
const z = 1000 * Math.random();
sprite.position.set(x, y, z);
group.add(sprite);
}
function render() {
group.children.forEach(sprite => {
sprite.position.y -= 0.1;
if (sprite.position.y < 0) {
sprite.position.y = 1000;
}
});
requestAnimationFrame(render);
}
render();
export default group;
创建一万个 Sprite,放在在 0 到 1000 的随机位置。
然后在渲染循环里每一帧改变一下 y 的位置,就可以实现下落的效果。
在 main.js 里引入:

注释掉 AxesHelper,改一下相机位置。
camera.position.set(200, 200, 200);
看下效果:

可以看到,现在就有下雪的效果了。
偶尔会有一些特别大的雪花,这个改一下相机的近裁截面,把它设置大一点就好了。

这个 y 每次下降的高度还有另一种写法:

用 Clock 的 getDelta 可以拿到每次渲染的时间间隔,可以用它来做 y 的下降高度。

案例代码上传了小册仓库。
总结
这节我们学了 Sprite,它没有几何体,是一个永远面向相机的 1 * 1 的矩形平面。
我们可以用它来做一些场景中物体的标注,也可以用它来实现下雨、下雪等效果。
不同于点模型、线模型、网格模型,Sprite 精灵模型是比较特殊的,在特定场景下会用到。