0

three.js 实现风暴云特效

 2 years ago
source link: https://juejin.cn/post/6978636242720292901
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
2021年06月28日 阅读 7237

three.js 实现风暴云特效

大家好,这里是 CSS兼WebGL 魔法使——alphardex。

本文我们将用three.js来实现风暴云特效,以下是最终实现的效果图

让我们开始吧!

为了实现这个特效,我们先简要了解一下FBM吧

FBM中文意思是分形布朗运动,另一种称呼是分形噪声(说明它也属于噪声的一种)。它常用于描绘各种自然之中的形状(山脉、云层、河流等)。概念是在一个for循环内叠加几次噪声(往往是6次,相当于一个八度octave),并在叠加的同时升高频率,降低振幅。以下是一个简易的fbm实现的噪声图案

#pragma glslify:centerUv=require(../modules/centerUv)
#pragma glslify:snoise=require(glsl-noise/simplex/2d)

uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;

varying vec2 vUv;
varying vec3 vPosition;

#define OCTAVES 6

float fbm(vec2 p){
    float sum=0.;
    float amp=.5;
    for(int i=0;i<OCTAVES;i++){
        float noise=snoise(p)*amp;
        sum+=noise;
        p*=2.;
        amp*=.5;
    }
    return sum;
}

void main(){
    vec2 cUv=centerUv(vUv,uResolution);
    vec2 p=cUv*3.;
    float noise=fbm(p);
    vec3 color=vec3(noise);
    gl_FragColor=vec4(color,1.);
}
复制代码

笔者的three.js模板:点击右下角的fork即可复制一份

为了将着色器模块化,需要用到glslify

同时也需要安装如下的npm包:glsl-noise

创建一张铺满屏幕的平面,作为画布

class CloudySky extends Base {
  clock!: THREE.Clock;
  cloudySkyMaterial!: THREE.ShaderMaterial;
  params!: any;
  constructor(sel: string, debug: boolean) {
    super(sel, debug);
    this.clock = new THREE.Clock();
    this.cameraPosition = new THREE.Vector3(0, 0, 1);
    this.params = {
      velocity: 5,
      skyColor: "#324678",
    };
  }
  // 初始化
  init() {
    this.createScene();
    this.createOrthographicCamera();
    this.createRenderer();
    this.createCloudySkyMaterial();
    this.createPlane();
    this.createLight();
    this.trackMousePos();
    this.addListeners();
    this.setLoop();
  }
  // 创建材质
  createCloudySkyMaterial() {
    const cloudySkyMaterial = new THREE.ShaderMaterial({
      vertexShader: cloudySkyVertexShader,
      fragmentShader: cloudySkyFragmentShader,
      side: THREE.DoubleSide,
      uniforms: {
        uTime: {
          value: 0,
        },
        uMouse: {
          value: new THREE.Vector2(0, 0),
        },
        uResolution: {
          value: new THREE.Vector2(window.innerWidth, window.innerHeight),
        },
        uVelocity: {
          value: this.params.velocity,
        },
        uSkyColor: {
          value: new THREE.Color(this.params.skyColor),
        },
      },
    });
    this.cloudySkyMaterial = cloudySkyMaterial;
    this.shaderMaterial = cloudySkyMaterial;
  }
  // 创建平面
  createPlane() {
    const geometry = new THREE.PlaneBufferGeometry(2, 2, 100, 100);
    const material = this.cloudySkyMaterial;
    this.createMesh({
      geometry,
      material,
    });
  }
  // 动画
  update() {
    const elapsedTime = this.clock.getElapsedTime();
    const mousePos = this.mousePos;
    if (this.cloudySkyMaterial) {
      this.cloudySkyMaterial.uniforms.uTime.value = elapsedTime;
      this.cloudySkyMaterial.uniforms.uMouse.value = mousePos;
    }
  }
}
复制代码

顶点着色器直接用默认的就可以了

片元着色器

思路也是基本的fbm写法,只是在外层连续应用了16次(这样特别烧显卡,但是实现的效果很炫酷,帅就完事了),并且加上了随着时间的x轴位移

#pragma glslify:centerUv=require(../modules/centerUv)
#pragma glslify:snoise=require(glsl-noise/simplex/3d)
#pragma glslify:invert=require(../modules/invert)

uniform float uTime;
uniform vec2 uMouse;
uniform vec2 uResolution;
uniform float uVelocity;
uniform vec3 uSkyColor;

varying vec2 vUv;
varying vec3 vPosition;

#define OCTAVES 6

float fbm(vec3 p){
    float sum=0.;
    float amp=1.;
    for(int i=0;i<OCTAVES;i++){
        vec3 r=p/amp*.2;
        float noise=snoise(r)*amp;
        sum+=noise;
        amp*=.5;
    }
    return sum;
}

void main(){
    vec2 cUv=centerUv(vUv,uResolution);
    vec2 p=cUv;
    vec3 ray=vec3(0.);
    vec3 eye=normalize(vec3(p,2.));
    float displacement=uTime*uVelocity;
    ray.x+=displacement;
    float cloud=0.;
    float sum=0.;
    for(int i=0;i<16;i++){
        ray+=eye;
        sum=fbm(ray);
        sum=clamp(sum,0.,1.)*.1;
        cloud+=sum;
    }
    vec3 color=uSkyColor+cloud;
    gl_FragColor=vec4(color,1.);
}
复制代码

最终效果如下

Cloudy Sky


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK