最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Three.js中實(shí)現(xiàn)流動(dòng)的管線效果的全過程

 更新時(shí)間:2025年09月06日 09:49:12   作者:阿琰a_  
最近使用threejs開發(fā)項(xiàng)目, 碰到需要在模型的管道上畫出流動(dòng)效果,下面這篇文章主要介紹了Three.js中實(shí)現(xiàn)流動(dòng)的管線效果的相關(guān)資料,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

最近在跟第三方對(duì)接工業(yè)流程的模型,發(fā)現(xiàn)建模的管線還挺好看的,給材質(zhì)加上一個(gè)偏移,就會(huì)出現(xiàn)一直流動(dòng)的效果,于是在想可不可以使用Three.js畫出同等效果的管線。

Three.js文檔:three.js docs

整體的實(shí)現(xiàn)思路是先繪制管線模型,然后在模型上添加一個(gè)動(dòng)態(tài)的著色器材質(zhì),實(shí)現(xiàn)管線流動(dòng)效果。

首先我想通過使用CatmullRomCurve3和TubeGeometry這倆個(gè)類API去創(chuàng)建平滑的 3D 樣條曲線。

代碼如下

/**
 * 繪制管線
 * @param THREE
 * @param scene
 * @param points
 */
function createPipeLine(THREE, scene ,points) {
  const cps = points.map(item=>{
    return new THREE.Vector3(...item)
  })
  console.log(cps,'cps')
  const curve = new THREE.CatmullRomCurve3(cps)

  let geometry = new THREE.TubeGeometry(curve, 100, 10, 32)
  const material = new THREE.MeshLambertMaterial({
    color: '#19bbd5',
    side: THREE.DoubleSide,
  })

  let length = curve.getLength()
  let uniforms = {
    totalLength: { value: length },
    stripeOffset: { value: 0 },        // 條紋偏移量
    stripeWidth: { value: 100 },       // 條紋寬度 (歸一化值)
    stripeSpacing: { value: 100 },     // 條紋間距 (歸一化值)
    stripeColor: { value: new THREE.Color('#096be3') }, // 條紋顏色
    speedFactor: { value: 50 },
  };

  material.onBeforeCompile = shader => {
    shader.uniforms.totalLength = uniforms.totalLength;
    shader.uniforms.stripeOffset = uniforms.stripeOffset;
    shader.uniforms.stripeWidth = uniforms.stripeWidth;
    shader.uniforms.stripeSpacing = uniforms.stripeSpacing;
    shader.uniforms.stripeColor = uniforms.stripeColor;

    shader.fragmentShader = `
        uniform float totalLength;
        uniform float stripeOffset;
        uniform float stripeWidth;
        uniform float stripeSpacing;
        uniform vec3 stripeColor;

        ${shader.fragmentShader}
        `.replace(
        `#include <color_fragment>`,
        `#include <color_fragment>

        // 計(jì)算條紋模式
        float pattern = mod((vUv.x - stripeOffset) * totalLength / (stripeWidth + stripeSpacing), 1.0);
        float isStripe = step(pattern, stripeWidth / (stripeWidth + stripeSpacing));

        // 平滑邊緣
        float edge = fwidth(vUv.x) * 2.0;
        float smoothFactor = smoothstep(0.0, edge, abs(pattern - 0.5 * stripeWidth));

        // 混合顏色
        diffuseColor.rgb = mix(diffuseColor.rgb, stripeColor, isStripe * smoothFactor);
      `
    )
  }

  material.defines = { 'USE_UV': "" }
  let mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  let clock = new THREE.Clock()
  function animation() {
    const delta = clock.getDelta();

    // 計(jì)算歸一化的流動(dòng)速度 (速度系數(shù) / 管線長(zhǎng)度)
    const normalizedSpeed = uniforms.speedFactor.value / uniforms.totalLength.value*10;

    // 更新條紋偏移量
    uniforms.stripeOffset.value += delta * normalizedSpeed;

    // 重置偏移量以保持循環(huán)
    uniforms.stripeOffset.value = uniforms.stripeOffset.value % 1.0;

    requestAnimationFrame(animation)
  }
  animation()
}

const points = [[-1137,70,2850],[694,70,2810],[684,70,1550]]
createPipeLine(THREE,scene,points)

因?yàn)槲壹虞d了一個(gè)模型地板比較大,所以我把管線相關(guān)的參數(shù)設(shè)置的也比較大。

實(shí)現(xiàn)效果如下:

其實(shí)沒有達(dá)到我的預(yù)期,我想的是如下箭頭效果,但這個(gè)曲線它自動(dòng)偏移圓滑了一些。

于是我換一個(gè)曲線API,使用LineCurve3去實(shí)現(xiàn)管線。

效果如下,管線畫出來了,但是拐角又不是很好看。解決辦法就是把拐角的點(diǎn)設(shè)置多一些,就看起來比較圓滑了。

拐角點(diǎn)設(shè)置多后的效果

代碼如下

/**
 * 繪制管線
 * @param THREE
 * @param scene
 * @param points
 */
