隐形力量:Phantom.land交互式网格与3D面部粒子系统的构建
Phantom.land的动态网格和体积面部扫描,采用React Three Fiber、GLSL着色器和GSAP精心打造。
技术选型
我们从Next/React基础开始,利用React Three Fiber库无缝连接DOM组件和全站使用的WebGL上下文。样式方面采用自定义CSS组件和SASS。对于交互行为和动画,选择GSAP因其包含SplitText、CustomEase和ScrollTrigger等插件,且能在DOM和WebGL组件间使用统一的动画框架。
首页网格
网格视图通过将原始Three.js对象集成到React Three Fiber场景中实现:
1
2
3
4
5
6
7
8
9
10
|
// GridView.tsx
const GridView = () => {
return (
<Canvas>
...
<ProjectsGrid />
<Postprocessing />
</Canvas>
);
}
|
由于组件复杂度,最终选择使用原生Three.js类而非React Three Fiber编写全部代码。
后处理失真效果
通过后处理管道中的自定义着色器通道实现标志性失真效果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
// Postprocessing.tsx
const Postprocessing = () => {
const {gl, scene, camera} = useThree();
const {effectComposer, distortionShader} = useMemo(() => {
const renderPass = new RenderPass(scene, camera);
const distortionShader = new DistortionShader();
const distortionPass = new ShaderPass(distortionShader);
const outputPass = new OutputPass();
const effectComposer = new EffectComposer(gl);
effectComposer.addPass(renderPass);
effectComposer.addPass(distortionPass);
effectComposer.addPass(outputPass);
return {effectComposer, distortionShader};
}, []);
}
|
失真强度通过DistortionShader类中的简单补间动画进行变化:
1
2
3
4
5
6
7
8
9
10
|
class DistortionShader extends ShaderMaterial {
setDistortion(value: number) {
gsap.to(this, {
distortionIntensity: value,
duration: 1,
ease: 'power2.out',
onUpdate: () => this.update()
}
}
}
|
失真通过自定义着色器应用:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// fragment.ts
export const fragmentShader = /* glsl */ `
uniform sampler2D tDiffuse;
uniform vec2 distortion;
void main() {
vec2 shiftedUv = getShiftedUv(vUv);
shiftedUv *= (0.88 + distortion * dot(shiftedUv));
vec2 transformedUv = getUnshiftedUv(shiftedUv);
color = texture2D(tDiffuse, distortedUV).rgb;
gl_FragColor = vec4(color, 1.);
}
|
1
2
3
4
5
6
7
8
9
10
11
|
### 微交互设计
**环境鼠标偏移**:计算鼠标在网格上的位置并相应移动网格网格体:
```typescript
getAmbientCursorOffset() {
const uv = this.navigation.pointerUv;
const offset = uv.subScalar(0.5).multiplyScalar(0.2);
return offset;
}
|
拖拽缩放:检测用户开始和停止拖拽时触发GSAP动画:
1
2
3
4
5
6
7
|
animateCameraZ(distance: number, duration: number) {
gsap.to(this.camera.position, {
z: distance,
duration,
ease: CustomEase.create('cameraZoom', '.23,1,0.32,1'),
});
}
|
拖拽移动:用户拖拽释放后网格带有惯性滑动:
1
2
3
4
|
drag(offset: Vector2) {
this.dragAction = offset;
this.velocity.lerp(offset, 0.8);
}
|
面部粒子系统
员工面部轮播通过动态3D粒子系统呈现,使用React Three Fiber的BufferGeometry和自定义GLSL着色器构建。
核心概念:深度驱动的粒子生成
将2D图像转换为体积3D表示,每个面部仅使用两个优化的256×256 WebP图像(各小于15KB)。
通过RealityScan进行3D扫描,在Cinema4D中渲染位置和颜色通道,在Photoshop中转换为灰度深度图。
每个面部由约78,400个粒子(280×280网格)构成:
1
2
3
4
5
6
|
const POINT_AMOUNT = 280;
const points = useMemo(() => {
const length = POINT_AMOUNT * POINT_AMOUNT;
const vPositions = new Float32Array(length * 3);
// ...位置属性数组生成
}, []);
|
动态粒子缩放
通过颜色数据分析影响粒子大小,较亮区域生成更大粒子:
1
2
3
4
|
/* vertex shader */
vec3 colorTexture1 = texture2D(colorMap1, vIndex.xy).xyz;
float density = (mainColorTexture.x + mainColorTexture.y + mainColorTexture.z) / 3.;
float pScale = mix(pScaleMin, pScaleMax, density);
|
环境噪声动画
使用卷曲噪声创建细微流动运动:
1
2
|
/* vertex shader */
pos += curlNoise(pos * curlFreq1 + time) * noiseScale * 0.1;
|
面部过渡动画
使用GSAP时间轴动画化多个着色器参数:
1
2
3
4
|
timelineRef.current = gsap
.timeline()
.fromTo(uniforms.transition, {value: 0}, {value: 1.3, duration: 1.6})
.to(uniforms.posZ, {value: particlesParams.offset_z, duration: 1.6}, 0);
|
着色器处理两个面部状态间的视觉混合:
1
2
3
|
/* vertex shader */
vec3 mainColorTexture = mix(colorTexture1, colorTexture2, speed);
vec3 depthValue = mix(depthTexture1, depthTexture2, speed);
|
自定义景深效果
在着色器材质中实现自定义景深效果:
1
2
3
4
5
6
7
8
|
/* vertex shader */
vec4 viewPosition = viewMatrix * modelPosition;
vDistance = abs(focus + viewPosition.z);
gl_PointSize = pointSize * pScale * vDistance * blur * totalScale;
/* fragment shader */
float alpha = (1.04 - clamp(vDistance * 1.5, 0.0, 1.0));
gl_FragColor = vec4(color, alpha);
|
挑战:统一面部比例
需要为每个面部手动调整多个参数:
1
2
3
4
5
|
particle_params: {
offset_z: 0, // 整体Z位置
z_depth_scale: 0, // 深度图缩放因子
face_size: 0, // 整体面部比例
}
|
结语
我们的面部粒子系统展示了如何通过简单而谨慎的技术实现,从最小资源创建有趣的视觉体验。通过结合轻量级WebP纹理、自定义着色器材料和动画,我们创建了一个将简单2D肖像转换为交互式3D图形的系统。