
📷 城市混合扫光
提示词
使用Three.js加载城市FBX模型,实现径向扫描效果,通过自定义着色器创建从中心向外扩散的彩色圆环扫描特效。
效果拆解
| 效果 | 实现方式 |
|---|---|
| 城市模型加载 | 使用FBXLoader加载城市模型并缩放定位 |
| 径向扫描 | 创建从中心向外扩散的圆环效果 |
| 颜色渐变 | 圆环颜色从起始色渐变到终止色 |
| 星空背景 | 使用粒子系统创建星空背景 |
| 循环扫描 | 圆环到达最大半径后重新开始 |
| 强度控制 | 通过intensity参数控制扫描亮度 |
核心技术点
1. 径向扫描着色器封装
JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
function attachRadialScanShader(model){
const materials = [];
model.traverse(child=>{
if(child.isMesh) materials.push(child.material);
});
const uniqueMats = [...new Set(materials)];
const uniforms = {
innerCircleWidth: { value: 480 },
circleWidth: { value: 160 },
circleMax: { value: 940 },
circleSpeed: { value: 1.5 },
diff: { value: new THREE.Color(0x6edbe8) },
color3: { value: new THREE.Color(0x1919f9) },
center: { value: new THREE.Vector3(-1,0,0) },
intensity: { value: 4.0 },
isDisCard: { value: false }
};
uniqueMats.forEach(mat=>{
mat.onBeforeCompile = shader=>{
Object.assign(shader.uniforms, uniforms);
shader.vertexShader = shader.vertexShader.replace(
'void main() {',
`
varying vec3 v_position;
void main(){
v_position = (modelMatrix * vec4(position,1.0)).xyz;
`
);
shader.fragmentShader = shader.fragmentShader.replace(
'#include <common>',
`
#include <common>
uniform float innerCircleWidth;
uniform float circleWidth;
uniform vec3 diff;
uniform vec3 color3;
uniform vec3 center;
uniform float intensity;
uniform bool isDisCard;
varying vec3 v_position;
`
);
shader.fragmentShader = shader.fragmentShader.replace(
'vec4 diffuseColor = vec4( diffuse, opacity );',
`
vec4 diffuseColor;
float dis = distance(v_position, center);
if(dis > innerCircleWidth && dis < innerCircleWidth + circleWidth){
float r = (dis - innerCircleWidth) / circleWidth;
#ifdef USE_MAP
vec3 tex = texture2D(map, vUv).rgb;
if(isDisCard && length(tex)<0.1) discard;
#endif
diffuseColor = vec4(mix(diff,color3,r) * intensity, opacity);
}else{
if(isDisCard) discard;
else diffuseColor = vec4(diffuse, opacity);
}
`
);
mat.needsUpdate = true;
};
});
model.render = () => {
if(uniforms.innerCircleWidth.value < uniforms.circleMax.value){
uniforms.innerCircleWidth.value += uniforms.circleSpeed.value;
}else{
uniforms.innerCircleWidth.value = 0;
}
};
}
2. 星空背景创建
JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
11
12
13
const starGeo = new THREE.BufferGeometry();
const starCount = 6000;
const starPos = [];
for(let i=0;i<starCount;i++){
starPos.push(
THREE.MathUtils.randFloatSpread(4000),
THREE.MathUtils.randFloatSpread(4000),
THREE.MathUtils.randFloatSpread(4000)
);
}
starGeo.setAttribute('position', new THREE.Float32BufferAttribute(starPos, 3));
const starMat = new THREE.PointsMaterial({color:0x00ffff, size:1.2, transparent:true, opacity:.6});
scene.add(new THREE.Points(starGeo, starMat));
3. 城市模型加载
JAVASCRIPT
1
2
3
4
5
6
7
8
9
10
let cityModel = null;
new FBXLoader().load(FILE_HOST + 'files/model/city.FBX', obj => {
scene.add(obj);
obj.scale.set(0.04, 0.04, 0.04);
obj.position.set(224, -9, -49);
cityModel = obj;
attachRadialScanShader(obj);
});
4. 渲染循环
JAVASCRIPT
1
2
3
4
5
6
7
function animate(){
requestAnimationFrame(animate);
controls.update();
cityModel && cityModel.render && cityModel.render();
renderer.render(scene, camera);
}
animate();
调试技巧
- 扫描速度:调整circleSpeed参数控制扫描速度
- 圆环宽度:修改circleWidth参数改变圆环带宽
- 颜色渐变:调整diff和color3参数改变渐变颜色
- 扫描中心:修改center参数改变扫描圆心位置
- 亮度强度:调整intensity参数控制扫描亮度
扩展思路
- 多圆环扫描:创建多个不同颜色的扫描圆环
- 交互控制:添加鼠标交互改变扫描中心
- 音频响应:根据音频频率调整扫描速度和颜色
- 3D扫描:实现球面或锥形扫描效果
- 数据可视化:在扫描圆环上显示数据信息
- 动态纹理:使用动态纹理增强扫描效果








京公网安备 11010502038735号
💬 评论区
评论功能即将上线,敬请期待!