一、流水管線
實現邏輯:
1)先自定義幾個點,透過CatmullRomCurve3生成一條平滑曲線
2)根據生成的曲線在XY面擴充套件一個面,其中需要注意頂點索引、UV座標新增的順序,否則可能會導致繪製的圖片混亂,不是完整的圖片
3)新增紋理同時設定偏移量實現流動效果
4)為了保證顯示的箭頭圖示不失真,根據管線的長度和圖示的長度動態計算repeat的個數
function addFlowByGeometry() { // 自定義點 const points = [ new THREE.Vector3(-5, 5, 0), new THREE.Vector3(-5, 0, 0), new THREE.Vector3(0, 0, 0), new THREE.Vector3(5, 0, 0), new THREE.Vector3(5, -5, 3), ]; // 生成一條平滑的曲線 const curve = new THREE.CatmullRomCurve3(points); const srcGeometry = new THREE.TubeGeometry(curve, 64, 0.8, 32, false); const srcMaterial = new THREE.MeshBasicMaterial({ color: 0x00ffff, transparent: true, opacity: 0.1, side: THREE.DoubleSide // 兩面都顯示 }); const srcMesh = new THREE.Mesh(srcGeometry, srcMaterial); scene.add(srcMesh); // 定義曲面的解析度 const widthSegments = 32; const heightSegments = 32; // 定義頂點位置和紋理座標 const vertices = []; const uvs = []; // 定義擴充套件寬度 const width = 0.5; for (let y = 0; y <= heightSegments; y++) { for (let x = 0; x <= widthSegments; x++) { const u = x / widthSegments; const v = y / heightSegments; // 獲取曲線上的點 const point = curve.getPoint(u); // 獲取曲線的切線向量 const tangent = curve.getTangent(u); // 計演算法線向量 const normal = new THREE.Vector3(-tangent.y, tangent.x, 0).normalize(); // 擴充套件成平面 const px = point.x + normal.x * (v * 2 - 1) * width; const py = point.y + normal.y * (v * 2 - 1) * width; const pz = point.z; vertices.push(px, py, pz); uvs.push(u, v); } } // 定義頂點索引 const indices = []; for (let y = 0; y < heightSegments; y++) { for (let x = 0; x < widthSegments; x++) { const a = x + y * (widthSegments + 1); const b = x + 1 + y * (widthSegments + 1); const c = x + 1 + (y + 1) * (widthSegments + 1); const d = x + (y + 1) * (widthSegments + 1); indices.push(a, b, d); indices.push(b, c, d); } } // 建立一個自定義的 BufferGeometry const geometry = new THREE.BufferGeometry(); // 將頂點位置新增到幾何體 geometry.setAttribute('position', new THREE.BufferAttribute(new Float32Array(vertices), 3)); // 將頂點索引新增到幾何體 geometry.setIndex(new THREE.BufferAttribute(new Uint16Array(indices), 1)); // 將紋理座標新增到幾何體 geometry.setAttribute('uv', new THREE.BufferAttribute(new Float32Array(uvs), 2)); // 建立紋理載入器 const textureLoader = new THREE.TextureLoader(); // 載入紋理 const texture = textureLoader.load('Objects/imgs/arrow.png', () => { texture.wrapS = THREE.RepeatWrapping; texture.wrapT = THREE.RepeatWrapping; const originalWidth = texture.image.width; const originalHeight = texture.image.height; // 建立平面幾何體 const [allWidth, allHeight] = [curve.getLength(), 1]; // 更新幾何體的尺寸 texture.repeat.set(allWidth / (originalWidth * allHeight / 2 / originalHeight), 1); // 建立材質 const material = new THREE.MeshBasicMaterial({ map: texture, side: THREE.DoubleSide, color: 0x00ffff, wireframe: false }); // 建立網格 const mesh = new THREE.Mesh(geometry, material); // 將網格新增到場景中 scene.add(mesh); }); setInterval(() => { texture.offset.x -= 0.04; }, 30) }