Three.js 模型扫光效果

需求:

1.需要在模型上实现 内容标记,扩散光圈,横向扫光效果.

 

方案:

通常就是通过更新材质信息来实现动画效果,

1.给材质加一个动态属性

2.接收这个动态属性去更新材质的数据(应该是这么理解,太多会导致性能问题么? 听说现在可以用GPU进行优化,没试过)

 

实现:

1.创建动画,并添加到模型上面

  wordBox1(number=10){
      // 世界模型创建
    var geometrys = new THREE.Geometry()

    // 环形扫光,圆圈扫光效果 从中心向外扩张
    const uniforms = {
      u_color: { value: new THREE.Color("#5588aa") },
      u_tcolor: { value: new THREE.Color("#5588aa") },
      u_r: { value: 200 },
      u_length: { value: 100 },//扫过区域
      u_max: { value: 500 }//扫过最大值
    };
    // 顶点着色器
    const vertexShader = `
    varying vec3 vp;
    void main(){
    vp = position; 
    gl_Position    = projectionMatrix * modelViewMatrix * vec4(position, 0.5);
    }
    `
      
    // 片元着色器代码

    const fragmentShader = `
    varying vec3 vp;
      uniform vec3 u_color;
      uniform vec3 u_tcolor;
      uniform float u_r;
      uniform float u_length;
      uniform float u_max;
      
      float getLeng(float x, float y){
          return  sqrt((x-0.0)*(x-0.0)+(y-0.0)*(y-0.0));
      }
      void main(){ 
          float uOpacity = 0.3; 
          vec3 vColor = u_color;
          float uLength = getLeng(vp.x,vp.z);
          gl_FragColor = vec4(vColor,uOpacity);
        // 就是下面的内容实现的光效(当动态参数变换到某个区域时,对应的材质也跟着变,就是这样子
// 环形扫光 if ( uLength <= u_r && uLength > u_r - u_length ) { // 直线扫光 // if ( vp.z <= u_r && vp.z > u_r - u_length ) { // 单向渐变光效果 // float op = sin( (u_r - uLength) / u_length ) * 0.6 + 0.3 ; // uOpacity = op; // vColor = u_tcolor; // 双向渐变光效果 float per = (u_r - uLength) / uLength; // 范围0~1 if(per >= 0.5){ per = 1.0 - per; } gl_FragColor.rgb = mix( gl_FragColor.rgb, vec3(1.0,1.0,0.0), per); // 渐变色
         
} } ` const material = new THREE.ShaderMaterial({ uniforms: uniforms, vertexShader: vertexShader,// 顶点着色器 fragmentShader: fragmentShader,// 片元着色器 side: THREE.DoubleSide,//双面显示 transparent: true,//允许透明 depthWrite:true }); let floorGeometry = new THREE.PlaneGeometry(2000, 2000, 1); var sphereMaterial = new THREE.MeshStandardMaterial({ color: 0x00ffff,side: THREE.DoubleSide }); var floorsphere = new THREE.Mesh(floorGeometry, sphereMaterial); // floorsphere.castShadow = true; //default is false // floorsphere.receiveShadow = true; //default floorsphere.position.set(0, -1, 0); floorsphere.rotation.x = Math.PI * -0.5; // floorsphere.updateMatrix(); floorsphere.lookAt(0, 0, 0); // this.scene.add(floorsphere); // geometrys.merge(floorGeometry, floorsphere.matrix); var materialA = new THREE.MeshStandardMaterial({ color: 0x00ffff,side: THREE.DoubleSide }); const geometry = new THREE.BoxGeometry(); for ( let i = 0; i < number; i ++ ) { const mesh = new THREE.Mesh( geometry, materialA ); mesh.position.x = Math.random() * 1600 - 800; mesh.position.y = 20; mesh.position.z = Math.random() * 1600 - 800; // mesh.scale.x = 20; mesh.scale.y = Math.random() * 80 + 10; // mesh.scale.z = 20; mesh.updateMatrix(); mesh.matrixAutoUpdate = false; mesh.name = 'boxA'; // mesh.shader = shader; // const axesHelper = new THREE.AxesHelper(100); // mesh.add(axesHelper); // console.log('顶点位置', mesh); geometrys.merge(mesh.geometry, mesh.matrix); this.pointslists.push(mesh.position) } const newMesh = new THREE.Mesh( geometrys, material ); newMesh.name = 'boxB'; // const a = new THREE.Euler(10, 0, 10, 'ZYX'); // 欧拉角 // newMesh.rotation.setFromVector3(a); this.scene.add( newMesh ); // console.log(newMesh); this.render() },

 

2.更新动画

    update() {
      let that = this

      // 更新扫光效果
      let boxModel = this.scene.getObjectByName('boxB')
      // boxModel.material.uniforms.y.value += 0.1;
        // 一旦y接近模型mesh顶部,重新设置为0,这样扫光反复循环
        // if (boxModel.material.uniforms.y.value > 50) boxModel.material.uniforms.y.value = -0.5;
        let dalte = this.clock.getDelta();
        if (boxModel) {
      //更新u_r 的值 boxModel.material.uniforms.u_r.value
+= dalte * 500; if (boxModel.material.uniforms.u_r.value >= boxModel.material.uniforms.u_max.value) { boxModel.material.uniforms.u_r.value = 200 } } }

 

posted @ 2024-07-11 11:09  睡到自然醒ccc  阅读(4)  评论(0编辑  收藏  举报