threejs之创建发光墙体

threejs之创建发光墙体

组件文件:

图片两张:

bgImg.png

flowImg.png(图片是透明的,页面上看不到)

组件文件:

import * as THREE from 'three';
import bgImg from '/scene3D/bgImg.png'
import flowImg from '/scene3D/flowImg.png'
/**
 * 创建流体墙体材质
 * option =>
 * params bgUrl flowUrl
 * **/
const createWallMaterial = ({
  bgTexture,
  flowTexture
}) => {
  // 顶点着色器
  const vertexShader = `
          varying vec2 vUv;
          varying vec3 fNormal;
          varying vec3 vPosition;
          void main(){
                  vUv = uv;
                  vPosition = position;
                  vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
                  gl_Position = projectionMatrix * mvPosition;
          }
      `;
  // 片元着色器
  const fragmentShader = `
          uniform float time;
          varying vec2 vUv;
          uniform sampler2D flowTexture;
          uniform sampler2D bgTexture;
          void main( void ) {
              vec2 position = vUv;
              vec4 colora = texture2D( flowTexture, vec2( vUv.x, fract(vUv.y - time )));
              vec4 colorb = texture2D( bgTexture , position.xy);
              gl_FragColor = colorb + colorb * colora;
          }
      `;
  // 允许平铺
  flowTexture.wrapS = THREE.RepeatWrapping;
  return new THREE.ShaderMaterial({
    uniforms: {
      time: {
        value: 0,
      },
      flowTexture: {
        value: flowTexture,
      },
      bgTexture: {
        value: bgTexture,
      },
    },
    transparent: true,
    depthWrite: false,
    depthTest: false,
    side: THREE.DoubleSide,
    vertexShader: vertexShader,
    fragmentShader: fragmentShader,
  });
};
/**
 * 通过path构建墙体
 * option =>
 * params height path material expand(是否需要扩展路径)
 * **/
export const creatWallByPath = ({
  height = 10,
  path = [],
  material,
  expand = true,
}) => {
  let verticesByTwo = null;
  // 1.处理路径数据  每两个顶点为为一组
  if (expand) {
    // 1.1向y方向拉伸顶点
    verticesByTwo = path.reduce((arr, [x, y, z]) => {
      return arr.concat([
        [
          [x, y, z],
          [x, y + height, z],
        ],
      ]);
    }, []);
  } else {
    // 1.2 已经处理好路径数据
    verticesByTwo = path;
  }
  // 2.解析需要渲染的四边形 每4个顶点为一组
  const verticesByFour = verticesByTwo.reduce((arr, item, i) => {
    if (i === verticesByTwo.length - 1) return arr;
    return arr.concat([[item, verticesByTwo[i + 1]]]);
  }, []);
  // 3.将四边形面转换为需要渲染的三顶点面
  const verticesByThree = verticesByFour.reduce((arr, item) => {
    const [[point1, point2], [point3, point4]] = item;
    return arr.concat(
      ...point2,
      ...point1,
      ...point4,
      ...point1,
      ...point3,
      ...point4
    );
  }, []);
  const geometry = new THREE.BufferGeometry();
  // 4. 设置position
  const vertices = new Float32Array(verticesByThree);
  geometry.setAttribute("position", new THREE.BufferAttribute(vertices, 3));
  // 5. 设置uv 6个点为一个周期 [0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1]

  // 5.1 以18个顶点为单位分组
  const pointsGroupBy18 = new Array(verticesByThree.length / 3 / 6)
    .fill(0)
    .map((item, i) => {
      return verticesByThree.slice(i * 3 * 6, (i + 1) * 3 * 6);
    });
  // 5.2 按uv周期分组
  const pointsGroupBy63 = pointsGroupBy18.map((item, i) => {
    return new Array(item.length / 3)
      .fill(0)
      .map((it, i) => item.slice(i * 3, (i + 1) * 3));
  });
  // 5.3根据BoundingBox确定uv平铺范围
  geometry.computeBoundingBox();
  const { min, max } = geometry.boundingBox;
  const rangeX = max.x - min.x;
  const uvs = [].concat(
    ...pointsGroupBy63.map((item) => {
      const point0 = item[0];
      const point5 = item[5];
      const distance =
        new THREE.Vector3(...point0).distanceTo(new THREE.Vector3(...point5)) /
        (rangeX / 10);
      return [0, 1, 0, 0, distance, 1, 0, 0, distance, 0, distance, 1];
    })
  );
  geometry.setAttribute(
    "uv",
    new THREE.BufferAttribute(new Float32Array(uvs), 2)
  );
  // 更新法线
  // geometry.computeVertexNormals();
  const meshMat =
    material ||
    new THREE.MeshBasicMaterial({
      color: 0x00ffff,
      side: THREE.DoubleSide,
    });
  return new THREE.Mesh(geometry, material);
};
// 创建墙体
export function createWall(path) {
  const bgTexture = new THREE.TextureLoader().load(bgImg);
  const flowTexture = new THREE.TextureLoader().load(flowImg);
  const material = createWallMaterial({
    bgTexture,
    flowTexture
  });
  const wallMesh = creatWallByPath({
    height: 10, 
    path,
    material:material
  });
  return wallMesh
}

  注意:图片文件放在public中使用import引用

方法调用:

            const path = [
                [80, 0, -40],
                [10, 0, 0],
                [60, 0, 50],
                [0, 10, 0],
                [-60, 0, 50],
                [-50, 0, -30],
                [80, 0, -40],
            ];
            scene.add(createWall(path));

  效果:

 注:动态效果后续使用后补充

 

 

转载自:

https://www.cnblogs.com/loveFlex/p/17669620.html
 
 
posted @ 2025-06-16 18:18  莫小龙  阅读(64)  评论(0)    收藏  举报