The effect

GIF playback is slow, download down quickly! Video address

Create a plane

const geometry = new three.PlaneGeometry(1.1.50.50);
const material = new three.MeshPhysicalMaterial({ 
    color: 0x665533.side: three.DoubleSide,
    transparent: true.blending: three.AdditiveBlending,
    // roughness: 0,
    / / metalness: 0.5.
    // envMap: shared.assets.env_warehouse.data
    map: ao.threeLoadTexture(this.data.src)
});
const plane = new three.Mesh(geometry, material);
Copy the code

Using onBeforeCompile

Create the uniforms

  • Stores the parameters to be sent to the shader for processing in the formatname: {value: 'value'}
var uniforms = {
    time: { value: 1.0}}Copy the code
  • Based on my partial understanding,vertexShaderTo deal with coordinates,fragmentShaderIt is used to process colors and output them to a location visible to the browser

Using onBeforeCompile

OnBeforeCompile prints the data contained in the shader before performing GPU calculations.

material.onBeforeCompile = (shader) = >{
    // Handle shader snippets
    console.log(shader)
    // The data to be processed needs to be passed into shader's uniforms.
    Der.imaginative = imaginative, because there were other variables in shader. imaginative?
    // I had to ask the same question to come up with a possible answer, and I felt stupid for asking that question.
    shader.uniforms.time = uniforms.time;
}
Copy the code

Expand the vertexShader or fragmentShader reference library (derived fromMike Luan)

Release #include for easy learning and processing.

export function threeExpandShaderIncludes(s) {
    const includePattern = /^[ \t]*#include +<([\w\d./]+)>/gm;
    function resolveIncludes(string) {

        return string.replace(includePattern, includeReplacer);

    }
    function includeReplacer(match, include) {
        const string = three.ShaderChunk[include];

        if (string === undefined) {

            throw new Error('Can not resolve #include <' + include + '>');

        }
        return resolveIncludes(string);
    }
    return resolveIncludes(s);
}
threeExpandShaderIncludes(shader.vertexShader)
Copy the code

shader.vertexShaderWhat is the data of

console.log(shader.vertexShader)Exported data

#define STANDARD
varying vec3 vViewPosition;
#ifdef USE_TRANSMISSION
	varying vec3 vWorldPosition;
#endif
#include <common>
#include <uv_pars_vertex>
#include <uv2_pars_vertex>
#include <displacementmap_pars_vertex>
#include <color_pars_vertex>
#include <fog_pars_vertex>
#include <normal_pars_vertex>
#include <morphtarget_pars_vertex>
#include <skinning_pars_vertex>
#include <shadowmap_pars_vertex>
#include <logdepthbuf_pars_vertex>
#include <clipping_planes_pars_vertex>
void main() {
	#include <uv_vertex>
	#include <uv2_vertex>
	#include <color_vertex>
	#include <beginnormal_vertex>
	#include <morphnormal_vertex>
	#include <skinbase_vertex>
	#include <skinnormal_vertex>
	#include <defaultnormal_vertex>
	#include <normal_vertex>
	#include <begin_vertex>
	#include <morphtarget_vertex>
	#include <skinning_vertex>
	#include <displacementmap_vertex>
	#include <project_vertex>
	#include <logdepthbuf_vertex>
	#include <clipping_planes_vertex>
	vViewPosition = - mvPosition.xyz;
	#include <worldpos_vertex>
	#include <shadowmap_vertex>
	#include <fog_vertex>
#ifdef USE_TRANSMISSION
	vWorldPosition = worldPosition.xyz;
#endif
}
Copy the code

Analytical data

#include

represents the introduction of a library called begin_vertex

Do a Google search or Github Threejs global search for the source code to see what is introduced

A search will reveal that most of the library import addresses are recorded in ShaderChunk/shaderchunk.js

If you search for begin_vertex in ShaderChunk and you find the begin_vertex.glsl.js source file, what does it say

Achieve vertexShader

Early ideas

  • Looking at main(), you’ll see that the transformed variable records the coordinates of the last step, so you’ll see where it’s more convenient to process. Start with #include

  • Through the way of string split, split context shader. VertexShader. Split (‘ # include < begin_vertex > ‘), Use.join(‘#include

    + ‘) to join the top and bottom codes, inserting the #include

    back in.

The steps of code processing

  • First increment the time variable time in the loop
requestAnimationFrame( v= > {
    uniforms.time.value += 0.01;
})
Copy the code
  • Go back toshaderthrough+ = 5To view the changes that occur in the plane, to achieve the plane’s Z-axis offset.
