原文链接及内容

效果如下图所示:

示例代码如下:

注:

  1. 着色器代码中也可以通过//添加注释。
  2. new Cesium.Material(options):材质通过漫反射、镜面反射、法线、发射和透明度组件的组合定义表面外观。这些值使用称为 Fabric 的 JSON 模式指定,并在后台解析并组装成 glsl 着色器代码。具体教程请参考:https://github.com/CesiumGS/cesium/wiki/Fabric
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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
const boxSize = 25;

// 使用自定义着色器代码创建材质外观。
function createMaterialAppearance() {
// 创建自定义材质
const customMaterial = new Cesium.Material({
translucent: false,
fabric: {
type: "CustomBoxShader",
/**
* 注意:Material 材质中的 Uniform 变量只能直接在材质着色器源代码中使用。
* 在最终的着色器代码中,变量名称将被覆盖。
*/
uniforms: {
time: 0.0,
},
source: `
//由fabric添加的uniform变量需要显式声明。
uniform float time;

// 定义一个函数以返回 fabric uniform 的值。
// 这使得它可以在最终的着色器代码中访问,其中修改后的 uniform 名称是未知的。
float getUniformTimeOfMaterial(){
return time;
}

czm_material czm_getMaterial(czm_materialInput materialInput) {
czm_material material = czm_getDefaultMaterial(materialInput);

// 在 Material fabric source中,可以直接使用声明的 uniform 名称。
// material.alpha = 0.33 + (sin(time * 0.03) + 1.0) / 3.0;

return material;
}
`,
},
});

const appearance = new Cesium.MaterialAppearance({
material: customMaterial,
flat: false,
faceForward: true,
translucent: true,
closed: true,
vertexShaderSource: `
in vec3 position3DHigh;
in vec3 position3DLow;
in vec3 normal;
in vec2 st;
in float batchId;

out vec3 v_positionEC;
out vec3 v_normalEC;
out vec2 v_st;
out vec3 v_position;

// 通过 appearance.uniforms 传递的变量需要显式声明。
uniform float frameNumber;

mat3 rotateZ(float angle) {
float c = cos(angle);
float s = sin(angle);
return mat3(
c, -s, 0.0,
s, c, 0.0,
0.0, 0.0, 1.0
);
}

void main()
{
vec4 p = czm_computePosition();
//从位置获取原始模型坐标(Model Coordinates,MC),这将损失一些精度。
vec3 cameraPositionMC = czm_encodedCameraPositionMCHigh + czm_encodedCameraPositionMCLow;
vec3 originMC = p.xyz + cameraPositionMC;

// 使用 Appearance 中的 uniform frameNumber 来旋转盒子。
mat3 rotation = rotateZ(frameNumber * 0.01);
originMC = rotation * originMC;

v_position = originMC;

// 恢复到相对于相机的坐标。
originMC = originMC - cameraPositionMC;
p.xyz = originMC;

v_positionEC = (czm_modelViewRelativeToEye * p).xyz;
v_normalEC = czm_normal * normal;
v_st = st;

gl_Position = czm_modelViewProjectionRelativeToEye * p;
}`,
fragmentShaderSource: `
in vec3 v_positionEC;
in vec3 v_normalEC;
in vec2 v_st;
in vec3 v_position;

// appearance.uniforms 传递的变量需要明确声明。
uniform vec3 customColor;
uniform float boxSize;

void main()
{
vec3 positionToEyeEC = -v_positionEC;
vec3 normalEC = normalize(v_normalEC);

#ifdef FACE_FORWARD
normalEC = faceforward(normalEC, vec3(0.0, 0.0, 1.0), -normalEC);
#endif

czm_materialInput materialInput;
materialInput.normalEC = normalEC;
materialInput.positionToEyeEC = positionToEyeEC;
materialInput.st = v_st;
czm_material material = czm_getMaterial(materialInput);

// 使用 Appearance 中的 uniform customColor 来改变颜色
material.diffuse = customColor;

// 两条随机透明且彼此远离的明亮线条。
// Material.uniforms 中的 uniform 变量的值可以通过这里的 Material 中的辅助函数(helper function)获取。
int uniformOfMaterial = int(getUniformTimeOfMaterial() * 4.0);
float s1 = boxSize / 10.0;
float delta = abs(abs(v_position.z) - float(uniformOfMaterial % int((boxSize / 2.0 - s1) * 100.0)) / 100.0);
if(delta < s1)
{
float scale = 1.0 - delta / s1;
material.diffuse = vec3(1.0) * scale;
material.alpha = scale;
}

#ifdef FLAT
out_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
out_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif
}
`,
materialCacheKey: "my-box-material-appearance",
});

// 在 MaterialAppearance 图层中添加 uniform 变量。
// 这些可以直接用于顶点着色器和片元着色器。
// 变量的名称在最终着色器中不会更改。
const color = new Cesium.Color(1.0, 1.0, 0.0, 1.0);
appearance.uniforms = {
frameNumber: 1.0, // 用于顶点着色器。
customColor: color, // 用于片元着色器。
boxSize: boxSize, // 用于片元着色器。
};

return appearance;
}

