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 format
name: {value: 'value'}
var uniforms = {
time: { value: 1.0}}Copy the code
- Based on my partial understanding,
vertexShader
To deal with coordinates,fragmentShader
It 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.vertexShader
What 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 to
shader
through+ = 5
To 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
- in
vertexShader
The header defines a variablevarying float displace;
.varying
Type 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