transformed.z += 5;
Copy the code
  • Flag flapping depends on the distance relationship, using the sin() function to test the movement trajectory
transformed.z = sin(time);
Copy the code
  • You need a hand-drawn chart formula here

  • Sine operation using the y axis (formula: sin(time * frequency + x or y axis) * intensity)

transformed.z = sin(time * 2.0 + transformed.y) * 0.13;
Copy the code
  • Superposition X-axis sin operation (formula:Sin (time * frequency + x or y axis) * intensity)
transformed.z += sin(time * 1.0 + transformed.x) * 0.13;
Copy the code
  • Add noise to the rhythm of x + y axis, so that the rhythm is not so regular, need to reference noise in the head, some big gods written noise algorithm
Y * 5.0 + time * 2.8, x * 5.0, time * 1.3)) * 0.2;Copy the code
  • Finally, to fix the top, you need a attenuation formula, and you’ve got the red flag floating and floating
Z *= (position.y - 0.5)Copy the code
  • invertexShaderThe header defines a variablevarying float displace;.varyingType variables can be transferred directly tofragmentShader, is used to transmit the changes of z-axis. Light and shadow change according to z-axis. The bigger the light, the darker the light, and the smaller the light. Modify the above code.
Y) * (sin(time * 2.0 + Transformed. Y) * 0.13 + sin(time * 1.0 + transformed. X) * 0.13 + Y * 5.0 + time * 2.8, transform. X * 5.0, time * 1.3)) * 0.2); transformed.z = displace;Copy the code

VertexShader source

shader.vertexShader = ` uniform float time; varying float displace; Float mod289(float x){return x-floor (x * (1.0/289.0)) * 289.0; float mod289(float x){return x-floor (x * (1.0/289.0)) * 289.0; } vec4 mod289(vec4 x){return x-floor (x * (1.0/289.0)) * 289.0; } vec4 perm(vec4 x){return mod289(((x * 34.0) + 1.0) * x); } float noise(vec3 p){ vec3 a = floor(p); vec3 d = p - a; D = d * d * (3.0-2.0 * d); Vec4 b = A. xyy + VEC4 (0.0, 1.0, 0.0, 1.0); vec4 k1 = perm(b.xyxy); vec4 k2 = perm(k1.xyxy + b.zzww); vec4 c = k2 + a.zzzz; vec4 k3 = perm(c); Vec4 k4 = perm(c + 1.0); Vec4 o1 = fract(k3 * (1.0/41.0)); Vec4 O2 = fract(k4 * (1.0/41.0)); Vec4o3 = o2 * d.z + o1 * (1.0-d.z); vec4O3 = O2 * d.z + O1 * (1.0-d.z); Vec2o4 = O3.yw * d.x + O3.xz * (1.0-d.x); vec2o4 = O3.yw * d.x + O3.xz * (1.0-d.x); Return o4.y * d.y + o4.x * (1.0-d.y); } ` + shader.vertexShader;
    
shader.vertexShader = shader.vertexShader.split("#include <begin_vertex>").join('#include 
      
        displace = (position.y-0.5) * (sin(time * 2.0 + transformed. Y) * 0.13 + sin(time * 1.0 +) X) * 0.13 + noise(vec3(port.y * 5.0 + time * 2.8, port.x * 5.0, time * 1.3)) * 0.2); transformed.z = displace; `
      );
Copy the code

Implement fragmentShader

Early ideas

Make light and shadow changes according to the Z axis, the bigger the darker, the smaller the brighter, and not so dark that there is no light at all.

The steps of code processing

  • The header first references the variable displace
varying float displace;
Copy the code
  • First create an External variable to receive displaces. Note that displace can be negative, with a range of 0-1.
Float Brightness = Max (0.0, min(1.0, 0.5 + Displace * 2.0));Copy the code
  • The relationship between the intensity of light and dark, dark not all black. Impose a minimum limit of 0.07.
Brightness *= 0.8 + 0.07;Copy the code
  • When the intensity is changed, the difference between the values is more obvious, the small ones are smaller and the large ones are larger. Then assign to the native color variable.
Brightness += POw (Max (Displace, 0.0) * 3.0, 4.0) * 15.0; Gl_FragColor * = vec4 (vec3 (brightness), 1.0);Copy the code

FragmentShader source

shader.fragmentShader = ` #define STANDARD #ifdef PHYSICAL #define IOR #define SPECULAR #endif uniform vec3 diffuse; uniform vec3 emissive; uniform float roughness; uniform float metalness; uniform float opacity; varying float displace; #ifdef IOR uniform float ior; #endif #ifdef SPECULAR uniform float specularIntensity; uniform vec3 specularColor; #ifdef USE_SPECULARINTENSITYMAP uniform sampler2D specularIntensityMap; #endif #ifdef USE_SPECULARCOLORMAP uniform sampler2D specularColorMap; #endif #endif #ifdef USE_CLEARCOAT uniform float clearcoat; uniform float clearcoatRoughness; #endif #ifdef USE_SHEEN uniform vec3 sheenColor; uniform float sheenRoughness; #ifdef USE_SHEENCOLORMAP uniform sampler2D sheenColorMap; #endif #ifdef USE_SHEENROUGHNESSMAP uniform sampler2D sheenRoughnessMap; #endif #endif varying vec3 vViewPosition; #include <common> #include <packing> #include <dithering_pars_fragment> #include <color_pars_fragment> #include <uv_pars_fragment> #include <uv2_pars_fragment> #include <map_pars_fragment> #include <alphamap_pars_fragment> #include <alphatest_pars_fragment> #include <aomap_pars_fragment> #include <lightmap_pars_fragment> #include <emissivemap_pars_fragment> #include <bsdfs> #include <cube_uv_reflection_fragment> #include <envmap_common_pars_fragment> #include <envmap_physical_pars_fragment> #include <fog_pars_fragment> #include <lights_pars_begin> #include <normal_pars_fragment> #include <lights_physical_pars_fragment> #include <transmission_pars_fragment> #include <shadowmap_pars_fragment> #include <bumpmap_pars_fragment> #include <normalmap_pars_fragment> #include <clearcoat_pars_fragment> #include <roughnessmap_pars_fragment> #include <metalnessmap_pars_fragment> #include <logdepthbuf_pars_fragment> #include <clipping_planes_pars_fragment> void main() {  #include <clipping_planes_fragment> vec4 diffuseColor = vec4( diffuse, opacity ); ReflectedLight = ReflectedLight(VEC3 (0.0), VEC3 (0.0), VEC3 (0.0)); vec3 totalEmissiveRadiance = emissive; #include <logdepthbuf_fragment> #include <map_fragment> #include <color_fragment> #include <alphamap_fragment> #include <alphatest_fragment> #include <roughnessmap_fragment> #include <metalnessmap_fragment> #include <normal_fragment_begin> #include <normal_fragment_maps> #include <clearcoat_normal_fragment_begin> #include <clearcoat_normal_fragment_maps> #include <emissivemap_fragment> #include <lights_physical_fragment> #include <lights_fragment_begin> #include <lights_fragment_maps> #include <lights_fragment_end> #include <aomap_fragment> vec3 totalDiffuse = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse; vec3 totalSpecular = reflectedLight.directSpecular + reflectedLight.indirectSpecular; #include <transmission_fragment> vec3 outgoingLight = totalDiffuse + totalSpecular + totalEmissiveRadiance; #ifdef USE_SHEEN float sheenEnergyComp = 1.0-0.157 * max3(material. SheenColor); outgoingLight = outgoingLight * sheenEnergyComp + sheenSpecular; #endif #ifdef USE_CLEARCOAT float dotNVcc = saturate( dot( geometry.clearcoatNormal, geometry.viewDir ) ); vec3 Fcc = F_Schlick( material.clearcoatF0, material.clearcoatF90, dotNVcc ); OutgoingLight = outgoingLight * (1.0 - material. Clearcoat * Fcc) + clearcoatSpecular * Material. Clearcoat; #endif #include <output_fragment> float brightness = Max (0.0, min(1.0, 0.5 + displace * 2.0)) * 0.8 + 0.07; Brightness += POw (Max (Displace, 0.0) * 3.0, 4.0) * 15.0; Gl_FragColor * = vec4 (vec3 (brightness), 1.0); #include <tonemapping_fragment> #include <encodings_fragment> #include <fog_fragment> #include <premultiplied_alpha_fragment> #include <dithering_fragment> } `;Copy the code

Cite the link to 🔗

  • Whose algorithm
  • Threejs
  • ShaderChunk
  • ShaderChunk.js
  • begin_vertex.glsl.js
  • output_fragment.glsl.js
  • Mike Luan

Welcome to my Github ~ ~ ~ article where I started: Using Shader to implement flag waving