精灵 Sprite 是 ThreeJS 一个特殊的存在,它的存在理由之一是极低的资源消耗。精灵能够节省资源开销源于其特性:一个形状为矩形、默认颜色为白色的不需要实例化几何体的 2d 图像,总是面向相机,没有阴影,不会旋转。精灵因其此类特性非常适合用于显示图标、指示器、粒子系统中的单个粒子、UI 元素等等。
在 ThreeJS 中绘制一个精灵图像非常简单:
// 创建一个默认精灵
const sprite = new THREE.Sprite(new THREE.SpriteMaterial());
// 精灵加入到场景
scene.add(sprite);
精灵由于总是面部朝着相机,因而使用 rotate 或 rotation 旋转 Sprite 精灵是徒劳的,甚至在场景中加入了相机轨道控制器之后自动和手动旋转、翻滚场景,精灵都不为所动。下面的代码分别绘制了一个立方体(BoxGeometry)、一个平面几何体(PlaneGeometry)、一个精灵(Sprite),场景中加入了相机轨道控制器、可以自动、手动翻转场景中的景象:
<!-- 标准化模块系统 :创建可靠的模块导入结构 -->
<script type="importmap">
{
"imports": {
"three": "https://unpkg.ihwx.cn/three@0.176.0/build/three.module.js",
"three/examples/jsm/": "https://unpkg.ihwx.cn/three@0.176.0/examples/jsm/"
}
}
</script>
<script type="module">
import * as THREE from 'three'; // 导入ThreeJS核心库
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);
camera.position.set(0, 0, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
const controls = new OrbitControls(camera, renderer.domElement); // 轨道控制器
controls.autoRotate = true; // 启用轨道自转
// 立方体
const cube = new THREE.Mesh(
new THREE.BoxGeometry(),
new THREE.MeshNormalMaterial()
);
cube.position.set(-2, 0, 0); // 定位
cube.rotateX(Math.PI / 4); // X轴方向侧身
// 几何体平面
const plane = new THREE.Mesh(
new THREE.PlaneGeometry(),
new THREE.MeshNormalMaterial({ side: THREE.DoubleSide }) // 双面渲染
);
plane.position.set(1.5, 0, 0); // 定位
plane.rotateX(Math.PI / 4); // X轴方向侧身
// 钢蓝色精灵(可定位但旋转设置无效)
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0x4682b4 }));
scene.add(cube, plane, sprite); // 图像都加入到场景中
const animate = () => {
requestAnimationFrame(animate);
controls.update(); // 更新轨道控制器令其自转
renderer.render(scene, camera);
};
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
animate();
</script>
查看上面例子的运行效果会发现,平面几何体、立方体绕着精灵旋转,精灵纹丝不动,这证实了如前已述精灵的特性,总是面朝相机。顺便提提:平面几何体是升级版的精灵,它有 Geometry 几何体,是名副其实的几何体家族元素(只不过人家是2d的),不像 Sprite 精灵那样是个没有骨架的东东。
精灵天生一副四方脸,不一定符合大众的审美情趣。好在有上一讲讲到的 texture 纹理,我们可以轻轻松松给精灵贴个图,让它长出一张万人迷的漂亮脸蛋应该不是个事,例如下面示例的太阳花脸儿:贴图来源是一张 SVG 图片,贴上这张图片的精灵从Z轴的 -20 个距离单位出发,向观者扑面而来,如此往复。代码如下:
<div style="margin: 10px; position: absolute;">点击页面暂停/继续动画</div>
<script type="module">
import * as THREE from 'https://638183.freep.cn/638183/web/ku/three.module.min.js';
const scene = new THREE.Scene;
const clock = new THREE.Clock();
const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 设置一些动画需要的变量 :zpos - 出发点z坐标值,currentpos - 当前点z坐标值,step 歩幅系数
let zpos = -20, currentpos = -20, step = 5;
// 加载纹理
const texture = new THREE.TextureLoader().load('https://638183.freep.cn/638183/web/svg/sunfl-2.svg', () => {
renderer.render(scene, camera);
animate();
});
// 创建精灵 :颜色 + 纹理
const sprite = new THREE.Sprite(new THREE.SpriteMaterial({ color: 0xff4500, map: texture }));
sprite.position.z = zpos; // 精灵在Z轴上的初始位置
scene.add(sprite); // 精灵加入到场景
// 动画函数 :精灵在Z轴上的位置变化 + 精灵材质旋转
const animate = () => {
requestAnimationFrame(animate); // 请求关键帧动画API循环调用函数
const delta = clock.getDelta(); // 从动画时钟获取上下帧时间间隔
currentpos += delta * step; // Z轴当前位置变化
if (currentpos > 5) currentpos = zpos; // Z轴当前位置大于 5 个距离单位时复位到出发点
sprite.position.z = currentpos; // 精灵在Z轴上的位置
sprite.material.rotation += delta * 2; // 精灵的材质旋转(精灵不会旋转但材质可以)
renderer.render(scene, camera); // 渲染效果
};
window.onresize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
document.onclick = () => clock.running ? clock.stop() : clock.start();
</script>
texture 纹理彻底改变了精灵的容貌,纹理使用的图片因此至关重要,一般建议使用背景透明的图片,如 png、gif、webp 或 svg,这些格式的图片都允许制作出背景透明、只呈现图像外观的最终效果。除了改变精灵的外观,我们还让不能旋转的精灵在运行的时候旋转了,但必须明白,精灵自己并没有旋转,大家看到的旋转效果实际上是构建成精灵外观的材质 material 即精灵的漂亮皮囊在旋转;并且,如果愿意,当加入了相机轨道控制器,还可以通过类似的其它途径驱动精灵也能和其它几何体一样响应轨道的变化而产生相应的变化,不过这个知识点已经超出了入门的范畴,而且,也不符合精灵的存在意义。
附:1. ThreeJS中文网·精灵(Sprite) 2. ThreeJS中文网·精灵模型Sprite