Advanced Three.js Hero Section with Shaders, Particles, Lighting, and UI
This tutorial walks through building an advanced hero section using Three.js, covering prerequisites, loading and animating a robot model, configuring lighting, creating particle effects, adding UI elements, applying a radial gradient background, implementing parallax interaction, and rendering flowing lines for a polished WebGL experience.
Before diving into advanced Three.js and Shader (GLSL) topics, ensure you are familiar with core Three.js concepts such as Scene, Camera, Renderer, Geometry, Material, and Mesh, and have a solid grasp of GLSL syntax for vertex and fragment shaders.
0. Prerequisites
Familiarity with Three.js basics and GLSL shader programming is required. Recommended resources include the Three.js journey course by Bruno Simon and various 3D model repositories.
1. Hero Section Overview
The hero section provides an immediate visual impact, often featuring a static robot that gently oscillates, surrounded by particles of varying sizes and a sweeping line across the screen.
2. Basic Scene Setup
Set up the scene by creating a static robot model, adding three particle size groups, and a line that moves from left to right.
2.1 Basic Scene Construction
Refer to previous articles for detailed scene setup or use AI tools to reproduce the layout.
2.2 Robot Resource Acquisition
Obtain 3D robot assets from sites like MeshyAI, Hyper3D, or Tencent Hunyuan, then animate them using Mixamo by uploading an FBX or OBJ file.
this.loader = new GLTFLoader();
this.loader.load('path/to/robot.glb', (gltf) => {
this.model = gltf.scene;
// Adjust model size
this.model.scale.set(0.02, 0.02, 0.02);
// Set model position
this.model.position.set(0, -2.81, 0);
// Add model to scene
this.scene.add(this.model);
});2.3 Loading the Robot Model
Use GLTFLoader to load robot.glb , then adjust its scale and position to fit the hero layout.
Step 2: Play Robot Animation
Configure an idle animation using AnimationMixer and play the first available clip.
// Set up animation mixer
this.animation = {
mixer: new THREE.AnimationMixer(this.model),
actions: {},
animations: this.resources.items.sintRobot.animations
};
// Create actions for each clip
this.animation.animations.forEach(clip => {
this.animation.actions[clip.name] = this.animation.mixer.clipAction(clip);
});
// Play the first animation
if (this.animation.animations.length > 0) {
this.currentAction = this.animation.actions[this.animation.animations[0].name];
this.currentAction.play();
}Step 3: Update Animation Frames
Update the mixer inside requestAnimationFrame to ensure smooth playback.
if (this.animation.mixer) {
this.animation.mixer.update(this.time.delta);
}2.4 Lighting Setup
Three directional lights and an ambient light are added to enhance depth and realism.
// Directional Light 1
this.directionalLight1 = new THREE.DirectionalLight();
this.directionalLight1.position.set(1, 2, 2);
this.directionalLight1.color.setRGB(0.13, 0.09, 0.15);
this.directionalLight1.intensity = 3;
this.scene.add(this.directionalLight1);
// Directional Light 2
this.directionalLight2 = new THREE.DirectionalLight();
this.directionalLight2.position.set(-1, 3, 1);
this.directionalLight2.color.setRGB(0.45, 0.36, 0.22);
this.directionalLight2.intensity = 4.1;
this.scene.add(this.directionalLight2);
// Ambient Light
this.ambientLight = new THREE.AmbientLight();
this.ambientLight.color.setRGB(0.309, 0, 0.964);
this.ambientLight.intensity = 2;
this.scene.add(this.ambientLight);3. Particle Effects
Create two sampling planes at z = 0.5 and z = -0.5, then generate particles using MeshSurfaceSampler to avoid intersecting the robot.
// Create sampling planes
const planeGeometry = new THREE.PlaneGeometry(2 * this.sizes.aspect, 2, 1, 1);
const planeMaterial = new THREE.MeshBasicMaterial({ color: 0xFFFFFF, wireframe: true });
this.plane = new THREE.Mesh(planeGeometry, planeMaterial);
const clonedPlane = this.plane.clone();
clonedPlane.position.set(0, 0, -0.5);
this.scene.add(clonedPlane);
this.plane.position.set(0, 0, 0.5);
this.scene.add(this.plane);
this.plane.updateMatrixWorld();
clonedPlane.updateMatrixWorld();
// Initialize particle system with the two planes
this.geometryParticles = new ParticleSystem([this.plane, clonedPlane]);
this.plane.visible = false;
clonedPlane.visible = false;Sample particle positions, noises, speeds, and sizes, then render them with a ShaderMaterial that manipulates vertex positions over time.
4. UI Integration
Add UI components on top of the canvas and set the Three.js renderer to use a transparent background.
this.instance = new THREE.WebGLRenderer({
canvas: this.canvas,
antialias: true,
alpha: true // Enable transparent background
});
this.instance.setClearColor(0x000000, 0); // Fully transparentApply a radial gradient to the page background via CSS to enhance visual depth.
5. Parallax Effect
Capture normalized mouse coordinates and smoothly interpolate the camera's lookAt target to create a subtle parallax interaction.
window.addEventListener('mousemove', (event) => {
this.normalizedMouse.x = (event.clientX / window.innerWidth) * 2 - 1;
this.normalizedMouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
});
const lookAtTarget = new THREE.Vector3(
this.scene.position.x + this.normalizedMouse.x * this.parallax.factor * 0.25,
this.scene.position.y + this.normalizedMouse.y * this.parallax.factor * 0.15,
this.scene.position.z
);
if (!this.currentLookAtTarget) this.currentLookAtTarget = lookAtTarget.clone();
this.currentLookAtTarget.lerp(lookAtTarget, 0.07);
this.camera.lookAt(this.currentLookAtTarget);6. Flowing Lines
Generate a curve, sample points, assign per‑vertex alpha values, and render with a custom ShaderMaterial that uses additive blending for a glowing line effect.
// Create line geometry from curve points
this.lineGeometry = new THREE.BufferGeometry().setFromPoints(this.curvePoints);
// Add alpha attribute for fading
const alphas = new Float32Array(this.curvePoints.length);
for (let i = 0; i < this.curvePoints.length; i++) {
const t = i / (this.curvePoints.length - 1);
alphas[i] = Math.sin(t * Math.PI) * this.parameters.lineOpacity;
}
this.lineGeometry.setAttribute('alpha', new THREE.BufferAttribute(alphas, 1));
// Shader material handling per‑vertex alpha
this.lineMaterial = new THREE.ShaderMaterial({
vertexShader: `
attribute float alpha;
varying float vAlpha;
void main() {
vAlpha = alpha;
gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
`,
fragmentShader: `
uniform vec3 color;
varying float vAlpha;
void main() {
gl_FragColor = vec4(color, vAlpha);
}
`,
uniforms: { color: { value: new THREE.Color(this.parameters.lineColor) } },
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});7. Final Thoughts
With AI lowering the barrier to 3D asset creation, front‑end developers will increasingly incorporate sophisticated WebGL experiences. Three.js remains a top choice for WebGL development due to its ease of use, extensive features, and strong community support.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.