用Three.js搞2种3D地球扫光效果

1,645 阅读3分钟

之前写了个3D地球,添加大量柱体和性能优化,这次给3D地球实现更加炫酷的特效。

1.搞个地球

{
            const geometry = new THREE.SphereGeometry(16, 32, 32);
            const texture = new THREE.TextureLoader().load('assets/world.jpg');
            texture.wrapS = THREE.RepeatWrapping;
            texture.wrapT = THREE.RepeatWrapping;

            const texture1 = new THREE.TextureLoader().load('assets/earth.jpg');
            texture1.wrapS = THREE.RepeatWrapping;
            texture1.wrapT = THREE.RepeatWrapping;

            const material = new THREE.MeshStandardMaterial({
              map: texture,
              bumpMap: texture,
              bumpScale: 1
            });
            const sphere = new THREE.Mesh(geometry, material);
            this.scene.add(sphere);
          }

添加bumpMap黑白凹凸贴图,让地球表面多点真实感。

bathymetry_bw_composite_4k.jpg

  • bumpMap:用于创建凹凸贴图的纹理。黑色和白色值映射到相对于灯光的感知深度。凹凸实际上不会影响对象的几何体,只会影响照明。如果定义了法线贴图,则会忽略此项。 image.png

2.渐变地球扫光

给地球添加一个透明外壳,半径比地球大一点。

const geometry = new THREE.SphereGeometry(18, 32, 16);
            const material = new THREE.MeshBasicMaterial({
              color: 0xffffff,
              transparent: true,
              opacity: 0.2
            }); 
            const sphere = new THREE.Mesh(geometry, material);
            this.scene.add(sphere);

image.png

扫光特效

顶点着色器,要将顶点着色器里面的uv传递给片元着色器

varying vec2 vUv;
void main(){
 vUv=uv;
 gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}

片元着色器

float PI = acos(-1.0);
uniform float iTime;
uniform vec3 uColor;
varying vec2 vUv;
void main(){
     vec2 uv = vUv+ vec2(0.0, iTime);//扫光向下移动
    float current = abs(sin(uv.y * PI));//透明度中间往两边减少
   gl_FragColor.rgb=uColor;//扫光颜色
   gl_FragColor.a = mix(1.0, 0.0, current);//透明度渐变
  }

扫光材质

const material = new THREE.ShaderMaterial({
              uniforms: {
                iTime: { value: 0.0 },//随时间变化
                uColor: { value: new THREE.Color('#FFFFFF') }//扫光颜色
              },
              transparent: true,//开启透明
              vertexShader: ``,
              fragmentShader: ``
            });

扫光动起来

animateAction() {
          if (this.material) {
            if (this.material.uniforms.iTime.value > 1) this.material.uniforms.iTime.value = 0;
            else this.material.uniforms.iTime.value += 0.005;//扫光速度
          }
        }

噔噔噔!渐变扫光效果完成。可通过uColor配置扫光的颜色 20240611_143324.gif

3.地球斑点扫光

与上面的渐变扫光相似,只是片元着色器有些不同。

对于画圆点可以参考我之前的文章用three.js搞3个炫酷粒子出场

float PI = acos(-1.0);
    uniform vec3 uColor;//点的颜色
    uniform vec2 pointNum;//点的数量
    uniform float iTime;//随时间变化
    varying vec2 vUv;//从顶点着色器传过来的uv
    void main() {
vec2 uv = vUv + vec2(0.0, iTime);//随着时间向下移动
float current = abs(sin(uv.y * PI));//中间往两边减少
//中间的那行点强调,其他点大小的逐渐变小
if(current < 0.996) {
current = current * 0.5;
}
//画多个圆点
float d = distance(fract(uv * pointNum * 2.0), vec2(0.5, 0.5));
if(d > current * 0.2) {//与中心点距离外的无颜色
discard;
} else {
gl_FragColor = vec4(uColor, current);
}
  • distance(point1,point2)计算两个点的距离,当point2为vec2(0.5, 0.5)时即计算点到中心的距离,可以通过距离半径if(d > radius)discard;来排除外面的圆点外面的颜色,然后就画出圆了。
  • uv的x和y范围是都是[0,1],通过将其乘以数量,然后fract规整取,fract(uv * pointNum)可以让横向和纵向的数量翻倍。

注意:

  • pointNum赋值最好是2:1的比例相对应,球体的贴图展开成矩形刚好是360:180=》2:1,这样才能保持画出的点是圆形的,否则有可能出现椭圆形。
 uniforms: {
pointNum: { value: new THREE.Vector2(32, 16) },
}
  • 另外,点的数量足够多才可以做到包围球体并扫描,所以我fract(uv * pointNum * 2.0)这里特意将数量乘以两倍。

20240611_161053.gif

GitHub地址

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