写点什么

Three.js 杂记(十一)—— 精灵与粒子(绘制中国地图)

用户头像
空城机
关注
发布于: 2021 年 03 月 22 日
Three.js杂记(十一)—— 精灵与粒子(绘制中国地图)

精灵模型 Sprite 简介

精灵模型对象Sprite。精灵模型对象和网格模型一样需要设置材质,不过精灵模型不需要程序员设置几何体,Threejs 系统渲染的时候会自动设置。


通过 Threejs 精灵模型可以给场景中模型对象设置标签,也可以生成大量精灵模型对象模拟一个粒子系统


Three.js 的精灵模型对象 Sprite 和 Threejs 的网格模型 Mesh 一样都是模型对象,基类都是 Object3D,关于精灵模型对象 Sprite 的方法和属性除了可以查看文档 Sprite,也可以查看基类 Object3D。


创建精灵模型对象 Sprite 和创建网格模型对象一样需要创建一个材质对象,不同的地方在于创建精灵模型对象不需要创建几何体对象 Geometry,精灵模型对象本质上你可以理解为已经内部封装了一个平面矩形几何体 PlaneGeometry,*矩形精灵模型与矩形网格模型的区别在于精灵模型的矩形平面会始终平行于 Canvas 画布*。


如果你想理解精灵模型的本质可以阅读官方文件 three.js-master 精灵模型对象的封装源码\src\objects\Sprite.js、解析渲染精灵模型的源码\src\renderers\webgl\WebGLSpriteRenderer.js


Sprite 和 SpriteMaterial

通过 Sprite 创建精灵模型不需要几何体,只需要给构造函数 Sprite 的参数设置为一个精灵材质SpriteMaterial即可。


精灵材质对象 SpriteMaterial 和普通的网格材质一样可以设置颜色.color、颜色贴图.map、开启透明.transparent、透明度.opacity 等属性,精灵材质对象 SpriteMaterial 的基类是材质Material


Sprite 用途

说到精灵模型对象,这种情况下你肯定关心它的用途,关于用途,你可以在三维场景中把精灵模型作为一个模型的标签,标签上可以显示一个写模型的信息,你可以通过足够多的精灵模型对象,构建一个粒子系统,来模拟一个下雨、森林、或下雪的场景效果。


练习:中国城市粒子地图

先准备一份载有中国各个城市位置的 Json 数据文件,如下所示即可,具体数据在文本最下方

可以准备一张纹理图片



显示效果:


如果不使用纹理贴图效果:



难点:编写粒子地图,在鼠标的移动过程中需要获取对应的城市粒子模型,在这里使用了光线投射Raycaster。光线投射用于进行鼠标拾取(在三维空间中计算出鼠标移过了什么物体)。


最终代码如下:


