preface
The actual effect of this article can be seen in auroral Au Design official website, here by the way, Au Design has been officially launched!! Don’t forget to give a thumbs-up if you think it’s good.
Au Design
AU Design is a Design language, Jiguang Experience Design (JED for short) is a comprehensive Experience Design team, specializing in interaction Design, visual Design, effect advertising Design, etc., responsible for the creativity and Experience Design of the whole line of aurora products. Enabling business through experience design. JED adheres To the concept of extreme design and is committed To building a first-class To B comprehensive experience design team.
So with all this vertex shaders, slice shaders and all this stuff, it’s time for the fun part. To create a full 60fps dazzle motion model switching effect. There are both CPU rendering and GPU rendering implementations of this effect, but with too many vertices, the performance of rendering in CUP will suffer.
Analysis of the characteristics of
The effect is a combination of the following transformations
- Particle smooth displacement transformation
- The particle size increases or decreases smoothly
- The transparency of particles decreases as the Z-axis becomes smaller
- Glow aftertreatment
Let’s take a look at what the structure of the plug-in class looks like, without going into detail about shaders, models, parameter passing and so on. It’s a little difficult to understand, but you’re curious about how to do it, so you can read the first two basic articles. The Distance between the Front End and the GPU
structure
First, the plug-in class inherits and encapsulates the utility classes described in the last two articles, and then implements the functionality step by step.
// ModelControl.ts
export default class ModelControl extends ThreeTool{
// Model list
public modelList: Array<IModel> = [];
// Record the current model
public currentModelIndex = 0;
/ /...
constructor(){
super({
canvas: document.getElementById("canvasFrame") as HTMLCanvasElement,
container: document.getElementById("canvasWrap") as HTMLCanvasElement,
mode: "dev".clearColor: new THREE.Color("# 000")}); }/ /...
// Generate a particle material
private createMainMaterial(){}
// Generate the default geometry for later saving particle/vertex position coordinates
private createInitGeometry(){}
// Generate the cache model
private createInitModel(){}
// Switch the current model to the specified model in the model list
public changeModelByIndex(){}
// Switch the model automatically
public autoPlay(){}
// Load the custom model
public async loaderModel(){}
/ /...
}
Copy the code
With the help of ThreeTool, we don’t need to worry about cameras, lights, sizes, etc.
Implementation details
- Since this is a model toggle effect, only one model can exist at any given time.
- The model holds the current vertex coordinates
position
And the target model target to which you need to switchtargetPosition
, which can achieve the effect of model switching. - Maintains the state of particles in shaders, rendered using the GPU.
- Use a list to save the models for easy cycling.
- Use Tweenjs to generate continuous time segments.
The particle motion
If you imagine A point moving from A to B in time T, how would that be represented? So just to think about it, you’re going to go from A to B A little bit at every interval, and you’re going to get to the B coordinate. It’s the same thing in three dimensions, except you decompose the coordinates of the points into three dimensions xyz.
Again, there are two ways to do this
- The first intuitive way to do this is to use
The total time T
Between two pointsDistance D
calculateRate of S
Each frame is addedS*time
So much distance. - The second is to do a linear transformation between the AB coordinates, where each frame is located
xA + (xB - xA) * val
, includingval
The value of is in the range of[0, 1]
Between, thisval
You can think of it as the rate of change. Because time is linear, the effect of using this directly is a linear transformation. If the linear transformation is too rigid, you can adjust itval
To make a variety of easing effects.
For example, you can use xA + (xB-XA) * pow(val,2) to create a slow and fast effect
The two solutions are essentially the same, but the second is more concise. Let’s take a look at the CPU and GPU rendering scheme written with the second solution.
CPU render
render((time) = >{
const val = (time * 0.0001) % 1;
const x = xA + (xB - xA) * val;
const y = yA + (yB - yA) * val;
const z = zA + (zB - zA) * val;
point.position.set(x,y,z);
})
Copy the code
The GPU to render
uniform float uTime;
attribute vec3 targetPosition;
void main() {
vec3 cPosition;
cPosition.x = position.x + (targetPosition.x - position.x) * uTime;
cPosition.y = position.y + (targetPosition.y - position.y) * uTime;
cPosition.z = position.z + (targetPosition.z - position.z) * uTime;
gl_PointSize = 2.
gl_Position = projectionMatrix * modelViewMatrix * vec4(cPosition, 1.0);
}
Copy the code
Here a smooth motion can be achieved by using a continuous time change
Random flashing
Since each particle/vertex has a different spatial coordinate, you can use their spatial coordinates to generate the initial state, such as cposition.x * cposition.y * cposition.z.
The particle size
The smooth change of particle size is achieved with the help of the sine/cosine function, which is actually modulated into a waveform like the one below.
gl_PointSize = (sin(cPosition.x*cPosition.y*cPosition.z+uTime)+1.) *2.;
Copy the code
Particle gradient
Same idea as particle size change, just a little tweaked, the transparency needs to be reduced as the Z-axis gets smaller.
float opacity = ((vZIndex+150.) /300.) - sin(curPos.z*curPos.x*curPos.y+uTime) + 0.3;
Copy the code
Dazzle light effects
The flash effect uses post-processing technology. The simple thing to do here is to use Threejs’ built-in flash shader, LuminosityHighPassShader, which can be found in the Shaders directory. It’s ok to write your own flash shader, but there’s no need to reinvent the wheel.
// UnrealBloomPass parameter
// resolution: The size of the scene covered by the light
// Strength: strength of light
// radius: the radius of the light emission
// threshold: the threshold for the light to dazzle (if the light in the scene is stronger than this value, the effect will be dazzle)
// See the first article on Wrapping the Threejs utility class for details on the render function.
public bloomRender(){
const renderScene = new RenderPass(this.scene, this.camera);
// Create a channel
const bloomPass = new UnrealBloomPass(
new THREE.Vector2(window.innerWidth, window.innerHeight),
1.5.0.5.0.2
);
bloomPass.renderToScreen = true;
bloomPass.strength = 1.5;
bloomPass.radius = 0.5;
bloomPass.threshold = 0.2;
const composer = new EffectComposer(this.renderer);
composer.setSize(window.innerWidth, window.innerHeight);
composer.addPass(renderScene);
// The channel bloomPass is inserted into the composer
composer.addPass(bloomPass);
const render = (time: number) = > {
if (this.resizeRendererToDisplaySize(this.renderer)) {
const canvas = this.renderer.domElement;
this.css2drenderer.setSize(canvas.clientWidth, canvas.clientHeight);
this.camera.aspect = canvas.clientWidth / canvas.clientHeight;
this.camera.updateProjectionMatrix();
}
this.css2drenderer.render(this.scene, this.camera);
composer.render();
const t = time * 0.001;
requestAnimationFrame(render);
};
render(0);
};
Copy the code
The model is loaded
The model is loaded into the model list and the model with the most vertices is counted.
public loaderModel(geoPList: Array<Promise<IModel>>) { const modelList = await Promise.all(geoPList); this.modelList = modelList; const maxCount = Math.max( ... modelList.map((item) => item.attributes.position.count) ); this.positionCache = new Float32Array(maxCount * 3); return modelList; }Copy the code
Model switching
Change the model according to the subscript of the model list. The vertex coordinates of the target model are overwritten into the targetPosition of the cache model. After the passing time change this.tween.start(), the vertex coordinates of the particle model will change from the current coordinate position to targetPosition.
public changeModelByIndex(current: number){
this.currentModelIndex = current;
const originModel = this.originModel;
const targetModel = this.modelList[current];
const targetPosition = targetModel.attributes.position.array;
const positionCache = this.positionCache;
// The target coordinate of the last switch overwrites the current coordinate
if (originModel.geometry.attributes.targetPosition) {
const position = new Float32Array(
originModel.geometry.attributes.targetPosition.array
);
originModel.geometry.setAttribute(
"position".new THREE.BufferAttribute(position, 3)); originModel.material.uniforms.uVal.value =0;
}
// Override the coordinates of the target model
for (let i = 0, j = 0; i < positionCache.length; i++, j++) {
j %= targetPosition.length;
positionCache[i] = targetPosition[j];
}
originModel.geometry.setAttribute(
"targetPosition".new THREE.BufferAttribute(positionCache, 3));// Generate time changes
this.tween.start();
this.tween.onComplete(() = > {
this.currentVal.uVal = 0;
});
return originModel;
};
Copy the code
Automatically play
Set the timer to automatically call the model switch method.
public autoPlay(time: number = 8000, current? :number){
if(current ! = =undefined) {
this.currentModelIndex = current;
}
const timer = setInterval(() = > {
this.changeModelByIndex(this.currentModelIndex);
this.currentModelIndex =
(this.currentModelIndex + 1) % this.modelList.length;
}, time);
this.timer = timer;
return timer;
};
Copy the code
The end of the
So far, a cool particle effect transform plugin is done. The next article will look at the effects that can already be achieved using the various shader functions: Using shader built-in functions and related effects.
The source code
Original is not easy to reprint, please contact the author. This article involves the source code is still on the road, the author of the like is open source code and continue to update the power. The source code and Demo of the tool class were given in the previous article “The Distance between the front end and the GPU” on shaders.
Ready to update the series
- On sorting and Encapsulating The Threejs Tool class
- The Distance between front End and GPU
- “On using Shader built-in Functions and related effects” (in draft)
- Various functions and special effects
- About Shader Lighting effects (in draft)
- Flat shading
- Per-vertex shading (Gouraud shading)
- Per-pixel shading (Phong shading)
- BlinnPhong lighting model
- Fresnel effect
- Cartoon shaders
- “On local Coordinates, World Coordinates, Projection Coordinates” (in draft)
- Local coordinates
- The world coordinates
- The projection coordinate
- Matrix transformation
- On Github homepage Earth Effects
- About D3js
- On Visualizing a Data Diagram
- “On writing a Hop hop game.”
- Scenario generation
- Collision detection
- The game logic