function createPipeLine(THREE, scene ,points) {
  const cps = points.map(item=>{
    return new THREE.Vector3(...item)
  })
  console.log(cps,'cpscps')
  const curve = new THREE.CurvePath();
  for (let i = 0; i < cps.length - 1; i++) {
    // 每?jī)蓚€(gè)點(diǎn)之間形成一條三維直線
    const lineCurve = new THREE.LineCurve3(cps[i], cps[i + 1]);
    // curvePath有一個(gè)curves屬性,里面存放組成該三維路徑的各個(gè)子路徑
    curve.curves.push(lineCurve);
  }
  
  let geometry = new THREE.TubeGeometry(curve, 100, 10, 32)
  const material = new THREE.MeshLambertMaterial({
    color: '#19bbd5',
    side: THREE.DoubleSide,
  })

  let length = curve.getLength()
  let uniforms = {
    totalLength: { value: length },
    stripeOffset: { value: 0 },        // 條紋偏移量
    stripeWidth: { value: 100 },       // 條紋寬度 (歸一化值)
    stripeSpacing: { value: 100 },     // 條紋間距 (歸一化值)
    stripeColor: { value: new THREE.Color('#096be3') }, // 條紋顏色
    speedFactor: { value: 50 },
  };

  material.onBeforeCompile = shader => {
    shader.uniforms.totalLength = uniforms.totalLength;
    shader.uniforms.stripeOffset = uniforms.stripeOffset;
    shader.uniforms.stripeWidth = uniforms.stripeWidth;
    shader.uniforms.stripeSpacing = uniforms.stripeSpacing;
    shader.uniforms.stripeColor = uniforms.stripeColor;

    shader.fragmentShader = `
        uniform float totalLength;
        uniform float stripeOffset;
        uniform float stripeWidth;
        uniform float stripeSpacing;
        uniform vec3 stripeColor;

        ${shader.fragmentShader}
        `.replace(
        `#include <color_fragment>`,
        `#include <color_fragment>

        // 計(jì)算條紋模式
        float pattern = mod((vUv.x - stripeOffset) * totalLength / (stripeWidth + stripeSpacing), 1.0);
        float isStripe = step(pattern, stripeWidth / (stripeWidth + stripeSpacing));

        // 平滑邊緣
        float edge = fwidth(vUv.x) * 2.0;
        float smoothFactor = smoothstep(0.0, edge, abs(pattern - 0.5 * stripeWidth));

        // 混合顏色
        diffuseColor.rgb = mix(diffuseColor.rgb, stripeColor, isStripe * smoothFactor);
      `
    )
  }

  material.defines = { 'USE_UV': "" }
  let mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  let clock = new THREE.Clock()
  function animation() {
    const delta = clock.getDelta();

    // 計(jì)算歸一化的流動(dòng)速度 (速度系數(shù) / 管線長(zhǎng)度)
    const normalizedSpeed = uniforms.speedFactor.value / uniforms.totalLength.value*10;

    // 更新條紋偏移量
    uniforms.stripeOffset.value += delta * normalizedSpeed;

    // 重置偏移量以保持循環(huán)
    uniforms.stripeOffset.value = uniforms.stripeOffset.value % 1.0;

    requestAnimationFrame(animation)
  }
  animation()
}

const points = [[-1137,70,2850],[654, 70, 2850], [664, 70, 2840], [674, 70, 2830],[684,70,2820],[694,70,2810],[684,70,1550]]

createPipeLine(THREE,scene,points)

上面的方案能實(shí)現(xiàn)流動(dòng)的管線,但是拐角的點(diǎn)位很難處理,還是打算再換一種方案。

最終方案:直線使用LineCurve3+拐角曲線CatmullRomCurve3組合使用。

效果如下

源代碼

/**
 * 繪制管線
 * @param THREE
 * @param scene
 * @param points
 */