<!DOCTYPE html><html>	<head>		<meta charset="utf-8">		<title>粒子地图</title>		<!-- 来自three.js文件包 -->		<script src="../js/three.js" type="text/javascript" charset="utf-8"></script>		<script src="../js/OrbitControls.js" type="text/javascript" charset="utf-8"></script>		<style type="text/css">			*{				margin: 0;				padding: 0;			}			#cityInfo {				background-color: #F3E9B4;				position: absolute;				z-index: 10;				top: 100px;				left: 375px;			}		</style>	</head>	<body>		<div id="app"></div>		<div id="cityInfo">全国</div>				<script type="text/javascript">						var scene = new THREE.Scene();			camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000);			render = new THREE.WebGLRenderer({				antialias: true			});			render.setPixelRatio(window.devicePixelRatio);			render.setSize(window.innerWidth, window.innerHeight)
var app = document.getElementById("app"); app.appendChild(render.domElement); /********************************************************/ var group = new THREE.Group(); // 创建组对象,包含所有精灵对象 var loader = new THREE.FileLoader().setResponseType('json'); // 文件加载对象 let texture = new THREE.TextureLoader().load("../img/sprite/sprite.png"); // 粒子贴图 // 进行光线投射来获取鼠标坐标点 var raycaster = new THREE.Raycaster(); var mouse = new THREE.Vector2(); // 加载json文件资料 loader.load('../img/sprite/chinaCity.json', function(data) { //遍历数据 for (let elem in data) { if (elem.indexOf('北京') > -1) { var color = '#FF0000' } else { var color = '#1A41E5' } // 精灵材质 let spriteMaterial = new THREE.SpriteMaterial({ color: color, transparent: true, opacity: 0.6, map: texture,//设置精灵纹理贴图 }) let sprite = new THREE.Sprite(spriteMaterial); // 创建精灵模型对象 sprite.title = elem; // 给精灵模型添加title属性 group.add(sprite); sprite.scale.set(0.5, 0.5, 1); //获得城市坐标设置精灵模型对象的位置 sprite.position.set(data[elem][0], data[elem][1], 0) } scene.add(group);//把精灵群组插入场景中 // 中国城市坐标整体的几何中心不在坐标原点,需要适当的平移 group.position.set(-110, -30, 0); let hainanSpriteMaterial = new THREE.SpriteMaterial({ map: new THREE.TextureLoader().load("../img/sprite/hainan9.jpg"),//设置精灵纹理贴图 }) let hainanSprite = new THREE.Sprite(hainanSpriteMaterial); hainanSprite.position.set(120, 16, 0); hainanSprite.scale.set(8, 8); hainanSprite.title = '海南九段图' group.add(hainanSprite); }) function onMouseMove( event ) { // 将鼠标位置归一化为设备坐标。x 和 y 方向的取值范围是 (-1 to +1) mouse.x = ( event.clientX / window.innerWidth ) * 2 - 1; mouse.y = - ( event.clientY / window.innerHeight ) * 2 + 1; } window.addEventListener( 'mousemove', onMouseMove, false ); scene.background = new THREE.Color('#fff'); //设置场景颜色 camera.position.set(20, 20, 60); //设置相机位置 camera.lookAt(new THREE.Vector3(0, 0, 0)) // 鼠标控件 var controls = new THREE.OrbitControls(camera, render.domElement); /********************************************************/ let currentSprite = null, currentSpriteTitle = ''; function animate(){ // 通过摄像机和鼠标位置更新射线 raycaster.setFromCamera( mouse, camera ); // 计算物体和射线的焦点 true代表包括后代模型 let intersects = raycaster.intersectObjects( scene.children, true ); if (intersects.length > 0) { //判断鼠标移动到的模型数量 if (currentSprite != intersects && currentSprite) { for (let i = 0; i < currentSprite.length; i++ ) { if (currentSprite[i].object.title.indexOf('北京') > -1) { currentSprite[i].object.material.color.set( '#FF0000' ); } else { currentSprite[i].object.material.color.set( '#1A41E5' ); } if (currentSprite[i].object.title == '海南九段图') currentSprite[i].object.material.color.set('#FFF') } } for (let i = 0; i < intersects.length; i++ ) { if (intersects[i].object.title == '海南九段图') intersects[i].object.material.color.set('#FFF') else intersects[i].object.material.color.set( '#F7AA07' ); } currentSprite = intersects; if (!currentSpriteTitle || currentSpriteTitle != currentSprite[0].object.title) { // 判断是否在城市上,和城市名是否改变 currentSpriteTitle = currentSprite[0].object.title; showCity(currentSpriteTitle, true) } } render.render(scene, camera); window.requestAnimationFrame(animate); } function showCity(name, flag) { //显示当前鼠标移动到的城市名称 if (flag) { document.getElementById('cityInfo').style.visibility = 'visible'; document.getElementById('cityInfo').innerText = name; } else document.getElementById('cityInfo').style.visibility = 'hidden'; } animate(); </script> </body></html>
复制代码


地图数据

上传至 CSDN:中国城市坐标位置


发布于: 2021 年 03 月 22 日阅读数: 11
用户头像

空城机

关注

曾经沧海难为水,只是当时已惘然 2021.03.22 加入

业余作者,在线水文

评论

发布
暂无评论
Three.js杂记(十一)—— 精灵与粒子(绘制中国地图)