• Threejs围墙动画


    请添加图片描述

    Threejs围墙动画

    难点1:

    根据顶点构建出来的围墙,默认是z轴朝上。假如模型是y轴朝上,则不好控制围墙的边界顶点。
    这里的思路是 把模型绕x轴翻转90度,然后根据rayser射线选取顶点。根据顶点构建完围墙后,再把围墙mesh翻转90度回来

    代码:

    function addShape() {
      let c = [53.95640312236819, 193.4562016693937, 123.4327246849135, 187.81369736193992,
            125.41600819650955, 218.75893553744257, 51.14432668036085, 223.0195030041947, 53.95640312236819, 193.4562016693937];
      let posArr = [];
      let uvrr = [];
      let h = 10; //围墙拉伸高度
      for (let i = 0; i < c.length - 2; i += 2) {
        // 围墙多边形上两个点构成一个直线扫描出来一个高度为h的矩形
        // 矩形的三角形1
        posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
        // 矩形的三角形2
        posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
    
        // 注意顺序问题,和顶点位置坐标对应
        uvrr.push(0, 0, 1, 0, 1, 1);
        uvrr.push(0, 0, 1, 1, 0, 1);
      }
      let geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
      // 设置几何体attributes属性的位置position属性
      geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
      // 设置几何体attributes属性的位置uv属性
      geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array(uvrr), 2);
      geometry.computeVertexNormals()
    
      let mesh = new THREE.Mesh(geometry, custMaterial); //网格模型对象Mesh
      scene.add(mesh);
      mesh.rotateX(-Math.PI / 2);
    

    难点2:

    围墙的shader编写,这个shader是拷贝的maptalks里面的围墙shader,查看源代码直接拿来用即可。
    代码:

    const vertexs = {
      normal_vertex: "\n  precision lowp float;\n  precision lowp int;\n  "
          .concat(
              THREE.ShaderChunk.fog_pars_vertex,
              "\n  varying vec2 vUv;\n  void main() {\n    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    "
          )
          .concat(THREE.ShaderChunk.fog_vertex, "\n  }\n"),
    };
    
    const fragments = {
      rippleWall_fragment:
          "\n  precision lowp float;\n  precision lowp int;\n  uniform float time;\n  uniform float opacity;\n  uniform vec3 color;\n  uniform float num;\n  uniform float hiz;\n\n  varying vec2 vUv;\n\n  void main() {\n    vec4 fragColor = vec4(0.);\n    float sin = sin((vUv.y - time * hiz) * 10. * num);\n    float high = 0.92;\n    float medium = 0.4;\n    if (sin > high) {\n      fragColor = vec4(mix(vec3(.8, 1., 1.), color, (1. - sin) / (1. - high)), 1.);\n    } else if(sin > medium) {\n      fragColor = vec4(color, mix(1., 0., 1.-(sin - medium) / (high - medium)));\n    } else {\n      fragColor = vec4(color, 0.);\n    }\n\n    vec3 fade = mix(color, vec3(0., 0., 0.), vUv.y);\n    fragColor = mix(fragColor, vec4(fade, 1.), 0.85);\n    gl_FragColor = vec4(fragColor.rgb, fragColor.a * opacity * (1. - vUv.y));\n  }\n",
    };
    
    const custMaterial = new THREE.ShaderMaterial({
      uniforms: {
        time: {
          type: "pv2",
          value: 0,
        },
        color: {
          type: "uvs",
          value: new THREE.Color("#FF4127"),
        },
        opacity: {
          type: "pv2",
          value: 1.0,
        },
        num: {
          type: "pv2",
          value: 10,
        },
        hiz: {
          type: "pv2",
          value: 0.15,
        },
      },
      vertexShader: vertexs.normal_vertex,
      fragmentShader: fragments.rippleWall_fragment,
      blending: THREE.AdditiveBlending,
      transparent: !0,
      depthWrite: !1,
      depthTest: !0,
      side: THREE.DoubleSide,
    });
    

    然后循环函数里面控制时间参数。

    function animal() {
      renderer.render(scene, camera)
      if (custMaterial) {
        custMaterial.uniforms.time.value += 0.015;
      }
      requestAnimationFrame(animal)
    }
    

    demo代码
    shader代码来源于maptalks

    <template>
       <div ref="test" class="test">
    
       </div>
    </template>
    
    <script setup>
    
    import * as THREE from "three";
    import {onMounted, ref} from "vue";
    import {MapControls} from 'three/examples/jsm/controls/OrbitControls'
    
    const vertexs = {
      normal_vertex: "\n  precision lowp float;\n  precision lowp int;\n  "
          .concat(
              THREE.ShaderChunk.fog_pars_vertex,
              "\n  varying vec2 vUv;\n  void main() {\n    vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );\n    vUv = uv;\n    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n    "
          )
          .concat(THREE.ShaderChunk.fog_vertex, "\n  }\n"),
    };
    
    const fragments = {
      rippleWall_fragment:
          "\n  precision lowp float;\n  precision lowp int;\n  uniform float time;\n  uniform float opacity;\n  uniform vec3 color;\n  uniform float num;\n  uniform float hiz;\n\n  varying vec2 vUv;\n\n  void main() {\n    vec4 fragColor = vec4(0.);\n    float sin = sin((vUv.y - time * hiz) * 10. * num);\n    float high = 0.92;\n    float medium = 0.4;\n    if (sin > high) {\n      fragColor = vec4(mix(vec3(.8, 1., 1.), color, (1. - sin) / (1. - high)), 1.);\n    } else if(sin > medium) {\n      fragColor = vec4(color, mix(1., 0., 1.-(sin - medium) / (high - medium)));\n    } else {\n      fragColor = vec4(color, 0.);\n    }\n\n    vec3 fade = mix(color, vec3(0., 0., 0.), vUv.y);\n    fragColor = mix(fragColor, vec4(fade, 1.), 0.85);\n    gl_FragColor = vec4(fragColor.rgb, fragColor.a * opacity * (1. - vUv.y));\n  }\n",
    };
    
    const custMaterial1 = new THREE.ShaderMaterial({
      uniforms: {
        time: {
          type: "pv2",
          value: 0,
        },
        color: {
          type: "uvs",
          value: new THREE.Color("#FF4127"),
        },
        opacity: {
          type: "pv2",
          value: 1.0,
        },
        num: {
          type: "pv2",
          value: 10,
        },
        hiz: {
          type: "pv2",
          value: 0.15,
        },
      },
      vertexShader: vertexs.normal_vertex,
      fragmentShader: fragments.rippleWall_fragment,
      blending: THREE.AdditiveBlending,
      transparent: !0,
      depthWrite: !1,
      depthTest: !0,
      side: THREE.DoubleSide,
    });
    
    let test =  ref(null), renderer, scene, camera, controls, pmremGenerator
    
    onMounted(() => {
      initModel();
      test.value.appendChild(renderer.domElement)
      animal();
    })
    
    function animal() {
      renderer.render(scene, camera)
      if (custMaterial1) {
        custMaterial1.uniforms.time.value += 0.015;
      }
      requestAnimationFrame(animal)
    }
    
    function initModel() {
      renderer = new THREE.WebGLRenderer({
        antialias: true, //开启锯齿
      })
      renderer.physicallyCorrectLights = true;
      renderer.outputEncoding = THREE.sRGBEncoding;
      renderer.toneMapping = THREE.ACESFilmicToneMapping;
      renderer.toneMappingExposure = 0.85
      renderer.shadowMap.enabled = true;
      renderer.shadowMap.type = THREE.PCFSoftShadowMap;
      let width = test.value.clientWidth
      let height = test.value.clientHeight
      renderer.setPixelRatio(window.devicePixelRatio) //设置设备像素比率,防止Canvas画布输出模糊。
      renderer.setSize(width, height) //设置渲染区域尺寸
    
      scene = new THREE.Scene()
      camera = new THREE.PerspectiveCamera(30, width / height, 1, 40000)
      camera.up.set(0, 0 ,1)
      controls = new MapControls(camera, renderer.domElement)
      camera.position.set(50, 50, 50)
      addShape()
      scene.add(new THREE.AxesHelper(50))
      let ambient = new THREE.AmbientLight(0xffffff, 0.8)
      scene.add(ambient)
      const hemiLight = new THREE.HemisphereLight(0x00AAFF, 0xFFAA00, 0.8);
      scene.add(hemiLight);
    
      console.log(scene)
    }
    
    function addShape() {
      let c = [0,0, 10, 0, 10, 10, 0, 10, 0, 0]
      let posArr = [];
      let uvrr = [];
      let h = 10; //围墙拉伸高度
      for (let i = 0; i < c.length - 2; i += 2) {
        // 围墙多边形上两个点构成一个直线扫描出来一个高度为h的矩形
        // 矩形的三角形1
        posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], 0, c[i + 2], c[i + 3], h);
        // 矩形的三角形2
        posArr.push(c[i], c[i + 1], 0, c[i + 2], c[i + 3], h, c[i], c[i + 1], h);
    
        // 注意顺序问题,和顶点位置坐标对应
        uvrr.push(0, 0, 1, 0, 1, 1);
        uvrr.push(0, 0, 1, 1, 0, 1);
      }
      let geometry = new THREE.BufferGeometry(); //声明一个空几何体对象
      // 设置几何体attributes属性的位置position属性
      geometry.attributes.position = new THREE.BufferAttribute(new Float32Array(posArr), 3);
      // 设置几何体attributes属性的位置uv属性
      geometry.attributes.uv = new THREE.BufferAttribute(new Float32Array(uvrr), 2);
      geometry.computeVertexNormals()
    
    
      let custMaterial = new  THREE.MeshLambertMaterial({
        color: 0X049ef4,
        side : THREE.DoubleSide
      })
    
      let mesh = new THREE.Mesh(geometry, custMaterial1); //网格模型对象Mesh
      scene.add(mesh);
      // mesh.rotateX(-Math.PI / 2);
    
    }
    
    </script>
    
    <style scoped lang="less">
    .test {
      height: 100%;
    }
    </style>
    
    
  • 相关阅读:
    艾美捷内毒素纯化树脂基本参数和原理说明
    java毕业设计儿童疫苗接种提醒系统小程序服务端Mybatis+系统+数据库+调试部署
    引入css(层叠样式表)文件的三种方法
    【测试开发】Mq消息重复如何测试?
    云原生【Docker】—— 认识 Docker、Docker 与虚拟机对比、Docker 组件
    nvm安装及使用(mac)
    面试 Java 并发编程八股文十问十答第四期
    vue+js登陆页面, 前端实现滑块验证
    C++ 基于红黑树的map和set
    Tracking Everything -视频目标跟踪
  • 原文地址:https://blog.csdn.net/u010568976/article/details/126951650