three 基础库
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

622 lines
21 KiB

<!DOCTYPE html>
<html>
<head>
<title>Threejs实现数字化工厂安全生产监控</title>
<meta charset="UTF-8">
<script type="text/javascript" src="lib/statistics.js"></script>
<!-- <script type="text/javascript" src="lib/steak.js"></script> -->
<script type="text/javascript" src="lib/three.js"></script>
<script type="text/javascript" src="lib/OrbitControls.js"></script>
<script type="text/javascript" src="lib/GLTFLoader.js"></script>
<script type="text/javascript" src="lib/ThreeBSP.js"></script>
<script type="text/javascript" src="lib/dat.gui.js"></script>
<script type="text/javascript" charset="UTF-8" src="lib/Tween.min.js"></script>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="dom"></div>
<script type="text/javascript">
var camera;
var renderer;
var peopleMesh;
var peopleMixer;
var mixer;
var matArrayA = []; //内墙
var matArr = []; //外墙
var texture;
var textureSecure;
var curve;
function init() {
var urls = [
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/posx.jpg',
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/negx.jpg',
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/posy.jpg',
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/negy.jpg',
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/posz.jpg',
'http://zuoben.top/#3-8/assets/bgImage/skyBox6/negz.jpg'
];
// 创建一个场景,它将包含我们所有的元素,如物体,相机和灯光。
var scene = new THREE.Scene();
var cubeLoader = new THREE.CubeTextureLoader();
// scene.background = cubeLoader.load(urls);
scene.opacity = 0;
// 创建一个摄像机,它定义了我们正在看的地方
camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100000);
// 将摄像机对准场景的中心
camera.position.z = 2000;
camera.position.y = 800;
camera.lookAt(scene.position);
var orbit = new THREE.OrbitControls(camera);
// 创建一个渲染器并设置大小,WebGLRenderer将会使用电脑显卡来渲染场景
renderer = new THREE.WebGLRenderer({
antialias: true,
logarithmicDepthBuffer: true,
});
renderer.setClearColor(new THREE.Color("#0e0934"));
renderer.setSize(window.innerWidth, window.innerHeight);
var ambientLight = new THREE.AmbientLight("#ffffff", 1);
scene.add(ambientLight);
// 将呈现器的输出添加到HTML元素
document.getElementById("dom").appendChild(renderer.domElement);
// 启动动画
renderScene();
initContent();
initSloganModel();
for (var i = 0; i < 8; i++) {
let px = 1060 - i * 300;
addArea(px, -400, 200, 500, scene, "库区1-" + (i + 1) + "号", "FF0000", 20, "居中");
}
for (var i = 0; i < 8; i++) {
let px = 1060 - i * 300;
if (i != 1 && i != 2 && i != 5 && i != 6) {
addArea(px, 400, 200, 500, scene, "库区2-" + (i + 1) + "号", "FF0000", 20, "居中");
}
}
initSecureModel();
// 初始化模型
function initContent() {
createFloor();
createWallMaterail();
var mat = new THREE.MeshPhongMaterial({
color: 0xafc0ca
});
createCubeWall(10, 200, 1400, 0, matArr, -1295, 100, 0, "墙面");
createCubeWall(10, 200, 1400, 1, matArr, 1295, 100, 0, "墙面");
createCubeWall(10, 200, 2600, 1.5, matArr, 0, 100, -700, "墙面");
//创建挖了门的墙
var wall = returnWallObject(2600, 200, 10, 0, mat, 0, 100, 700, "墙面");
var door_cube1 = returnWallObject(200, 180, 10, 0, mat, -600, 90, 700, "前门1");
var door_cube2 = returnWallObject(200, 180, 10, 0, mat, 600, 90, 700, "前门2");
var window_cube1 = returnWallObject(100, 100, 10, 0, mat, -900, 90, 700, "窗户1");
var window_cube2 = returnWallObject(100, 100, 10, 0, mat, 900, 90, 700, "窗户2");
var window_cube3 = returnWallObject(100, 100, 10, 0, mat, -200, 90, 700, "窗户3");
var window_cube4 = returnWallObject(100, 100, 10, 0, mat, 200, 90, 700, "窗户4");
var objects_cube = [];
objects_cube.push(door_cube1);
objects_cube.push(door_cube2);
objects_cube.push(window_cube1);
objects_cube.push(window_cube2);
objects_cube.push(window_cube3);
objects_cube.push(window_cube4);
createResultBsp(wall, objects_cube);
// 为墙面安装门
createDoor(100, 180, 2, 0, -700, 90, 700, "左门1");
createDoor(100, 180, 2, 0, -500, 90, 700, "右门1");
createDoor(100, 180, 2, 0, 500, 90, 700, "左门2");
createDoor(100, 180, 2, 0, 700, 90, 700, "右门2");
// 为墙面安装窗户
createWindow(100, 100, 2, 0, -900, 90, 700, "窗户");
createWindow(100, 100, 2, 0, 900, 90, 700, "窗户");
createWindow(100, 100, 2, 0, -200, 90, 700, "窗户");
createWindow(100, 100, 2, 0, 200, 90, 700, "窗户");
}
setTimeout(function () {
doorOpenColse(true);
}, 2000);
setTimeout(function () {
initPeople();
}, 2500);
// 添加人物模型
function initPeople() {
var loader = new THREE.GLTFLoader();
loader.load('http://zuoben.top/#3-8/assets/models/man/man.gltf', function (result) {
console.log(result)
result.scene.scale.set(50, 50, 50);
// result.scene.rotation.y = Math.PI / 2;
peopleMesh = result.scene;
scene.add(peopleMesh);
peopleMixer = new THREE.AnimationMixer(peopleMesh);
let AnimationAction = peopleMixer.clipAction(result.animations[0]);
AnimationAction.play();
motion()
});
}
function motion() {
// 通过类CatmullRomCurve3创建一个3D样条曲线
curve = new THREE.CatmullRomCurve3([
new THREE.Vector3(602, 3, 790),
new THREE.Vector3(597, 3, -3),
new THREE.Vector3(-605, 3, -3),
new THREE.Vector3(-602, 3, 790)
], false, "catmullrom", 0.01);
// 样条曲线均匀分割100分,返回51个顶点坐标
var points = curve.getPoints(100);
var geometry = new THREE.Geometry();
// 把从曲线轨迹上获得的顶点坐标赋值给几何体
geometry.vertices = points
var material = new THREE.LineBasicMaterial({
color: 0x4488ff
});
var line = new THREE.Line(geometry, material);
// scene.add(line)
// 通过Threejs的帧动画相关API播放网格模型沿着曲线做动画运动
// 声明一个数组用于存储时间序列
let arr = []
for (let i = 0; i < 101; i++) {
arr.push(i)
}
// 生成一个时间序列
var times = new Float32Array(arr);
var posArr = []
points.forEach(elem => {
posArr.push(elem.x, elem.y, elem.z)
});
// 创建一个和时间序列相对应的位置坐标系列
var values = new Float32Array(posArr);
// 创建一个帧动画的关键帧数据,曲线上的位置序列对应一个时间序列
var posTrack = new THREE.KeyframeTrack('.position', times, values);
let duration = 101;
let clip = new THREE.AnimationClip("default", duration, [posTrack]);
mixer = new THREE.AnimationMixer(peopleMesh);
let AnimationAction = mixer.clipAction(clip);
AnimationAction.play();
}
function changeLookAt(t) {
// 当前点在线条上的位置
var position = curve.getPointAt(t);
peopleMesh.position.copy(position);
if (position.z < 0) {
peopleMesh.rotation.y = Math.PI;
// camera.position.set(position.x + 200, 70, position.z);
}
if (position.z > 0 && position.x > 0) {
peopleMesh.rotation.y = Math.PI / 2;
// camera.position.set(position.x , 70, position.z+ 200);
}
if (position.z > 0 && position.x < 0) {
peopleMesh.rotation.y = -Math.PI / 2;
// camera.position.set(position.x , 70, position.z - 200);
}
// console.log(position)
// camera.position.set(position.x / 2, 69, position.z / 2);
camera.lookAt(position);
// camera.lookAt(position);659.0248303057256, y: 69.31162905236907, z: 921
// 返回一个点t在曲线上位置向量的法线向量
const tangent = curve.getTangentAt(t);
// 位置向量和切线向量相加即为所需朝向的点向量
const lookAtVec = tangent.add(position);
// peopleMesh.lookAt(lookAtVec);
}
//Secure 安全通道
function initSecureModel() {
var planGeometry = new THREE.PlaneGeometry(680, 40);
textureSecure = new THREE.TextureLoader().load('assets/textures/roll.png');
textureSecure.wrapS = THREE.RepeatWrapping
var tubeMaterial = new THREE.MeshBasicMaterial({
map: textureSecure,
transparent: true,
side: THREE.DoubleSide,
});
// 设置数组材质对象作为网格模型材质参数
var obj = new THREE.Mesh(planGeometry, tubeMaterial); //网格模型对象Mesh
obj.position.z = 350;
obj.position.y = 1.5;
obj.position.x = 599;
obj.rotateX(Math.PI / 2.0);
obj.rotateZ(Math.PI / 2.0);
var obj2 = obj.clone();
obj2.translateY(1200);
obj2.rotateZ(Math.PI);
var geometry2 = new THREE.PlaneGeometry(1230, 40);
var obj3 = new THREE.Mesh(geometry2, tubeMaterial);
obj3.position.set(0, 1.5, -10);
obj3.rotation.x = -Math.PI / 2.0;
var group = new THREE.Group();
group.add(obj);
group.add(obj2);
group.add(obj3);
scene.add(group);
}
//region 矩形区域
function addPlane(x, z, width, length, scene) {
var LineMat = new THREE.MeshLambertMaterial();
new THREE.TextureLoader().load("http://zuoben.top/#3-8/assets/textures/line.png", function (map) {
LineMat.map = map;
LineMat.needsUpdate = true;
});
var lineWidth = 8
var geometry = new THREE.PlaneGeometry(lineWidth, length);
var obj = new THREE.Mesh(geometry, LineMat);
obj.position.set(x, 1.5, z);
obj.rotation.x = -Math.PI / 2.0;
var obj2 = obj.clone();
obj2.translateX(width);
var geometry2 = new THREE.PlaneGeometry(lineWidth, width);
var obj3 = new THREE.Mesh(geometry2, LineMat);
obj3.position.set(x + width / 2, 1.5, z - length / 2 + lineWidth / 2);
obj3.rotation.x = -Math.PI / 2.0;
obj3.rotation.z = -Math.PI / 2.0;
var obj4 = obj3.clone();
obj4.translateX(length - lineWidth);
var group = new THREE.Group();
group.add(obj);
group.add(obj2);
group.add(obj3);
group.add(obj4);
group.translateX(-width / 2);
scene.add(group);
}
// 口号标语
function initSloganModel() {
var planGeometry = new THREE.PlaneGeometry(500, 35);
texture = new THREE.TextureLoader().load('assets/textures/biaoyu.png');
texture.wrapS = THREE.RepeatWrapping
var tubeMaterial = new THREE.MeshBasicMaterial({
map: texture,
transparent: true,
side: THREE.DoubleSide,
});
// 设置数组材质对象作为网格模型材质参数
var mesh = new THREE.Mesh(planGeometry, tubeMaterial); //网格模型对象Mesh
mesh.position.y = 175;
mesh.position.z = 710;
// mesh.rotateZ(3.14);
scene.add(mesh); //网格模型添加到场景中
}
//region 库区
/** 放置虚线框区域和库区名称 */
function addArea(x, z, width, length, scene, name, textColor, font_size, textposition) {
addPlane(x, z, width, length, scene);
new THREE.FontLoader().load('http://zuoben.top/#3-8/font/FZYaoTi_Regular.json', function (font) {
//加入立体文字
var text = new THREE.TextGeometry(name, {
// 设定文字字体
font: font,
//尺寸
size: font_size,
//厚度
height: 0.01
});
text.computeBoundingBox();
//3D文字材质
var m = new THREE.MeshStandardMaterial({
color: "#" + textColor
});
var mesh = new THREE.Mesh(text, m)
if (textposition == "左对齐") {
mesh.position.x = x - width / 2 + 10;
} else if (textposition == "居中") {
mesh.position.x = x - 65;
} else if (textposition == "右对齐") {
mesh.position.x = x + width / 2 - 60;
}
mesh.position.y = 1.3;
mesh.position.z = z + length / 2 - 20;
mesh.rotation.x = -Math.PI / 2.0;
scene.add(mesh);
});
}
//创建墙纹理
function createWallMaterail() {
matArr.push(new THREE.MeshPhongMaterial({
color: 0xafc0ca
})); //前 0xafc0ca :灰色
matArr.push(new THREE.MeshPhongMaterial({
color: 0x9cb2d1
})); //后 0x9cb2d1:淡紫
matArr.push(new THREE.MeshPhongMaterial({
color: 0xd6e4ec
})); //上 0xd6e4ec: 偏白色
matArr.push(new THREE.MeshPhongMaterial({
color: 0xd6e4ec
})); //下
matArr.push(new THREE.MeshPhongMaterial({
color: 0xafc0ca
})); //左 0xafc0ca :灰色
matArr.push(new THREE.MeshPhongMaterial({
color: 0xafc0ca
})); //右
}
//返回墙对象
function returnWallObject(width, height, depth, angle, material, x, y, z, name) {
var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
var cube = new THREE.Mesh(cubeGeometry, material);
cube.position.x = x;
cube.position.y = y;
cube.position.z = z;
cube.rotation.y += angle * Math.PI;
cube.name = name;
return cube;
}
//墙上挖门,通过两个几何体生成BSP对象
function createResultBsp(bsp, objects_cube) {
var material = new THREE.MeshPhongMaterial({
color: 0x9cb2d1,
specular: 0x9cb2d1,
shininess: 30,
transparent: true,
opacity: 1
});
var BSP = new ThreeBSP(bsp);
for (var i = 0; i < objects_cube.length; i++) {
var less_bsp = new ThreeBSP(objects_cube[i]);
BSP = BSP.subtract(less_bsp);
}
var result = BSP.toMesh(material);
result.material.flatshading = THREE.FlatShading;
result.geometry.computeFaceNormals(); //重新计算几何体侧面法向量
result.geometry.computeVertexNormals();
result.material.needsUpdate = true; //更新纹理
result.geometry.buffersNeedUpdate = true;
result.geometry.uvsNeedUpdate = true;
scene.add(result);
}
//创建地板
function createFloor() {
var loader = new THREE.TextureLoader();
loader.load("assets/textures/floor.jpg", function (texture) {
texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
texture.repeat.set(10, 10);
var floorGeometry = new THREE.BoxGeometry(2600, 1400, 1);
var floorMaterial = new THREE.MeshBasicMaterial({
map: texture,
});
var floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.name = "地面";
scene.add(floor);
});
}
//创建墙
function createCubeWall(width, height, depth, angle, material, x, y, z, name) {
var cubeGeometry = new THREE.BoxGeometry(width, height, depth);
var cube = new THREE.Mesh(cubeGeometry, material);
cube.position.x = x;
cube.position.y = y;
cube.position.z = z;
cube.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
cube.name = name;
scene.add(cube);
}
//创建门
function createDoor(width, height, depth, angle, x, y, z, name) {
var loader = new THREE.TextureLoader();
var doorgeometry = new THREE.BoxGeometry(width, height, depth);
var doormaterial_left = new THREE.MeshPhongMaterial({
map: loader.load("http://zuoben.top/#3-8/assets/textures/door_left.png")
});
doormaterial_left.opacity = 1.0;
doormaterial_left.transparent = true;
var doormaterial_right = new THREE.MeshPhongMaterial({
map: loader.load("http://zuoben.top/#3-8/assets/textures/door_right.png")
});
doormaterial_right.opacity = 1.0;
doormaterial_right.transparent = true;
var doormaterialArr = [];
if (name.indexOf("左门") > -1) {
doorgeometry.translate(50, 0, 0);
doormaterialArr = [doormaterial_left, doormaterial_left, doormaterial_left,
doormaterial_left, doormaterial_left, doormaterial_right
];
} else {
doorgeometry.translate(-50, 0, 0);
doormaterialArr = [doormaterial_right, doormaterial_right, doormaterial_right,
doormaterial_right, doormaterial_right, doormaterial_left
];
}
var door = new THREE.Mesh(doorgeometry, doormaterialArr);
door.position.set(x, y, z);
door.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
door.name = name;
scene.add(door);
}
//创建窗户
function createWindow(width, height, depth, angle, x, y, z, name) {
var loader = new THREE.TextureLoader();
loader.load("assets/textures/window.png", function (texture) {
var windowgeometry = new THREE.BoxGeometry(width, height, depth);
var windowmaterial = new THREE.MeshBasicMaterial({
map: texture,
color: 0xffffff
});
windowmaterial.opacity = 1.0;
windowmaterial.transparent = true;
var window = new THREE.Mesh(windowgeometry, windowmaterial);
window.position.set(x, y, z);
window.rotation.y += angle * Math.PI; //-逆时针旋转,+顺时针
window.name = name;
scene.add(window);
});
}
// 键盘事件
document.onkeydown = function (event) {
// 打开
if (event.keyCode == 79) {
doorOpenColse(true);
}
// 关闭
if (event.keyCode == 67) {
doorOpenColse(false);
}
}
// 门打开关闭
function doorOpenColse(bool) {
var l1 = scene.getObjectByName("左门1");
var l2 = scene.getObjectByName("左门2");
var r1 = scene.getObjectByName("右门1");
var r2 = scene.getObjectByName("右门2");
// 打开
if (bool) {
new TWEEN.Tween({
lv: 0,
rv: 0,
})
.to({
lv: -0.5 * Math.PI,
rv: 0.5 * Math.PI,
}, 1000)
.easing(TWEEN.Easing.Bounce.Out)
.onUpdate(function () {
l1.rotation.y = this.lv;
l2.rotation.y = this.lv;
r1.rotation.y = this.rv;
r2.rotation.y = this.rv;
})
.start();
}
// 关闭
if (!bool) {
new TWEEN.Tween({
lv: -0.5 * Math.PI,
rv: 0.5 * Math.PI,
})
.to({
lv: 0,
rv: 0,
}, 1000)
.easing(TWEEN.Easing.Bounce.Out)
.onUpdate(function () {
l1.rotation.y = this.lv;
l2.rotation.y = this.lv;
r1.rotation.y = this.rv;
r2.rotation.y = this.rv;
})
.start();
}
}
// 拾取对象
function pickupObjects(event) {
// 点击屏幕创建一个向量
var raycaster = new THREE.Raycaster();
var vector = new THREE.Vector2((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window
.innerHeight) * 2 + 1);
var fxl = new THREE.Vector3(0, 1, 0);
var groundplane = new THREE.Plane(fxl, 0);
raycaster.setFromCamera(vector, camera);
var ray = raycaster.ray;
let intersects = ray.intersectPlane(groundplane);
console.log(intersects)
// 点击屏幕创建一个向量
var vector1 = new THREE.Vector3((event.clientX / window.innerWidth) * 2 - 1, -(event.clientY / window
.innerHeight) * 2 + 1, 0.5);
vector1 = vector1.unproject(camera); // 将屏幕的坐标转换成三维场景中的坐标
var raycaster = new THREE.Raycaster(camera.position, vector1.sub(camera.position).normalize());
var intersects1 = raycaster.intersectObjects(scene.children, true);
if (intersects1.length > 0) {
console.log(intersects1)
// intersects1[0].object.material.color.set("#ff0000");
}
}
document.addEventListener('click', pickupObjects, false); //监听单击拾取对象初始化球体
var clock = new THREE.Clock(); //声明一个时钟对象
const loopTime = 50 * 1000; // loopTime: 循环一圈的时间
function renderScene() {
orbit.update();
TWEEN.update();
// 使用requestAnimationFrame函数进行渲染
requestAnimationFrame(renderScene);
renderer.render(scene, camera);
// 更新帧动画的时间
if (mixer) {
// mixer.update(clock.getDelta());
}
if (peopleMixer) {
peopleMixer.update(clock.getDelta());
}
if (curve) {
let time = Date.now();
let t = (time % loopTime) / loopTime; // 计算当前时间进度百分比
changeLookAt(t);
}
if (textureSecure) {
texture.offset.x += 0.004;
textureSecure.offset.x += 0.0005;
}
// console.log(camera.position)
}
// 渲染的场景
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, false);
</script>
</body>
</html>