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