背景
最近项目开发中遇到一个需求,需要画个流程图,由管道连接组成,且管道内有水流效果,于是想到了用Three.js。但是咱也不会呀,于是开始学习,通过看B站,查文档,以及参考别人的文章,实现了一个简单的效果。如下图,在此记录一下实现方法,大家以后遇到同样的需求可以参考这篇文章。
效果
实现
我使用的是html中引入three.js的方式进行开发的,通过设置 type="importmap" 的方式实现了在vue和react环境中通用的写法,这种方式需要手动引入three源码,我使用的是vscode,需要按一个Live Server插件,模拟本地环境,写好代码后,通过鼠标右键open with live server打开。vue和react中通过npm即可。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<style>
body {
margin: 0;
overflow: hidden;
}
</style>
<script type="importmap">
{
"imports": {
"three": "../three.js/build/three.module.js",
"three/addons/": "../three.js/examples/jsm/"
}
}
</script>
<script src="./TubeGeometry.js" type="module">
</script>
</body>
</html>
TubeGeometry.js文件,详细步骤及每段代码的含义,我标记了注释
import * as THREE from 'three';
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'
// 1. 创建一个场景
const scene = new THREE.Scene();
const width = window.innerWidth;
const height = window.innerHeight;
function createPath(pointsArr) {
// 将参数数组转换成点数组的形式
pointsArr = pointsArr.map((point) => new THREE.Vector3(...point));
// 自定义三维路径 curvePath
const path = new THREE.CurvePath();
for (let i = 0; i < pointsArr.length - 1; i++) {
// 每两个点之间形成一条三维直线
const lineCurve = new THREE.LineCurve3(pointsArr[i], pointsArr[i + 1]);
// curvePath有一个curves属性,里面存放组成该三维路径的各个子路径
path.curves.push(lineCurve);
}
return path;
}
const pointsArr = [
[42, 0, 10],
[21, 0, 10],
[21, 0, 1],
[-3, 0, 1],
[-3, 0, -18],
[-10, 0, -18],
[-10, 0, 5],
[1, 0, 5],
[1, 0, 24],
[-27, 0, 24],
[-27, 0, 18],
[-46, 0, 19],
[-46, 0, -4],
[-25, 0, -6],
[-25, 0, -19],
[-35, 0, -20],
[-35, 0, -26],
[-30, 0, -30],
[3, 0, -30]
];
const curve = createPath(pointsArr);
// 2. 创建管道体
const tubeGeometry = new THREE.TubeGeometry(curve, 1000, 0.5, 10, false);
// 纹理贴图:一定要使用透明背景的图片,否则贴图会全部叠在一起,看不出来效果
const texLoader = new THREE.TextureLoader();
// 图片可以用这张:http://pic.yupoo.com/mazhenghjj/e546038d/9610773f.jpg
const texture = texLoader.load('./WechatIMG110.jpg');
// 允许横纵设置矩阵(人话就是可以平铺)
texture.wrapS = THREE.RepeatWrapping
texture.wrapT = THREE.RepeatWrapping
texture.repeat.x = 110;
texture.repeat.y = 1;
texture.offset.y = 0.5;
// 3. 创建管道材质
const tubeMaterial = new THREE.MeshPhongMaterial({
map: texture, // 颜色贴图
transparent: true,
color: 0x47d8fa,
side: THREE.DoubleSide,
});
// 底部网格(可以不设置)
const gridHelper = new THREE.GridHelper(300, 25, 0x004444, 0x004444);
scene.add(gridHelper);
const mesh = new THREE.Mesh( tubeGeometry, tubeMaterial );
mesh.position.y = 35;
mesh.position.x = 0;
mesh.rotateZ(3.14);
mesh.scale.set(2, 2, 2);
// 4. 把几何体(管道)和 材质 生成的网格物体添加到场景中
scene.add( mesh );
// 5. 设置点光源(关键的一步,没有这一步,没法照亮材质)
const pointLight = new THREE.PointLight(0xffffff, 1);
// 点光源位置
pointLight.position.set(100, 100, 100);
scene.add(pointLight);
// 6. 点光源辅助对象(可有可无,辅助观察光源位置,需要时放开 add 方法)
const sphereSize = 10;
const pointLightHelper = new THREE.PointLightHelper( pointLight, sphereSize );
// scene.add( pointLightHelper );
// 7. 环境光(整个场景的光,添加后整个管道都能更明亮)
const light = new THREE.AmbientLight( 0xffffff, 1 );
scene.add( light );
// 8. 相机位置(拍照片,关键,Three的每一个帧需要拍下来才能显示)
const camera = new THREE.PerspectiveCamera(30, width / height, 0.1, 2000);
// 超出视锥体的远裁截面的范围会被剪掉
camera.position.set(200, 200, 200);
// 定义相机的视线
camera.lookAt(100, 100, 100);
// 9. 渲染方法
const renderer = new THREE.WebGLRenderer();
renderer.setSize(width, height);
// 用相机(camera)渲染一个场景(scene)。类比相机的拍照动作
renderer.render(scene, camera);
document.body.appendChild(renderer.domElement);
// 10. 循环
function render() {
// 周期性渲染,更新 canvas 画布的内容
renderer.render(scene, camera);
requestAnimationFrame(render);
// 关键一步,循环改变贴图的位置,css的属性
texture.offset.x -= 0.04;
}
render();
// 11. 拖动相机位置(场景能放大、缩小、移动,其实通过更改相机位置做到的)
const controls = new OrbitControls( camera, renderer.domElement );
// 12. 这一步是优化,在窗口改变时,canvas 位置也跟着改变
window.onresize = function() {
renderer.setSize(window.innerWidth, window.innerHeight);
// 相机宽高比
camera.aspect = window.innerWidth/window.innerHeight;
// 更新摄像机投影矩阵
camera.updateProjectionMatrix();
}
以上就是全部代码及步骤详解,大家可以复制代码到本地慢慢调试效果。
参考链接
小建议
前段时间找工作(找了三个月,人麻了),看到很多岗位需要了解或数据WebGL,Three.js是一种不错的实现方式,大家可以学习学习,最后祝大家变得更强