ECharts GL 3D曲面图完全指南
文档类型: 深度技术文档
难度等级: ⭐⭐⭐⭐⭐
源码版本: ECharts-GL 2.x
本文行数: 约580行
📋 目录
🎯 surface曲面图基础
基础3D曲面
typescript
import * as echarts from 'echarts';
import 'echarts-gl';
const option = {
tooltip: {},
visualMap: {
show: false,
dimension: 2,
min: -1,
max: 1,
inRange: {
color: ['#313695', '#4575b4', '#74add1', '#abd9e9', '#e0f3f8', '#ffffbf', '#fee090', '#fdae61', '#f46d43', '#d73027', '#a50026']
}
},
xAxis3D: {
type: 'value',
name: 'X'
},
yAxis3D: {
type: 'value',
name: 'Y'
},
zAxis3D: {
type: 'value',
name: 'Z'
},
grid3D: {
boxWidth: 200,
boxDepth: 200,
axisLine: {
lineStyle: { color: '#fff' }
},
axisPointer: {
lineStyle: { color: '#fff' }
},
viewControl: {
distance: 300,
alpha: 30,
beta: 45
},
light: {
main: {
intensity: 1.2,
shadow: true
},
ambient: {
intensity: 0.3
}
}
},
series: [{
type: 'surface',
data: generateSurfaceData(),
wireframe: {
show: false
},
itemStyle: {
opacity: 0.9
},
shading: 'lambert'
}]
};
chart.setOption(option);
// 生成曲面数据
function generateSurfaceData(): number[][] {
const data: number[][] = [];
for (let x = -10; x <= 10; x += 0.5) {
for (let y = -10; y <= 10; y += 0.5) {
// z = sin(sqrt(x^2 + y^2)) / sqrt(x^2 + y^2)
const r = Math.sqrt(x * x + y * y);
const z = r === 0 ? 1 : Math.sin(r) / r;
data.push([x, y, z]);
}
}
return data;
}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
79
80
81
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
79
80
81
📐 数学函数可视化
1. 抛物面 (Paraboloid)
typescript
function generateParaboloid(): number[][] {
const data: number[][] = [];
for (let x = -5; x <= 5; x += 0.3) {
for (let y = -5; y <= 5; y += 0.3) {
// z = x^2 + y^2
const z = x * x + y * y;
data.push([x, y, z]);
}
}
return data;
}
const paraboloidOption = {
series: [{
type: 'surface',
data: generateParaboloid(),
shading: 'realistic',
realisticMaterial: {
roughness: 0.5,
metalness: 0.2
},
wireframe: {
show: true,
lineStyle: {
color: '#fff',
opacity: 0.1
}
}
}]
};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
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
2. 双曲抛物面 (马鞍面)
typescript
function generateHyperbolicParaboloid(): number[][] {
const data: number[][] = [];
for (let x = -5; x <= 5; x += 0.3) {
for (let y = -5; y <= 5; y += 0.3) {
// z = x^2 - y^2
const z = x * x - y * y;
data.push([x, y, z]);
}
}
return data;
}1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
3. 球面 (Sphere)
typescript
function generateSphere(radius: number = 1): number[][] {
const data: number[][] = [];
const steps = 50;
for (let i = 0; i <= steps; i++) {
const theta = (i / steps) * Math.PI; // [0, π]
for (let j = 0; j <= steps; j++) {
const phi = (j / steps) * 2 * Math.PI; // [0, 2π]
// 球坐标转直角坐标
const x = radius * Math.sin(theta) * Math.cos(phi);
const y = radius * Math.sin(theta) * Math.sin(phi);
const z = radius * Math.cos(theta);
data.push([x, y, z]);
}
}
return data;
}
const sphereOption = {
series: [{
type: 'surface',
data: generateSphere(5),
shading: 'realistic',
realisticMaterial: {
roughness: 0.3,
metalness: 0.8,
detail: true
},
environment: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
{ offset: 0, color: '#00aaff' },
{ offset: 1, color: '#000033' }
]),
light: {
main: {
intensity: 2,
color: '#fff',
shadow: true,
shadowQuality: 'high'
},
ambient: {
intensity: 0.4
},
ambientCubemap: {
texture: 'path/to/env.hdr',
exposure: 1,
diffuseIntensity: 0.5,
specularIntensity: 2
}
}
}]
};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
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
4. 螺旋面 (Helicoid)
typescript
function generateHelicoid(): number[][] {
const data: number[][] = [];
for (let u = 0; u <= 4 * Math.PI; u += 0.2) {
for (let v = -2; v <= 2; v += 0.2) {
// 参数方程
const x = v * Math.cos(u);
const y = v * Math.sin(u);
const z = u;
data.push([x, y, z]);
}
}
return data;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
🏔️ 地形数据渲染
从高度图生成立体地形
typescript
class TerrainRenderer {
private chart: echarts.ECharts;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
}
/**
* 从高度图数据渲染地形
*/
renderTerrain(heightmap: number[][], options?: any) {
const terrainData = this.convertHeightmapToTerrain(heightmap);
const option = {
tooltip: {
formatter: (params: any) => {
const [x, y, z] = params.value;
return `经度: ${x.toFixed(2)}<br/>纬度: ${y.toFixed(2)}<br/>海拔: ${z.toFixed(0)}m`;
}
},
visualMap: {
show: true,
calculable: true,
dimension: 2,
min: 0,
max: Math.max(...terrainData.map(d => d[2])),
inRange: {
color: [
'#0066FF', // 深海
'#0099FF', // 浅海
'#66CC66', // 平原
'#99CC33', // 丘陵
'#CCCC66', // 山地
'#FFFFFF' // 雪山
]
}
},
grid3D: {
boxWidth: 300,
boxDepth: 300,
boxHeight: 100,
viewControl: {
distance: 400,
alpha: 40,
beta: 30,
minDistance: 200,
maxDistance: 800
},
environment: '#000',
postEffect: {
enable: true,
SSAO: {
enable: true,
radius: 3,
intensity: 1.5
}
}
},
xAxis3D: {
type: 'value',
name: '经度'
},
yAxis3D: {
type: 'value',
name: '纬度'
},
zAxis3D: {
type: 'value',
name: '海拔(m)',
axisLabel: {
formatter: '{value}m'
}
},
series: [{
type: 'surface',
data: terrainData,
shading: 'realistic',
realisticMaterial: {
roughness: 0.8,
metalness: 0.1,
detail: true
},
wireframe: {
show: false
},
silent: false
}]
};
this.chart.setOption(option);
}
/**
* 将高度图转换为3D数据
*/
private convertHeightmapToTerrain(heightmap: number[][]): number[][] {
const data: number[][] = [];
const rows = heightmap.length;
const cols = heightmap[0].length;
for (let y = 0; y < rows; y++) {
for (let x = 0; x < cols; x++) {
const height = heightmap[y][x];
data.push([x, y, height]);
}
}
return data;
}
/**
* 加载真实地形数据 (示例)
*/
async loadRealTerrain(url: string) {
const response = await fetch(url);
const heightmap = await response.json();
this.renderTerrain(heightmap);
}
dispose() {
this.chart.dispose();
}
}
// 使用
const terrain = new TerrainRenderer(document.getElementById('chart')!);
// 模拟高度图数据
const mockHeightmap = Array.from({ length: 100 }, (_, y) =>
Array.from({ length: 100 }, (_, x) => {
// 使用Perlin噪声或Simplex噪声生成更真实的地形
return Math.sin(x * 0.1) * Math.cos(y * 0.1) * 100 +
Math.sin(x * 0.3 + y * 0.2) * 50 +
200;
})
);
terrain.renderTerrain(mockHeightmap);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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
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
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
等高线叠加
typescript
const contourOption = {
series: [
{
type: 'surface',
data: terrainData,
shading: 'lambert',
// 等高线
contour: {
show: true,
highlight: {
show: true,
lineWidth: 2,
color: '#FF0000'
}
}
}
]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
✨ 高级特效与优化
1. 半透明曲面
typescript
const transparentOption = {
series: [{
type: 'surface',
data: surfaceData,
shading: 'lambert',
itemStyle: {
opacity: 0.6,
color: 'rgba(84, 112, 198, 0.6)'
},
// 双面渲染
backfaceCulling: false
}]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
2. 动态波浪效果
typescript
class AnimatedWave {
private chart: echarts.ECharts;
private time: number = 0;
private animationId: number | null = null;
constructor(container: HTMLElement) {
this.chart = echarts.init(container);
this.animate();
}
private animate() {
this.time += 0.05;
const data = this.generateWaveData();
this.chart.setOption({
series: [{
data: data
}]
});
this.animationId = requestAnimationFrame(() => this.animate());
}
private generateWaveData(): number[][] {
const data: number[][] = [];
for (let x = -10; x <= 10; x += 0.5) {
for (let y = -10; y <= 10; y += 0.5) {
// 动态波浪方程
const z = Math.sin(x + this.time) * Math.cos(y + this.time) * 2;
data.push([x, y, z]);
}
}
return data;
}
stop() {
if (this.animationId !== null) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
}
dispose() {
this.stop();
this.chart.dispose();
}
}
// 使用
const wave = new AnimatedWave(document.getElementById('chart')!);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
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
3. 性能优化策略
typescript
// 大数据量曲面优化
const optimizedOption = {
series: [{
type: 'surface',
data: largeSurfaceData, // 50000+ 点
// 渐进式渲染
progressive: 1000,
progressiveThreshold: 5000,
// 简化着色
shading: 'color', // 不使用光照
// 关闭线框
wireframe: {
show: false
},
// 降低采样率
sampling: 'lttb' // LTTB降采样
}]
};1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
4. LOD多层次细节
typescript
class LODSurface {
private chart: echarts.ECharts;
private currentLevel: number = 0;
updateViewDistance(distance: number) {
let level = 0;
if (distance > 400) level = 0; // 低精度
else if (distance > 200) level = 1; // 中精度
else level = 2; // 高精度
if (level !== this.currentLevel) {
this.currentLevel = level;
this.updateLOD(level);
}
}
private updateLOD(level: number) {
const resolutions = [1.0, 0.5, 0.25]; // 不同精度的采样率
const resolution = resolutions[level];
const data = this.generateDataWithResolution(resolution);
this.chart.setOption({
series: [{
data: data,
shading: level === 2 ? 'realistic' : 'color'
}]
});
}
private generateDataWithResolution(resolution: number): number[][] {
const data: number[][] = [];
const step = 0.5 / resolution;
for (let x = -10; x <= 10; x += step) {
for (let y = -10; y <= 10; y += step) {
const z = Math.sin(x) * Math.cos(y);
data.push([x, y, z]);
}
}
return data;
}
}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
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
📊 应用场景
1. 科学计算可视化
- 电磁场分布
- 流体动力学
- 温度场分析
2. 地理信息系统
- 数字高程模型(DEM)
- 地质勘探数据
- 气象数据
3. 工程仿真
- 应力分析
- 变形模拟
- 振动模态
🔗 相关资源
上一篇: bar3D-scatter3D
下一篇: 3D相机控制
