平面扫描效果

🎯 提示词

PROMPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
使用 Three.js 实现【平面扫描效果】,具体要求:

【核心特效】
- 圆形扫描动画
- 透明度渐变效果
- 纹理遮罩处理

【场景与光照】
- 深灰色背景
- 纹理贴图应用
- 平面几何体

【交互与控制】
- OrbitControls 视角控制
- 扫描速度可调

【技术要求】
- Three.js 版本
- onBeforeCompile 着色器修改
- TextureLoader 纹理加载

📖 效果拆解

层次 视觉效果 技术实现
基础 平面几何体 PlaneGeometry
核心特效 圆形扫描动画 onBeforeCompile 修改
增强细节 纹理遮罩 透明度检测 discard
动画效果 扫描线移动 时间驱动的半径变化

🔧 核心技术点

1. onBeforeCompile 修改

在现有材质上动态修改着色器:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
material.onBeforeCompile = (shader) => {
    // 添加自定义 uniforms
    Object.keys(uniforms).forEach((key) => shader.uniforms[key] = uniforms[key]);
    
    // 修改顶点着色器
    shader.vertexShader = shader.vertexShader.replace(`void main() {`,
        `varying vec2 vUv;
    varying vec3 v_position;
    void main() {
        vUv = uv;
        v_position = position;`
    );
    
    // 修改片段着色器
    shader.fragmentShader = shader.fragmentShader.replace(
        /#include <common>/,
        Object.keys(uniforms).map(i => 'uniform ' + uniforms[i].unit + ' ' + i + ';').join('\n') + 
        '\n' + 'varying vec3 v_position; varying vec2 vUv;\n' + '\n#include <common>\n'
    );
}

2. 扫描效果实现

在片段着色器中实现圆形扫描:

GLSL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
float dis = length(v_position - center);
vec4 diffuseColor;

if(dis < (innerCircleWidth + circleWidth) && dis > innerCircleWidth) {
    float r = (dis - innerCircleWidth) / circleWidth;
    float cOpacity = reverseOpacity ? (innerCircleWidth / circleMax) : 1. - (innerCircleWidth / circleMax);
    
    #ifdef USE_MAP
        vec3 textureColor = texture2D(map, vUv).rgb;
        if(isDisCard && textureColor.r < 0.1 && textureColor.g < 0.1 && textureColor.b < 0.1) discard;
    #endif
    
    diffuseColor = vec4(mix(diff, color3, r) * vec3(intensity, intensity, intensity), 
                        opacity * cOpacity * opacityScale);
} else {
    if(isDisCard) discard;
    else diffuseColor = vec4(diffuse, opacity);
}

3. 动态扫描控制

在动画循环中更新扫描半径:

JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
function animate() {
    // 扫描半径循环变化
    uniforms.innerCircleWidth.value < uniforms.circleMax.value 
        ? uniforms.innerCircleWidth.value += uniforms.circleSpeed.value 
        : uniforms.innerCircleWidth.value = 0;
    
    requestAnimationFrame(animate);
    controls.update();
    renderer.render(scene, camera);
}

💡 调试与优化

问题类型 表现形式 解决方案
着色器替换失败 材质无变化 打印 shader 源码确认替换锚点
纹理加载失败 纹理不显示 检查纹理路径和跨域设置
透明度异常 扫描区域不透明 调整 opacityScale 参数
动画卡顿 帧率下降 减少 uniforms 更新频率

🚀 扩展思路

变体效果 核心改动 难度
多色扫描 添加多种颜色过渡 ⭐⭐
椭圆扫描 修改扫描形状为椭圆 ⭐⭐
交互控制 鼠标控制扫描位置 ⭐⭐
发光边缘 添加扫描线发光效果 ⭐⭐⭐

本文档由 ThreeLab 编辑整理,如需转载,请注明出处。