携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第2天,点击查看活动详情
所用知识
Curve(曲线): threejs.org/docs/index.…
CatmullRomCurve3: threejs.org/docs/index.…
TubeBufferGeometry(管道): threejs.org/docs/index.…
Texture(纹理)
gsap:greensock.com/docs/
实现思路
通过一系列的点创建一条平滑的三维样条曲线,创建一个管道物体并将路径设置为曲线,设置材质,通过gsap动画实现纹理流动
初始化
创建一个场景,摄像机,渲染器,轨道控制器。
//创建场景和相机
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(
75,
window.innerWidth / window.innerHeight,
0.1,
1000
);
//创建渲染器,设置尺寸为窗口尺寸,并将渲染后的元素添加到body
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
//设置照相机的位置
camera.position.set(0, 5, 10);
// 添加控制器
let controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);
controls.enableDamping = true; //设置阻力效果
controls.enablePan = false;
controls.maxPolarAngle = 1.5;
controls.minDistance = 0.01;
controls.maxDistance = 70; //控制滚轮缩放的最大距离
//渲染器更新
let animate = function () {
renderer.render(scene, camera);
renderer.setSize(window.innerWidth, window.innerHeight);
//当窗口变化时重新设置渲染器的尺寸,这样按F12然后关掉能恢复原来的样子
requestAnimationFrame(animate);
};
animate();
我们可以得到一个黑色的窗口
创建管道
便于观察,先创建一个天空盒和平面(我上篇文章有介绍)
//创建一个天空盒
const skybox = new THREE.BoxGeometry(200, 200, 200)
const skyloader = new THREE.TextureLoader(); //加载材质
const skymaterials = [
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/px.jpg') }),
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/nx.jpg') }),
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/py.jpg') }),
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/ny.jpg') }),
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/pz.jpg') }),
new THREE.MeshBasicMaterial({ map: skyloader.load('./textures/nz.jpg') }),
];
skybox.scale(1, 1, -1);
const cube = new THREE.Mesh(skybox, skymaterials);
scene.add(cube)
//创建一个平面
const geometry = new THREE.PlaneGeometry( 10, 10, 1, 1)
const material = new THREE.MeshBasicMaterial( {color: 0xffff00, side: THREE.DoubleSide} );
const plane = new THREE.Mesh(geometry, material);
plane.rotation.x=-Math.PI/2
scene.add(plane);
得到下图
接着我们开始创建管道
TubeGeometry(path : Curve, tubularSegments : Integer, radius : Float, radialSegments : Integer, closed : Boolean) path — Curve - 一个由基类Curve继承而来的3D路径。 Default is a quadratic bezier curve.
tubularSegments — Integer - 组成这一管道的分段数,默认值为64。
radius — Float - 管道的半径,默认值为1。
radialSegments — Integer - 管道横截面的分段数目,默认值为8。
closed — Boolean 管道的两端是否闭合,默认值为false。
当我们把radialSegments设为2时就可以获得平的面,如下图
管道的path是一个Curve,我们需要创建一条曲线
构造函数 CatmullRomCurve3( points : Array, closed : Boolean, curveType : String, tension : Float ) points – Vector3点数组
closed – 该曲线是否闭合,默认值为false。
curveType – 曲线的类型,默认值为centripetal。
tension – 曲线的张力,默认为0.5。
曲线需要points Vector3点数组,则我们还需创建点数组。部分代码如下
//创建点数组
let linePoints=[
new THREE.Vector3(0,0,0),
new THREE.Vector3(10,5,0),
new THREE.Vector3(20,0,0),
new THREE.Vector3(20,0,5),
]
//1.创建曲线
let lineCurve=new THREE.CatmullRomCurve3(linePoints);
//2.根据曲线生成管道几何体
let geometry=new THREE.TubeBufferGeometry(
lineCurve, //一个由基类Curve继承而来的3D路径
100,//组成这一管道的分段数,默认值为64
1,//管道的半径,默认值为1
2,//管道横截面的分段数目,默认值为8
false //管道的两端是否闭合,默认值为false
);
//3.创建材质
let material=new THREE.MeshBasicMaterial({
color: 0xffffff,
});
//4.创建Mesh
let mesh=new THREE.Mesh(geometry,material)
添加到场景中可以得到白色的管道
设置纹理
//3.创建一个纹理
const textloader = new THREE.TextureLoader();
let texture = textloader.load("图片地址");
texture.repeat.set(1, 2);
//4.设置材质
let material=new THREE.MeshBasicMaterial({
color: 0xffffff,
map: texture,
transparent: true,
});
纹理贴图的 wrapS 和 wrapT 属性,定义了水平和垂直方向上纹理的包裹方式 使用RepeatWrapping,纹理将简单地重复到无穷大。 ClampToEdgeWrapping是默认值,纹理中的最后一个像素将延伸到网格的边缘。 使用MirroredRepeatWrapping, 纹理将重复到无穷大,在每次重复时将进行镜像。
可以得到下图
设置动画
// 创建动画
gsap.to(texture.offset, {
x: -1,
duration: 1,
repeat: -1,
ease: "none",
});
//offset 在 U 和 V 的每个方向上,纹理的单个重复从开头偏移多少。典型范围是0.0到1.0。
最后效果如图
调整下点的位置实现水平的箭头指引路线效果
let linePoints=[
new THREE.Vector3(0,0,0),
new THREE.Vector3(10,0,0),
new THREE.Vector3(20,0,0),
new THREE.Vector3(20,0,10),
]
可以根据不同需求改变点的位置
如果会着色器可以实现更好的效果,不过萌新还不会。 学习threejs第三周,请大佬们多多指点,给点建议。欢迎朋友们在评论区评论🥰