// 创建具有自定义材质外观的 Box 基元。
function createBoxPrimitive(destination, appearance) {
const boxGeometry = Cesium.BoxGeometry.fromDimensions({
dimensions: new Cesium.Cartesian3(boxSize, boxSize, boxSize),
});

const modelMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(destination);

// 将几何体包装为 GeometryInstance。
const boxInstance = new Cesium.GeometryInstance({
geometry: boxGeometry,
});

// 创建一个图元并将其添加到场景中。
const primitive = new Cesium.Primitive({
geometryInstances: boxInstance,
appearance: appearance,
asynchronous: false,
modelMatrix: modelMatrix,
});

return primitive;
}

// 更新 uniforms。
function updateAppearance(appearance) {
const t = appearance.material.uniforms.time++;
appearance.uniforms.frameNumber++;

const { customColor } = appearance.uniforms;
customColor.red = Math.sin(t * 0.01) ** 2 / 1.5;
customColor.green = Math.sin(t * 0.01 + (2 * Math.PI) / 3) ** 2 / 1.5;
customColor.blue = Math.sin(t * 0.01 + (4 * Math.PI) / 3) ** 2 / 1.5;
}

const viewer = new Cesium.Viewer("cesiumContainer", {
geocoder: false,
homeButton: false,
sceneModePicker: false,
navigationHelpButton: false,
navigationInstructionsInitiallyVisible: false,
animation: false,
timeline: false,
fullscreenButton: false,
skyBox: false,
shouldAnimate: true,
baseLayerPicker: false,
terrain: Cesium.Terrain.fromWorldTerrain(),
});
viewer.cesiumWidget.creditContainer.style.display = "none";

const scene = viewer.scene;

/**
* Cesium.JulianDate(julianDayNumber, secondsOfDay, timeStandard):设定时间,让太阳位于头顶
* - julianDayNumber:代表完整天数的儒略日数。这里没有指定。
* - secondsOfDay:当前儒略日数中的秒数,65398 秒 ≈ 18小时(64800 秒) + 9分钟 + 58 秒
* - timeStandard:默认值TimeStandard.UTC,前两个参数定义的时间标准
*
* 这里是将 Cesium 时钟的当前时间设置为当天的 18:09:58(UTC)。
*/
viewer.clock.currentTime.secondsOfDay = 65398;
scene.globe.enableLighting = true;
scene.fog.enabled = true;

const destination = {
x: -2280236.925141378,
y: 5006991.049189922,
z: 3215839.258024074,
};

const appearance = createMaterialAppearance();
const primitive = createBoxPrimitive(destination, appearance);
scene.preRender.addEventListener(() => {
updateAppearance(appearance);
});

viewer.scene.primitives.add(primitive);
viewer.camera.lookAt(
destination,
new Cesium.HeadingPitchRange(6.283185307179577, -0.4706003213405664, 100)
);