preface

Hi, this is ALphardex, the CSS and WebGL magic wizard.

In this article, we will use three.js to realize the storm cloud effect, and the following is the final effect diagram

Let’s get started!

Preliminary knowledge

To do this, let’s take a quick look at FBM

FBM means fractal Brownian motion in Chinese, another name is fractal noise (it also belongs to a kind of noise). It is often used to describe various shapes in nature (mountains, clouds, rivers, etc.). The concept is to stack the noise several times (often six times, equivalent to an octave) in a for loop, increasing the frequency and decreasing the amplitude as you stack. Here is a simple FBM implementation noise pattern

#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.);
}
Copy the code

The preparatory work

The author’s three.js template: click fork in the lower right corner to copy it

To modularize shaders, glSLIFy is needed

You also need to install the following NPM package: GLSL-noise

positive

Scenario building

Create a flat surface that covers the screen as the canvas

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"}; }/ / initialization
  init() {
    this.createScene();
    this.createOrthographicCamera();
    this.createRenderer();
    this.createCloudySkyMaterial();
    this.createPlane();
    this.createLight();
    this.trackMousePos();
    this.addListeners();
    this.setLoop();
  }
  // Create material
  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;
  }
  // Create the plane
  createPlane() {
    const geometry = new THREE.PlaneBufferGeometry(2.2.100.100);
    const material = this.cloudySkyMaterial;
    this.createMesh({
      geometry,
      material,
    });
  }
  / / animation
  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; }}}Copy the code

Use the default vertex shader

Chip shader

The idea is the same as the basic FBM writing method, but the outer layer is applied 16 times continuously (this is special to burn the graphics card, but the effect is very cool, the cool thing is done), and the X-axis displacement over time is added

#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.);
}
Copy the code

The final result is as follows

The project address

Cloudy Sky