threejs中模型自定义路线移动
生命不息,学习不止
基于r95Threejs版本
此例子中:包括背景设置:天空之盒。
模型的引用:小车和整体 glb模型引用
路线设置(因线line2无法设置宽度,所以选择了用管道,当然也可用点成面,看各自喜好):管道引用图片/颜色。
显示:标签
综合处理:比如缩放参数、指定模型闪烁、多个模型同时动态移动、移除场景等
具体效果如图
部分代码如下:如有需要完整代码,可私聊
<!DOCTYPE html>
<html>
<head>
<title>Threejs中寻路导航</title>
<style>
body {
margin: 0;
overflow: hidden;
}
.title {
display: flex;
align-items: center;
justify-content: center;
padding: 3px;
font-size: 18px;
color: rgb(0, 255, 255);
background: url(/static/scrap/assets/infoPg.png) no-repeat;
background-size: cover;
/* background-color: #ffff00; */
}
</style>
</head>
<body>
<div id="dom">
</div>
<script type="text/javascript">
var camera;//相机
var renderer;//渲染器
var carMovePath=[];//路径坐标
var carRoute=[];//路线纹理
var meshArr=[];//加载的小车模型
var shperePathIndex=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];//路线索引
var scene;//场景
var labelObj=[];//场景中的标签
var parkingdiv=[];//标签div
var labelRenderer;//标签渲染器
var material_yellow;//黄色材质--停车位闪烁用
var material_hyaline; //蓝色材质--停车位闪烁用
var cube12;
let circle_n = 0;
var model; //场景整体模型
var objectArr = []; //所有模型对象的集合,export导出用于射线拾取
var multiple_z=1.5;//z(宽)值扩大的倍数
var multiple_x=1;//x(长)值扩大的倍数
var multiple_y=2;//y(高)值扩大的倍数
function init() {
// 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
scene = new THREE.Scene();
var urls = [
'./assets/6/posx.jpg',
'./assets/6/negx.jpg',
'./assets/6/posy.jpg',
'./assets/6/negy.jpg',
'./assets/6/posz.jpg',
'./assets/6/negz.jpg'
];
var cubeLoader = new THREE.CubeTextureLoader();
scene.background = cubeLoader.load(urls);
// 创建一个摄像机,它定义了我们正在看的地方
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 1000);
// 将摄像机对准场景的中心
camera.position.x = -1;
camera.position.y = 40;
camera.position.z = 40;
camera.lookAt(scene.position);
var orbit = new THREE.OrbitControls(camera);
// 创建一个黄色的材质
material_yellow = new THREE.MeshLambertMaterial({color:0xFFFFFF});
material_hyaline = new THREE.MeshBasicMaterial({
color: 0x005577, //颜色材质
transparent: true,//开启透明
opacity: 0.5 //设置透明度
});
var loader = new THREE.GLTFLoader();
model = new THREE.Group(); //声明一个组对象,用来添加加载成功的三维场景
loader.load("/static/scrap/mode/plane.glb", function (gltf) { //gltf加载成功后返回一个对象
// gltf.scene.getObjectByName("electricalBuilding").material=material_hyaline;
model.add(gltf.scene);
// 设置平面位置并旋转
model.position.y =0.2;
model.position.x = 0;
model.position.z = 0;
//model.scale.set(1, 1, 1.5);
model.scale.z=multiple_z;
model.scale.x=multiple_x;
model.scale.y=multiple_y;
})
scene.add(model);//将model.js中的模型放入场景
function buildName(coordinate,buildName,title){
//设置标签
var build = document.createElement('div');
build.className = 'title';
build.textContent =title;
var buildMesh = new THREE.CSS2DObject(build);
buildMesh.name=buildName;
buildMesh.position.x =coordinate.x*multiple_x;
buildMesh.position.y =2*multiple_y;
buildMesh.position.z =coordinate.z*multiple_z;
//labelObj.name="labelObj"+parkingNum;
scene.add(buildMesh);
buildMesh.element.style.visibility = 'visible';//显示标签
}
// 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
renderer = new THREE.WebGLRenderer({
antialias: true, //开启反锯齿,消除混叠、抗图像折叠有损等
alpha: true,
});
renderer.setPixelRatio(window.devicePixelRatio); //设置设备像素比率,防止Canvas画布输出模糊。
renderer.setSize(window.innerWidth, window.innerHeight);
// 设置three.js背景颜色 和雾化颜色相配 0x005577
renderer.setClearColor(0xa0a0a0, 1);
//背景颜色透明化
renderer.setClearAlpha(0);
//解决加载gltf格式模型纹理贴图和原图不一样问题
renderer.outputEncoding = THREE.sRGBEncoding;
// scene.add(new THREE.AmbientLight(0x666666));
/**
* 光源设置
*/
// 平行光1
var directionalLight = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight.position.set(0, 20,-20);
scene.add(directionalLight);
// 平行光2
var directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.5);
directionalLight2.position.set(-400, -200, -300);
scene.add(directionalLight2);
//环境光
var ambientLight = new THREE.AmbientLight("#ffffff", 1);
scene.add(ambientLight);
// 在屏幕上显示坐标轴
// var axes = new THREE.AxisHelper(100);
// scene.add(axes);
//将平面添加到场景中
var plane = createPlaneGeometryBasicMaterial();
scene.add(plane);
// 创建一个CSS2渲染器CSS2DRenderer
labelRenderer = new THREE.CSS2DRenderer();
labelRenderer.setSize(window.innerWidth, window.innerHeight);
labelRenderer.domElement.style.position = 'absolute';
// 相对标签原来位置偏移
labelRenderer.domElement.style.top = '0';//信息弹窗界面高度一半
labelRenderer.domElement.style.left = '0';//信息弹窗界面宽度一半
// 设置.pointerEvents=none,以免模型标签HTML元素遮挡鼠标选择场景模型
labelRenderer.domElement.style.pointerEvents = 'none';
document.body.appendChild(labelRenderer.domElement);
// 创建小车模型
function createModels(parkingNum,carName) {
// let axes = new THREE.AxesHelper(6000);
// this.scene.add(axes);
//使用指定的点创建一条平滑的三维样条曲线当做小车运动路径
var point_scale_y=1;
if(multiple_y>1){
point_scale_y=multiple_y/10+1;
}
carMovePath[parkingNum] = new THREE.CatmullRomCurve3(pointMap[parkingNum-1]);
for (var i = 0; i < carMovePath[parkingNum].points.length; i++) {
var point = carMovePath[parkingNum].points[i];
point.x *= multiple_x;
point.y *= point_scale_y;
point.z *= multiple_z;
carMovePath[parkingNum][i] = point;
}
carMovePath[parkingNum].curveType = 'catmullrom';//定义catmullrom的张力
carMovePath[parkingNum].closed = false; //设置是否闭环
carMovePath[parkingNum].tension=0;//设置线的张力,0为无弧度折线
//参考路径上取1000个点,可以将模型安置在某个点位上
const pathPoints = carMovePath[parkingNum].getPoints(1000);
// 引入三维模型(glb或者gltf格式)
const loaders = new THREE.GLTFLoader();
meshArr[parkingNum]=null;
loaders.load(`/static/scrap/mode/car.glb`, (glb) => {
// meshArr[0] = glb.scene.children[0].children[0];
meshArr[parkingNum] = glb.scene.children[0];
//这里就是将模型安置在i*333这个点位上
meshArr[parkingNum].position.set(
pathPoints[0].x,
pathPoints[0].y,
pathPoints[0].z
);
//meshArr[0].rotation.y= 40 * (Math.PI / 180);
//设置模型大小
//meshArr[0].scale.set(0.001, 0.001, 0.001);
meshArr[parkingNum].scale.set(0.6, 0.6, 0.6);
scene.add(meshArr[parkingNum]);
});
//设置路线指引
var tubeGeometry = new THREE.TubeGeometry(carMovePath[parkingNum], 200, 0.1, 300, false);
var textureLoader = new THREE.TextureLoader();
var texture = textureLoader.load('/static/scrap/assets/2.png');
// 设置阵列模式 RepeatWrapping
// texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
// 设置x方向的重复数(沿着管道路径方向)
// 设置y方向的重复数(环绕管道方向)
texture.repeat.x = 10;
texture.repeat.y = 4;
// 设置管道纹理偏移数,便于对中
texture.offset.y = 0.5;
var tubeMaterial = new THREE.MeshPhongMaterial({
//map: texture,
color: 0xff0000,
transparent: true
});
carRoute[parkingNum] = new THREE.Mesh(tubeGeometry, tubeMaterial);
carRoute[parkingNum].name="carRoute"+parkingNum;
carRoute[parkingNum].position.y = -2;
//mesh.rotateZ(3.14);
carRoute[parkingNum].scale.set(1, 1, 1);
// 使用加减法可以设置不同的运动方向
setInterval(() => {
if(texture.offset.x<=-10){
texture.offset.x =0
}else{
texture.offset.x -= 0.0036
}
})
scene.add(carRoute[parkingNum])
//设置标签
parkingdiv[parkingNum] = document.createElement('div');
parkingdiv[parkingNum] .className = 'title';
parkingdiv[parkingNum] .textContent =carName;
labelObj[parkingNum] = new THREE.CSS2DObject(parkingdiv[parkingNum]);
labelObj[parkingNum].position.x =pathPoints[0].x;
labelObj[parkingNum].position.y =pathPoints[0].y;
labelObj[parkingNum].position.z =pathPoints[0].z;
//labelObj.name="labelObj"+parkingNum;
scene.add(labelObj[parkingNum]);
labelObj[parkingNum].element.style.visibility = 'visible';//显示标签
//设置对应的停车位闪烁
if (scene.getObjectByName("parking"+parkingNum) !=null) {
scene.getObjectByName("parking"+parkingNum).material = shaderMaterial;
}
}
/**
* 呼吸灯 添加渲染通道
*/
var shaderMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 1.0 },
color1: { value: new THREE.Color(0xFFFFFF) },
color2: { value: new THREE.Color(0x005577) }
},
vertexShader: [
'varying vec3 vNormal;',
'void main() {',
'vNormal = normalize(normalMatrix * normal);',
'gl_Position = projectionMatrix * modelViewMatrix * vec4(position,1.0);',
'}'
].join('\n'),
fragmentShader: [
'uniform float time;',
'uniform vec3 color1;',
'uniform vec3 color2;',
'varying vec3 vNormal;',
'void main() {',
'float intensity = sin(time + vNormal.z);',
'gl_FragColor = vec4(mix(color1, color2, intensity), 1.0);',
'}'
].join('\n')
});
// 将呈现器的输出添加到HTML元素
document.getElementById("dom").appendChild(renderer.domElement);
// 创建一个地面
function createPlaneGeometryBasicMaterial() {
var textureLoader = new THREE.TextureLoader();
var cubeMaterial = new THREE.MeshStandardMaterial({
map: textureLoader.load("/static/scrap/assets/cd.jpg"),
});
cubeMaterial.map.wrapS = THREE.RepeatWrapping;
cubeMaterial.map.wrapT = THREE.RepeatWrapping;
cubeMaterial.map.repeat.set(8, 8)
// 创建地平面并设置大小
var planeGeometry = new THREE.PlaneGeometry(200, 200);
var plane = new THREE.Mesh(planeGeometry, cubeMaterial);
// 设置平面位置并旋转
plane.rotation.x = -0.5 * Math.PI;
plane.position.x = 0;
plane.position.z = 0;
return plane;
}
//小车路径索引更新
// 启动动画
renderScene();
//渲染
function renderScene() {
orbit.update();
labelRenderer.render(scene, camera);
shaderMaterial.uniforms.time.value += 0.07; // 你可以根据需要调整这个值
// 使用requestAnimationFrame函数进行渲染
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
}
// 渲染的场景
renderer.render(scene, camera);
}
window.onload = init;
// 随着窗体的变化修改场景
function onResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
}
// 监听窗体调整大小事件
window.addEventListener('resize', onResize, { passive: false });
</script>
</body>
</html>
其中,还有一些不足,但作为一个例子,其中包含的还是比较全的。欢迎互相交流