ThreeJS 的效果樣例流水管線(五)

火星写程序發表於2024-09-25

一、流水管線

實現邏輯:

  1)先自定義幾個點,透過CatmullRomCurve3生成一條平滑曲線

  2)根據生成的曲線在XY面擴充套件一個面,其中需要注意頂點索引、UV座標新增的順序,否則可能會導致繪製的圖片混亂,不是完整的圖片

  3)新增紋理同時設定偏移量實現流動效果

  4)為了保證顯示的箭頭圖示不失真,根據管線的長度和圖示的長度動態計算repeat的個數

ThreeJS 的效果樣例流水管線(五)
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)
}
流動管線

相關文章