function createPipeLine(THREE, scene ,points) {
  if(points.length<=1){
    return false
  }
  const curvePath = new THREE.CurvePath();
  let prevPoint = null; // 用于記錄上一個(gè)點(diǎn)

  // 遍歷點(diǎn)數(shù)組
  for (let i = 0; i < points.length; i++) {
    const current = points[i];

    // 處理普通點(diǎn)(一維數(shù)組)
    if (Array.isArray(current) && current.length === 3 && typeof current[0] === 'number') {
      const point = new THREE.Vector3(...current);

      if (prevPoint === null) {
        // 第一個(gè)點(diǎn),只記錄不創(chuàng)建曲線
        prevPoint = point;
      } else {
        // 創(chuàng)建直線連接到當(dāng)前點(diǎn)
        curvePath.add(new THREE.LineCurve3(prevPoint, point));
        prevPoint = point;
      }
    }
    // 處理拐角點(diǎn)(二維數(shù)組)
    else if (Array.isArray(current) && current.length >= 3) {
      // 將拐角點(diǎn)數(shù)組轉(zhuǎn)換為Vector3
      const curvePoints = current.map(p => new THREE.Vector3(...p));

      // 確保與上一個(gè)點(diǎn)連接
      if (prevPoint !== null) {
        // 檢查拐角曲線的起點(diǎn)是否與上一個(gè)點(diǎn)相同
        if (!prevPoint.equals(curvePoints[0])) {
          // 如果不相同,添加一條連接線
          curvePath.add(new THREE.LineCurve3(prevPoint, curvePoints[0]));
        }
      }

      // 創(chuàng)建CatmullRom曲線
      const cornerCurve = new THREE.CatmullRomCurve3(curvePoints);
      curvePath.add(cornerCurve);

      // 更新上一個(gè)點(diǎn)為拐角曲線的最后一個(gè)點(diǎn)
      prevPoint = curvePoints[curvePoints.length - 1];
    } else {
      console.warn('無效的點(diǎn)格式:', current);
    }
  }

// 創(chuàng)建管道幾何體
  const segments = Math.max(100, Math.floor(curvePath.getLength() / 20));
  const geometry = new THREE.TubeGeometry(
      curvePath,
      segments, // 動(dòng)態(tài)計(jì)算分段數(shù)
      10,      // 管道半徑
      16,      // 徑向分段數(shù)
      false    // 是否閉合
  );
  const material = new THREE.MeshLambertMaterial({
    color: '#19bbd5',
    side: THREE.DoubleSide,
  })

  let length = curvePath.getLength()
  let uniforms = {
    totalLength: { value: length },
    stripeOffset: { value: 0 },        // 條紋偏移量
    stripeWidth: { value: 100 },       // 條紋寬度 (歸一化值)
    stripeSpacing: { value: 100 },     // 條紋間距 (歸一化值)
    stripeColor: { value: new THREE.Color('#096be3') }, // 條紋顏色
    speedFactor: { value: 50 },
  };

  material.onBeforeCompile = shader => {
    shader.uniforms.totalLength = uniforms.totalLength;
    shader.uniforms.stripeOffset = uniforms.stripeOffset;
    shader.uniforms.stripeWidth = uniforms.stripeWidth;
    shader.uniforms.stripeSpacing = uniforms.stripeSpacing;
    shader.uniforms.stripeColor = uniforms.stripeColor;

    shader.fragmentShader = `
        uniform float totalLength;
        uniform float stripeOffset;
        uniform float stripeWidth;
        uniform float stripeSpacing;
        uniform vec3 stripeColor;

        ${shader.fragmentShader}
        `.replace(
        `#include <color_fragment>`,
        `#include <color_fragment>

        // 計(jì)算條紋模式
        float pattern = mod((vUv.x - stripeOffset) * totalLength / (stripeWidth + stripeSpacing), 1.0);
        float isStripe = step(pattern, stripeWidth / (stripeWidth + stripeSpacing));

        // 平滑邊緣
        float edge = fwidth(vUv.x) * 2.0;
        float smoothFactor = smoothstep(0.0, edge, abs(pattern - 0.5 * stripeWidth));

        // 混合顏色
        diffuseColor.rgb = mix(diffuseColor.rgb, stripeColor, isStripe * smoothFactor);
      `
    )
  }

  material.defines = { 'USE_UV': "" }
  let mesh = new THREE.Mesh(geometry, material);
  scene.add(mesh);

  let clock = new THREE.Clock()
  function animation() {
    const delta = clock.getDelta();

    // 計(jì)算歸一化的流動(dòng)速度 (速度系數(shù) / 管線長(zhǎng)度)
    const normalizedSpeed = uniforms.speedFactor.value / uniforms.totalLength.value*10;

    // 更新條紋偏移量
    uniforms.stripeOffset.value += delta * normalizedSpeed;

    // 重置偏移量以保持循環(huán)
    uniforms.stripeOffset.value = uniforms.stripeOffset.value % 1.0;

    requestAnimationFrame(animation)
  }
  animation()
}

const points = [
        [-1137,70,2850],
        [440,70,2850],
        [[470,70,2838],[480,70,2825],[490,70,2812]],//拐角點(diǎn)二維數(shù)組且至少三個(gè)點(diǎn)
        [500,70,2800],
        [500,70,1850]
    ]

createPipeLine(THREE,scene,points)

總結(jié) 

到此這篇關(guān)于Three.js中實(shí)現(xiàn)流動(dòng)的管線效果的文章就介紹到這了,更多相關(guān)Three.js流動(dòng)管線效果內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論

尼勒克县| 玉屏| 华安县| 木兰县| 淳化县| 华池县| 陆丰市| 海盐县| 当雄县| 绩溪县| 汾西县| 鸡西市| 株洲县| 德阳市| 平凉市| 长葛市| 仁寿县| 巩义市| 鄯善县| 宝兴县| 买车| 满城县| 错那县| 巴彦县| 襄樊市| 博爱县| 三明市| 北京市| 灵寿县| 凤山市| 理塘县| 周宁县| 新巴尔虎右旗| 双辽市| 泾源县| 碌曲县| 通道| 荥经县| 宜阳县| 赣榆县| 榆树市|