用Three.js搞个炫酷雷达扩散和扫描特效

1.画点建筑模型

添加光照,开启阴影

js 复制代码
//开启renderer阴影
  this.renderer.shadowMap.enabled = true;
this.renderer.shadowMap.type = THREE.PCFSoftShadowMap;

 //设置环境光
  const light = new THREE.AmbientLight(0xffffff, 0.6); // soft white light
this.scene.add(light);

 //夜晚天空蓝色,假设成蓝色的平行光
  const dirLight = new THREE.DirectionalLight(0x0000ff, 3);
   dirLight.position.set(50, 50, 50);
  this.scene.add(dirLight);   

平行光设置阴影

js 复制代码
//开启阴影
 dirLight.castShadow = true;
 //阴影相机范围
dirLight.shadow.camera.top = 100;
dirLight.shadow.camera.bottom = -100;
dirLight.shadow.camera.left = -100;
dirLight.shadow.camera.right = 100;
//阴影影相机远近
dirLight.shadow.camera.near = 1;
dirLight.shadow.camera.far = 200;
//阴影贴图大小
dirLight.shadow.mapSize.set(1024, 1024);
  • 平行光的阴影相机跟正交相机一样,因为平行光的光线是平行的,就跟视线是平行一样,切割出合适的阴影视角范围,用于计算阴影。
  • shadow.mapSize设置阴影贴图的宽度和高度,值越高,阴影的质量越好,但要花费计算时间更多。

增加建筑

js 复制代码
//添加一个平面
const pg = new THREE.PlaneGeometry(100, 100);
//一定要用受光材质才有阴影效果
          const pm = new THREE.MeshStandardMaterial({
            color: new THREE.Color('gray'),
            transparent: true,//开启透明
            side: THREE.FrontSide//只有渲染前面
          });
          const plane = new THREE.Mesh(pg, pm);
          plane.rotateX(-Math.PI * 0.5);
          plane.receiveShadow = true;//平面接收阴影
          this.scene.add(plane);

          //随机生成建筑
          this.geometries = [];
          const helper = new THREE.Object3D();
          for (let i = 0; i < 100; i++) {
            const h = Math.round(Math.random() * 15) + 5;
            const x = Math.round(Math.random() * 50);
            const y = Math.round(Math.random() * 50);
            helper.position.set((x % 2 ? -1 : 1) * x, h * 0.5, (y % 2 ? -1 : 1) * y);
            const geometry = new THREE.BoxGeometry(5, h, 5);
            helper.updateWorldMatrix(true, false);
            geometry.applyMatrix4(helper.matrixWorld);
            this.geometries.push(geometry);
          }
          //长方体合成一个形状
          const mergedGeometry = BufferGeometryUtils.mergeGeometries(this.geometries, false);
          //建筑贴图
          const texture = new THREE.TextureLoader().load('assets/image.jpg');
          texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
          const material = new THREE.MeshStandardMaterial({ map: texture,transparent: true });
          const cube = new THREE.Mesh(mergedGeometry, material);
          //形状产生阴影
          cube.castShadow = true;
          //形状接收阴影
          cube.receiveShadow = true;
          this.scene.add(cube);

效果就是很多高楼大厦的样子,为什么楼顶有窗?别在意这些细节,有的人就喜欢开天窗呢~

2.搞个雷达扩散和扫描特效

改变建筑材质shader,计算建筑的俯视uv

js 复制代码
material.onBeforeCompile = (shader, render) => {
            this.shaders.push(shader);
            //范围大小
            shader.uniforms.uSize = { value: 50 };
            shader.uniforms.uTime = { value: 0 };
            //修改顶点着色器
            shader.vertexShader = shader.vertexShader.replace(
              'void main() {',
              ` uniform float uSize;
              varying vec2 vUv;
              void main() {`
            );
            shader.vertexShader = shader.vertexShader.replace(
              '#include <fog_vertex>',
              `#include <fog_vertex>
              //计算相对于原点的俯视uv
                      vUv=position.xz/uSize;`
            );
            //修改片元着色器
            shader.fragmentShader = shader.fragmentShader.replace(
              'void main() {',
              `varying vec2 vUv;
                uniform float uTime;
                void main() {`
            );
            shader.fragmentShader = shader.fragmentShader.replace(
              '#include <dithering_fragment>',
              `#include <dithering_fragment>
              //渐变颜色叠加
                  gl_FragColor.rgb=gl_FragColor.rgb+mix(vec3(0,0.5,0.5),vec3(1,1,0),vUv.y);`
            );
          };

然后你将同样的onBeforeCompile函数赋值给平面的时候,没有对应的效果。

