1. 效果展示
2. 完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>自定义着色器完整代码</title>
<!-- 线上 -->
<script src="https://cesium.com/downloads/cesiumjs/releases/1.107.1/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.107.1/Build/Cesium/Widgets/widgets.css" rel="stylesheet" />
<style>
#map {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
</head>
<body>
<div id="map"></div>
<script>
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJhZjZkZDAwZC1mNTFhLTRhOTEtOGExNi00MzRhNGIzMDdlNDQiLCJpZCI6MTA1MTUzLCJpYXQiOjE2NjA4MDg0Njd9.qajeJtc4-kppqfR1--Y2FqCu5r3TE1xYYGnEQhy-JF0'
const viewer = new Cesium.Viewer('map', {
infoBox: false,
shadows: true,
terrainShadows: Cesium.ShadowMode.RECEIVE_ONLY,
terrain: Cesium.Terrain.fromWorldTerrain(),
})
//平面的顶点数据
let points = [110.2, 20.0, 112.2, 20.0, 110.2, 22.0, 112.2, 22.0]
let positions = Cesium.Cartesian3.fromDegreesArray(points)
//顶点对应的纹理坐标数据
let sts = [0, 0, 0, 1, 1, 0, 1, 1]
//顶点卷绕的方式
let positionIndex = [0, 1, 2, 1, 2, 3]
function primitivePlaneShader(options) {
console.log(options, 'options')
let viewer = options.viewer
let vertexShader = v_shader()
let fragShader = f_shader()
let sts = new Uint8Array(options.sts) //纹理数据
let positionIndex = new Uint16Array(options.positionIndex) //顶点索引数据
let tempPosition = []
for (var i = 0; i < options.positions.length; i++) {
tempPosition.push(options.positions[i].x)
tempPosition.push(options.positions[i].y)
tempPosition.push(options.positions[i].z)
}
positions = new Float64Array(tempPosition) //顶点数据
let geometry = createGeometry(positions, sts, positionIndex) //几何体
let appearance = createAppearance(vertexShader, fragShader) //外观
//primitive方式加载
viewer.scene.primitives.add(
new Cesium.Primitive({
geometryInstances: new Cesium.GeometryInstance({
//渲染的几何体
geometry: geometry,
}),
appearance: appearance, //外观
asynchronous: false,
})
)
}
function createAppearance(vertexShader, fragShader) {
return new Cesium.Appearance({
material: new Cesium.Material({
/* 在Cesium中,fabric 是用于创建着色器材质(Shader Material)的对象。
它允许您定义一种着色器材质类型,并通过uniforms(统一变量)传递外部数据给着色器。 */
fabric: {
/*
uniforms是一个包含统一变量(Uniform)的对象。
统一变量是一种在着色器程序中定义的全局变量,
它们的值可以在渲染过程中从CPU(JavaScript代码)传递给GPU(着色器代码)。 */
uniforms: {
speed: 0.1,
},
source: getMS(),
},
}),
translucent: false, //显示不为半透明
renderState: {
blending: Cesium.BlendingState.PRE_MULTIPLIED_ALPHA_BLEND, //使用Alpha混合功能启用混合
depthTest: { enabled: true }, //深度检测
depthMask: true, //将深度值写入深度缓冲区
},
fragmentShaderSource: fragShader, //片段着色器
vertexShaderSource: vertexShader, //顶点着色器
})
}
//构建几何体
function createGeometry(positions, sts, positionIndex) {
return new Cesium.Geometry({
attributes: {
//几何顶点属性
position: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.DOUBLE, //数据类型
componentsPerAttribute: 3, //定义几个为一组
values: positions, //坐标值
}),
st: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.FLOAT, //数据类型
componentsPerAttribute: 2, //定义几个为一组
values: sts, //坐标值
}),
},
indices: positionIndex, //顶点索引
primitiveType: Cesium.PrimitiveType.TRIANGLES, //图元类型
boundingSphere: Cesium.BoundingSphere.fromVertices(positions), //包围球
})
}
function v_shader() {
// 这段着色器代码的作用是将顶点变换到裁剪空间,并传递纹理坐标给片段着色器,为后续的光栅化和渲染阶段做准备。
return `
in vec3 position3DHigh;//这是一个输入变量,表示高精度的3D位置。通常情况下,这个变量的值是由CesiumJS引擎在渲染过程中提供的,它表示当前处理的顶点或片段的高精度3D位置。
in vec3 position3DLow;//这也是一个输入变量,表示低精度的3D位置。在CesiumJS中,当高精度3D位置无法完全表示时,这个变量提供额外的信息,以便更准确地确定位置。
in float batchId;//这是一个输入变量,表示批次ID。在CesiumJS中,通常用于将相同类型的几何体分组在一起,以优化渲染性能。
in vec2 st;//这是一个输入变量,表示纹理坐标。
out vec2 v_st; //这是一个输出变量,表示处理后的纹理坐标。
void main() {
// czm_computePosition() //计算模型视图相对眼睛坐标空间中的顶点位置。
//czm_modelViewProjectionRelativeToEye//变换顶点位置到裁剪空间,并将结果保存在position中。
vec4 position = czm_modelViewProjectionRelativeToEye *czm_computePosition();
//将输入变量st赋值给输出变量v_st,这样纹理坐标将在顶点着色器中传递给片段着色器。
v_st = st;
//将经过变换后的顶点位置position赋值给内置变量gl_Position,这样OpenGL将使用这个值来确定顶点在屏幕上的位置。
gl_Position = position;
}
`
}
function f_shader() {
return `
in vec2 v_st;//这是一个输入变量,表示纹理坐标。在片段着色器中,v_st变量将包含插值后的纹理坐标值,这些值在顶点着色器中计算并传递给片段着色器。
//uniform float speed;
void main() {
//这一行将纹理坐标(v_st)转换为范围在[-1, 1]的坐标(position)。在OpenGL中,纹理坐标的范围通常是[0, 1],而转换后的坐标在顶点着色器的插值过程中进行了变换,使其范围变为[-1, 1]。
vec2 position = -1.0 + 2.0 *v_st;
//这行代码调用一个函数getSpeed(),该函数返回一个浮点数,表示速度。
float speed = getSpeed();
//这一行根据帧号(czm_frameNumber)和速度(speed)计算时间(time)。czm_frameNumber是CesiumJS引擎提供的一个内置变量,表示当前渲染帧的序号。
float time= czm_frameNumber *speed;
//计算颜色
float r = abs( cos( position.x * position.y + time / 5.0 ));
float g = abs( sin( position.x * position.y + time / 4.0 ) );
float b = abs( cos( position.x * position.y + time / 3.0 ) );
//并将其赋值给特殊的输出变量out_FragColor,这个变量是CesiumJS中用于存储片段着色器输出颜色的内置变量。
out_FragColor = vec4( r, g, b, 1.0 );
}
`
}
function getMS() {
return `#version 300 es
uniform float speed;
float getSpeed(){
return speed;
}
`
}
var plane = new primitivePlaneShader({
viewer: viewer,
positions: positions,
sts: sts,
positionIndex: positionIndex,
})
viewer.scene.camera.setView({
destination: new Cesium.Cartesian3.fromDegrees(110.2, 20.0, 500000),
})
</script>
</body>
</html>