因为平面没有z,只有xy,而且经过了-90度旋转后,坐标位置也要对应反转,由此可以得出平面的uv计算公式

c++ 复制代码
vUv=vec2(position.x,-position.y)/uSize;

至此,建筑和平面的俯视uv一致了。

雷达扩散特效

  • 雷达扩散就是一段渐变的环,随着时间扩大。

  • 顶点着色器不变,改一下片元着色器,增加扩散环颜色uColor,对应shader.uniforms也要添加

js 复制代码
shader.uniforms.uColor = { value: new THREE.Color('#00FFFF') };

 const fragmentShader1 = `varying vec2 vUv;
                uniform float uTime;
                uniform vec3 uColor;
                uniform float uSize;
                void main() {`;
          const fragmentShader2 = `#include <dithering_fragment>
              //计算与中心的距离
            float d=length(vUv);            
                  if(d >= uTime&&d<=uTime+ 0.1) {
                  //扩散圈
                    gl_FragColor.rgb = gl_FragColor.rgb+mix(uColor,gl_FragColor.rgb,1.0-(d-uTime)*10.0 )*0.5  ;                    
                  }`;
         
shader.fragmentShader = shader.fragmentShader.replace('void main() {', fragmentShader1);
shader.fragmentShader = shader.fragmentShader.replace(
              '#include <dithering_fragment>',
              fragmentShader2); 
   
   //改变shader的时间变量,动起来
  animateAction() {
          if (this.shaders?.length) {
            this.shaders.forEach((shader) => {
              shader.uniforms.uTime.value += 0.005;
              if (shader.uniforms.uTime.value >= 1) {
                shader.uniforms.uTime.value = 0;
              }
            });
          }
        }

噔噔噔噔,完成啦!是立体化的雷达扩散,看起来很酷的样子。

雷达扫描特效

跟上面雷达扩散差不多,只要修改一下片元着色器

  • 雷达扫描是通过扇形渐变形成的,还要随着时间旋转角度
js 复制代码
  const fragmentShader1 = `varying vec2 vUv;
                uniform float uTime;
                uniform vec3 uColor;
                uniform float uSize;
                //旋转角度矩阵
                mat2 rotate2d(float angle)
{
    return mat2(cos(angle), - sin(angle),
                sin(angle), cos(angle));
}
//雷达扫描渐变范围
float vertical_line(in vec2 uv)
{
    if (uv.y > 0.0 && length(uv) < 1.2)
    {
        float theta = mod(180.0 * atan(uv.y, uv.x)/3.14, 360.0);
        float gradient = clamp(1.0-theta/90.0,0.0,1.0);
        return 0.5 * gradient;
    }
    return 0.0;
}
                void main() {`;
                
                
          const fragmentShader2 = `#include <dithering_fragment> 
            mat2 rotation_matrix = rotate2d(- uTime*PI*2.0);  
            //将雷达扫描扇形渐变混合到颜色中
            gl_FragColor.rgb= mix( gl_FragColor.rgb, uColor, vertical_line(rotation_matrix * vUv));                                       
                   `;

别问我上面的雷达扫描shader怎么搞出来的,其实我研究shader也不是很深入,就是看着shaderToy有些写得通俗易懂,然后扒拉下来改改再用就可以了。shaderToy里面好多优秀的案例,大家可以去看看学习一下,或许就有感悟了呢!

GitHub地址

https://github.com/xiaolidan00/my-earth

相关推荐
正小安30 分钟前
如何在微信小程序中实现分包加载和预下载
前端·微信小程序·小程序
_.Switch2 小时前
Python Web 应用中的 API 网关集成与优化
开发语言·前端·后端·python·架构·log4j
一路向前的月光2 小时前
Vue2中的监听和计算属性的区别
前端·javascript·vue.js
长路 ㅤ   2 小时前
vite学习教程06、vite.config.js配置
前端·vite配置·端口设置·本地开发
长路 ㅤ   2 小时前
vue-live2d看板娘集成方案设计使用教程
前端·javascript·vue.js·live2d
Fan_web2 小时前
jQuery——事件委托
开发语言·前端·javascript·css·jquery
安冬的码畜日常2 小时前
【CSS in Depth 2 精译_044】第七章 响应式设计概述
前端·css·css3·html5·响应式设计·响应式
莹雨潇潇3 小时前
Docker 快速入门(Ubuntu版)
java·前端·docker·容器
Jiaberrr3 小时前
Element UI教程:如何将Radio单选框的圆框改为方框
前端·javascript·vue.js·ui·elementui
Tiffany_Ho4 小时前
【TypeScript】知识点梳理(三)
前端·typescript