Cesium实现动态悬浮旋转四棱锥效果 2023.9.11
- 1、引言
- 2、两种实现思路介绍
- 2.1 思路一:添加已有的四棱锥(金字塔)模型实现(简单但受限)
- 2.2 思路二:自定义四棱锥几何模型实现(复杂且灵活)
- 3、代码实现及效果展示
- 3.1 思路一
- 3.1.1 代码实现
- 3.1.2 展示结果
- 3.2 思路二
- 3.2.1 代码实现
- 3.2.2 展示结果
- 4、总结
1、引言
最近看到一些数字城市特效,其中包含四棱锥动态旋转
的效果,乍一看眼前一亮,静下来来冷静思考觉得实现起来应该也十分简单,于是打算写此博客与诸位开发者共同分享,当然也是为了记录学习点滴!🕺🕺🕺❤️❤️❤️
2、两种实现思路介绍
顾名思义,动态悬浮旋转四棱锥效果中的关键词包括:四棱锥(金字塔)、旋转、动态
。
2.1 思路一:添加已有的四棱锥(金字塔)模型实现(简单但受限)
寻找并选择一个现成的四棱锥模型(gltf
或 glb
文件),调用Cesium添加Model的API将其作为模型加载到三维场景当中,之后动态设置旋转角度、位置高度
即可。
假设准备好的模型文件为pyramid.glb文件,利用Windows
系统自带的3D查看器通过设置跳跃、悬浮等动作即可预览动态效果。
2.2 思路二:自定义四棱锥几何模型实现(复杂且灵活)
调用Cesium
底层API
自定义几何形状(Geometry)和原型(Primitive),构造属于四棱锥的DrawCommand类,实现create方法在三维场景(Scene
)中添加四棱锥几何形状并设置纹理显示,实现update方法在每一帧画面中更新显示动态旋转及上下悬浮效果。
值得注意的是,我们仍需明确有关正四棱锥的一些数学理论知识:在三维立体几何空间中,四棱锥包含5个顶点、6个三角面(1个四边形可拆分为2个三角形),每个顶点包含X、Y、Z这三个坐标。
如果给所有顶点从0开始进行顺序编号,那么各个三角面就能根据三个顶点索引随着确定,相应地纹理赋值也能随之确定。
3、代码实现及效果展示
接下来将具体调用Cesium
的API
按照上述两种思路分别进行实现,具体代码如下:
3.1 思路一
3.1.1 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Cesium旋转金字塔-jjg</title>
<!-- 引入Cesium -->
<script src="https://unpkg.com/cesium@1.84.0/Build/Cesium/Cesium.js"></script>
<link rel="stylesheet" href="https://unpkg.com/cesium@1.84.0/Build/Cesium/Widgets/widgets.css">
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.5/lib/theme-chalk/index.css">
<script src="https://unpkg.com/element-ui@2.15.5/lib/index.js"></script>
<style>
* {
margin: 0;
padding: 0;
}
html,
body,
#viewer-container {
width: 100%;
height: 100%;
overflow: hidden;
}
.cesium-widget-credits{
display:none!important;
visibility:hide!important;
}
.cesium-viewer-toolbar{
display:none!important;
visibility:hide!important;
}
.form-container {
position: absolute;
left: 10px;
top: 90px;
padding: 10px 15px;
border-radius: 4px;
border: 1px solid rgba(128, 128, 128, 0.5);
color: #ffffff;
background: rgba(0, 0, 0, 0.4);
box-shadow: 0 3px 14px rgb(128 128 128 / 50%);
max-width: 380px;
}
button {
background: transparent;
border: 1px solid #00d0ffb8;
color: white;
padding: 7px 9px;
border-radius: 2px;
margin: 3px;
cursor: pointer
}
.tip-item {
margin: 2px 0px;
padding: 5px 1px;
}
</style>
</head>
<body>
<div id="viewer-container"></div>
<div class="form-container" id="formContainer">
<button onclick="setvisible('add')" style="margin-left:120px;">添加旋转金字塔</button>
<button onclick="setvisible('remove')">移除旋转金字塔</button>
</div>
<script>
var viewer = null;
var modelEntity = null;
// 开关
function setvisible(value) {
switch (value) {
case 'add':
addPyramidModel();
break;
case 'remove':
removeRotateCircle();
break;
}
}
// 添加旋转金字塔
function addPyramidModel() {
let hpr = new Cesium.HeadingPitchRoll(
Cesium.Math.toRadians(0),
Cesium.Math.toRadians(180),//0朝下 180朝上
Cesium.Math.toRadians(0)
)
let r = Cesium.Math.toRadians(2);
let lon = 121.50320483066757, lat = 31.23641093043576, height = 382.83983348350085,isUp = true;
// let position = Cesium.Cartesian3.fromDegrees(121.50320483066757, 31.23641093043576, 382.83983348350085);
modelEntity = this.viewer.entities.add({
position: new Cesium.CallbackProperty(e => {
if(height > 400)
{
height = 400;
isUp = false;
}
else if(height < 350)
{
height = 350;
isUp = true;
}
if(isUp)
{
height += 1.0;
}
else
{
height -= 1.0;
}
return Cesium.Cartesian3.fromDegrees(lon,lat,height);
}, false),
//旋转起来
orientation: new Cesium.CallbackProperty(e => {
window.console.log(e);
hpr.heading += r;
let position = Cesium.Cartesian3.fromDegrees(lon,lat, height);
return Cesium.Transforms.headingPitchRollQuaternion(position, hpr);
}, false),
model: {
uri: "pyramid.glb",//this.style.modelUrl
scale: 40,//this.style.scale ||
color: Cesium.Color.YELLOW.withAlpha(0.8),
colorBlendMode: Cesium.ColorBlendMode.MIX,
}
});
viewer.flyTo(modelEntity);
}
// 移除旋转金字塔
function removeRotateCircle() {
if(modelEntity != null)
{
viewer.entities.remove(modelEntity);
modelEntity.destroy();
modelEntity = null;
}
}
// init
function initPage() {
// 切换自己的token
Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYTQ2ZjdjNS1jM2E0LTQ1M2EtOWM0My1mODMzNzY3YjYzY2YiLCJpZCI6MjkzMjcsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1OTE5NDIzNjB9.RzKlVTVDTQ9r7cqCo-PDydgUh8Frgw0Erul_BVxiS9c';
// 初始化
viewer = new Cesium.Viewer("viewer-container", {
infoBox: false,
shouldAnimate: true,
vrButton: true,
geocoder: false,
homeButton: false,
sceneModePicker: false,
baseLayerPicker: false,
navigationHelpButton: false,
animation: false,//动画控制不显示
timeline: false,//时间线不显示
fullscreenButton: false,//全屏按钮不显示
terrainProvider: Cesium.createWorldTerrain({
requestWaterMask: true, // 水特效
requestVertexNormals: true // 地形光
}),
});
// 加载倾斜摄影 大雁塔
//var tileset = viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
// url: 'http://earthsdk.com/v/last/Apps/assets/dayanta/tileset.json'
//}));
//viewer.flyTo(tileset);
viewer.scene.primitives.add(new Cesium.Cesium3DTileset({
url: 'http://earthsdk.com/v/last/Apps/assets/dayanta/tileset.json',
show: true,
backFaceCulling: true,
})
).readyPromise.then((tileset) => {
//拉伸模型高度代码
let heightOffset = -26;
var boundingSphere = tileset.boundingSphere;
var cartographic = Cesium.Cartographic.fromCartesian(boundingSphere.center);
var surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 0.0);
var offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, heightOffset);
var translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
//viewer.zoomTo(tileset)
viewer.flyTo(tileset);
})
}
//
window.onload = function () {
initPage();
}
</script>
</body>
</html>
3.1.2 展示结果
3.2 思路二
3.2.1 代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
.cesium-widget-credits{
display:none!important;
visibility:hide!important;
}
.cesium-viewer-toolbar{
display:none!important;
visibility:hide!important;
}
.middleTop {
width: 300px;
height: 30px;
position: fixed;
top: 10px;
left: 20px;
text-align: center;
background: red;
opacity: 0.6;
}
button {
background: gray;
border: 1px solid #00d0ffb8;
color: white;
padding: 7px 9px;
border-radius: 2px;
margin: 3px;
cursor: pointer
}
.tip-item {
margin: 2px 0px;
padding: 5px 1px;
}
</style>
<script src="https://cesium.com/downloads/cesiumjs/releases/1.89/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.89/Build/Cesium/Widgets/widgets.css" rel="stylesheet">
<!-- <script src="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Cesium.js"></script>
<link href="https://cesium.com/downloads/cesiumjs/releases/1.108/Build/Cesium/Widgets/widgets.css" rel="stylesheet"> -->
</head>
<body>
<div id="cesiumContainer" style="width:100%;height:100%;">
</div>
<div class="middleTop" id="demo2">
<div class="map-tool">
<button id="addTetrahedron" class="newBtn">添加倒立四棱锥</button>
<button id="removeTetrahedron" class="newBtn">移除一个倒立四棱锥</button>
</div>
</div>
<script>
Cesium.Ion.defaultAccessToken = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIwZDhhOThhNy0zMzUzLTRiZDktYWM3Ni00NGI5MGY2N2UwZDUiLCJpZCI6MjQzMjYsInNjb3BlcyI6WyJhc3IiLCJnYyJdLCJpYXQiOjE1ODUwMzUwNDh9.DYuDF_RPKe5_8w849_y-sutM68LM51O9o3bTt_3rF1w";
const viewer = new Cesium.Viewer('cesiumContainer', { // Initialize the Cesium Viewer in the HTML element with the `cesiumContainer` ID.
baseLayerPicker: false,
//shadows: true,
shouldAnimate: true,
infoBox: false,
animation: false,//动画控制不显示
timeline: false,//时间线不显示
fullscreenButton: false, //全屏按钮不显示
terrainProvider: Cesium.createWorldTerrain({
requestWaterMask: true, // 水特效
requestVertexNormals: true // 地形光
}),
selectionIndicator: false, // By JIAO Jingguo 2022.9.21 移除Cesium自带的绿色聚焦瞄准框
//imageryProvider: new Cesium.ArcGisMapServerImageryProvider({
// url: 'http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer',
//}),
});
viewer._cesiumWidget._creditContainer.style.display = "none";//去除版权信息
//viewer.scene.globe.depthTestAgainstTerrain = true;
let silhouette = null,skylineAnayStages = null;//天际线分析工具
//打开天际线分析
function openSkylineAnay() {
if(skylineAnayStages){
silhouette.enabled=true;
return;
}
skylineAnayStages = viewer.scene.postProcessStages;
let edgeDetection = Cesium.PostProcessStageLibrary.createEdgeDetectionStage();
let postProccessStage = new Cesium.PostProcessStage({
//此后处理阶段的唯一名称,供组合中其他阶段参考,如果未提供名称,将自动生成GUID
// name:name,
//unform着色器对象 textureScale
fragmentShader: 'uniform sampler2D colorTexture;' +
'uniform sampler2D depthTexture;' +
'varying vec2 v_textureCoordinates;' +
'void main(void)' +
'{' +
'float depth = czm_readDepth(depthTexture, v_textureCoordinates);' +
'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
'if(depth<1.0 - 0.000001){' +
'gl_FragColor = color;' +
'}' +
'else{' +
'gl_FragColor = vec4(1.0,0.0,0.0,1.0);' +
'}' +
'}'
});
//PostProcessStage:要使用的片段着色器。默认sampler2D制服是colorTexture和depthTexture。
let postProccesStage_1 = new Cesium.PostProcessStage({
// name:obj.name+'_1',
fragmentShader: 'uniform sampler2D colorTexture;' +
'uniform sampler2D redTexture;' +
'uniform sampler2D silhouetteTexture;' +
'varying vec2 v_textureCoordinates;' +
'void main(void)' +
'{' +
'vec4 redcolor=texture2D(redTexture, v_textureCoordinates);' +
'vec4 silhouetteColor = texture2D(silhouetteTexture, v_textureCoordinates);' +
'vec4 color = texture2D(colorTexture, v_textureCoordinates);' +
'if(redcolor.r == 1.0){' +
'gl_FragColor = mix(color, vec4(5.0,0.0,0.0,1.0), silhouetteColor.a);' +
'}' +
'else{' +
'gl_FragColor = color;' +
'}' +
'}',
//uniform着色器对象
uniforms: {
redTexture: postProccessStage.name,
silhouetteTexture: edgeDetection.name
}
});
//如果inputPreviousStageTexture 是 true,则每个阶段输入是场景渲染到的输出纹理或之前执行阶段的输出纹理
//如果inputPreviousStageTexture是false,则合成中每个阶段的输入纹理都是相同的
silhouette= new Cesium.PostProcessStageComposite({
stages:[edgeDetection,postProccessStage,postProccesStage_1], //PostProcessStage要按顺序执行 的 s 或组合的数组。
inputPreviousStageTexture:false,//是否执行每个后处理阶段,其中一个阶段的输入是前一个阶段的输出。否则每个包含阶段的输入是组合之前执行的阶段的输出
uniforms:edgeDetection.uniforms//后处理阶段制服的别名
})
skylineAnayStages.add(silhouette);
};
function closeSkylineAnay(){ //关闭天际线分析
if(silhouette != null)
silhouette.enabled=false;
};
function flyToTianJin() {
viewer.camera.flyTo({
destination : Cesium.Cartesian3.fromDegrees(117.17888105784743,39.06048272010123, 5000),
orientation : {
heading : Cesium.Math.toRadians(0.0),
pitch : Cesium.Math.toRadians(-90.0),
roll: 0.0,
}
});
};
// 添加四棱锥
document.getElementById("addTetrahedron").addEventListener("click", function (e) {
onLineTetra();// By JIAO Jingguo 2023.8.31
});
// 移除四棱锥
document.getElementById("removeTetrahedron").addEventListener("click", function (e) {
removeSinglePrimitive();// By JIAO Jingguo 2023.8.31
});
let addedPrimitives = [];
function onLineTetra() // By JIAO Jingguo 2023.8.31 源自 Mars3d源码启发 和 网上资料
{
function TetrahedronPrimitive(options){
this.show = true;
this._command = undefined;
this._enuMatrix=undefined;
this._scaleMatrix=undefined;
this._localPosition = options.position;
this._createCommand = createCommand;
this._angle=0;
this._distance= Cesium.defaultValue(options.distance,1);
this._setInterval=undefined;
this._viewer= viewer;
this._speed= Cesium.defaultValue(options.speed,1.0);
this._color= Cesium.defaultValue(options.color,new Cesium.Color(1.0,0.0,0.0,0.8));
this._scale= Cesium.defaultValue(options.scale,new Cesium.Cartesian3(10, 10, 15));
this._texture=undefined;
// this._imageUrl= Cesium.buildModuleUrl('./fence.png');
this._modelMatrix=computeModelMatrix(this);
this._height=computeHeight(this);
//debugger
// createTexture(this);
}
TetrahedronPrimitive.prototype.update=function(frameState) {
if (!this.show)
{
return;
}
if (! Cesium.defined(this._command))
{
this._command = this._createCommand(frameState.context,this);
this._command.pickId = 'v_pickColor';
}
if ( Cesium.defined(this._command))
{
frameState.commandList.push(this._command);
}
}
TetrahedronPrimitive.prototype.isDestroyed=function() {
return false;
}
TetrahedronPrimitive.prototype.destroy=function() {
if ( Cesium.defined(this._command))
{
this._command.shaderProgram = this._command.shaderProgram && this._command.shaderProgram.destroy();
}
return Cesium.destroyObject(this);
}
//开启动画
TetrahedronPrimitive.prototype.startAnimate=function(){
let that=this;
this._setInterval=setInterval(animateFunc,5);
function animateFunc(){
that._angle=that._angle+0.01;
if(Math.sin(that._angle) < 0)
{
that._height=0.01;
}
else
{
that._height=-0.01;
}
let translation = new Cesium.Cartesian3( 0, 0, that._height );
Cesium.Matrix4.multiplyByTranslation(that._modelMatrix, translation, that._modelMatrix);
let rotationZ = Cesium.Matrix4.fromRotationTranslation( Cesium.Matrix3.fromRotationZ(Cesium.Math.toRadians(that._speed)));
Cesium.Matrix4.multiply(that._modelMatrix, rotationZ, that._modelMatrix);
}
}
//关闭动画
TetrahedronPrimitive.prototype.closeAnimate=function(){
clearInterval(this._setInterval);
}
//创建command
function createCommand(context,tetrahedronPrimitive) {
var translucent = false;
var closed = true;
var vs = creaateVertexShader();
var fs = createFragmentShader();
// 借用一下Appearance.getDefaultRenderState
var rawRenderState = Cesium.Appearance.getDefaultRenderState(translucent, closed, undefined);
var renderState = Cesium.RenderState.fromCache(rawRenderState);
var vertexShaderSource = new Cesium.ShaderSource({
sources: [vs]
});
var fragmentShaderSource = new Cesium.ShaderSource({
sources: [fs]
});
var uniformMap = {
color: function() {
return tetrahedronPrimitive._color;
},
myImage: function() {
if (Cesium.defined(tetrahedronPrimitive._texture)) {
return tetrahedronPrimitive._texture;
} else {
return tetrahedronPrimitive._viewer.scene.context.defaultTexture;
}
}
};
let attributeLocations = {
position: 0,
textureCoordinates:1
};
var shaderProgram = Cesium.ShaderProgram.fromCache({
context: context,
vertexShaderSource: vertexShaderSource,
fragmentShaderSource: fragmentShaderSource,
attributeLocations: attributeLocations
});
return new Cesium.DrawCommand({
vertexArray: createVertexArray(context),
primitiveType: Cesium.PrimitiveType.TRIANGLES,
renderState: renderState,
shaderProgram: shaderProgram,
uniformMap: uniformMap,
owner: this,
pass: Cesium.Pass.TRANSLUCENT,
modelMatrix: tetrahedronPrimitive._modelMatrix,
});
}
//创建vertexArray
function createVertexArray(context) {
let attributeLocations = {
position: 0,
textureCoordinates:1
};
var positionsAndIndice=cereatePositionsAndIndice();
var geometry = new Cesium.Geometry({
attributes: {
position: new Cesium.GeometryAttribute({
// 使用double类型的position进行计算
// componentDatatype : Cesium.ComponentDatatype.DOUBLE,
componentDatatype: Cesium.ComponentDatatype.FLOAT,
componentsPerAttribute: 3,
values: positionsAndIndice.positions
}),
textureCoordinates: new Cesium.GeometryAttribute({
componentDatatype: Cesium.ComponentDatatype.FLOAT,
componentsPerAttribute: 2,
values: positionsAndIndice.sts
}),
},
// Workaround Internet Explorer 11.0.8 lack of TRIANGLE_FAN
indices: positionsAndIndice.indices,
primitiveType: Cesium.PrimitiveType.TRIANGLES,
boundingSphere: Cesium.BoundingSphere.fromVertices(positionsAndIndice.positions)
});
//计算geometry的法向量
var geometryNormal= Cesium.GeometryPipeline.computeNormal(geometry);
var vertexArray = Cesium.VertexArray.fromGeometry({
context: context,
geometry: geometryNormal,
attributeLocations: attributeLocations,
bufferUsage: Cesium.BufferUsage.STATIC_DRAW,
});
return vertexArray;
}
//创建顶点数组与索引
function cereatePositionsAndIndice(){
var positions = new Float64Array(5 * 3);
// position 0
positions[0] = 0.0;
positions[1] = 1.0;
positions[2] = 0.0;
// position 1
positions[3] = -1.0;
positions[4] = 0.0;
positions[5] = 0.0;
// position 2
positions[6] = 0.0;
positions[7] = -1.0;
positions[8] = 0.0;
// position 3
positions[9] = 1.0;
positions[10] = 0.0;
positions[11] = 0.0;
// position 4
positions[12] = 0.0;
positions[13] = 0.0;
positions[14] = 1.0;
var indices = new Uint16Array(6 * 3);
// back triangle
indices[0] = 4;
indices[1] = 2;
indices[2] = 3;
// left triangle
indices[3] = 4;
indices[4] = 3;
indices[5] = 0;
// right triangle
indices[6] = 4;
indices[7] = 0;
indices[8] = 1;
// bottom triangle
indices[9] = 4;
indices[10] = 1;
indices[11] = 2;
// bottom triangle
indices[12] = 1;
indices[13] = 2;
indices[14] = 3;
// bottom triangle
indices[15] = 1;
indices[16] = 3;
indices[17] = 0;
// 1.3 定义纹理数组
var sts = new Float32Array([
0.0, 0.0, 1.0, 0.0, 1.0, 1.0,
0.0, 1.0, 0.5, 0.5,
]);
return {
indices:indices,
positions:positions,
sts:sts
};
}
//创建顶点着色器
function creaateVertexShader(){
var vertexShader =
`
attribute vec3 position;
attribute vec3 normal;
attribute vec2 st;
attribute float batchId;
varying vec3 v_positionEC;
varying vec3 v_normalEC;
varying vec2 v_st;
varying vec4 v_pickColor;
void main()
{
v_positionEC = (czm_modelView * vec4(position, 1.0)).xyz; // position in eye coordinates
v_normalEC = czm_normal * normal; // normal in eye coordinates
v_st = st;
//v_pickColor = czm_batchTable_pickColor(batchId);
gl_Position = czm_modelViewProjection * vec4(position, 1.0);
}
`;
return vertexShader;
}
//创建片源着色器
function createFragmentShader(){
var fragmentShader =
`
varying vec3 v_positionEC;
varying vec3 v_normalEC;
varying vec2 v_st;
uniform vec4 color;
varying vec4 v_pickColor;
uniform sampler2D myImage;
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;
vec2 st = materialInput.st;
czm_material material = czm_getDefaultMaterial(materialInput);
// float dt_a11 = fract(czm_frameNumber / 100.0) * 3.14159265 * 2.0;
// float dt_a12 = sin(dt_a11);
// float vst=smoothstep(0.7, 1.0, dt_a12)+0.4;
// vec4 colorImage = texture2D(myImage, vec2(fract(st.s- czm_frameNumber*0.003), st.t));
// material.alpha =mix(0.1,1.0,clamp((1.0-st.t) * color.a,0.0,1.0)) +(1.0-sign(st.t-czm_frameNumber*0.001))*0.2*(1.0-colorImage.r)+0.4 ;
// material.diffuse =(1.0-colorImage.a)*vec3(1.0,0.0,0.0)+colorImage.rgb*vec3(1.0,1.0,0);
material.alpha = (mix(0.1, 1.0, clamp((1.0 - st.t) * color.a, 0.0, 1.0)) + (1.0 - sign(st.t - czm_frameNumber * 0.001)) * 0.2 + 0.4) * 0.8;
material.diffuse = color.rgb;
#ifdef FLAT
gl_FragColor = vec4(material.diffuse + material.emission, material.alpha);
#else
gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);
#endif
}
`;
return fragmentShader;
}
//创建纹理
function createTexture(tetrahedronPrimitive){
Cesium.Resource.createIfNeeded(tetrahedronPrimitive._imageUrl).fetchImage().then(function(image){
var vTexture;
var context = tetrahedronPrimitive._viewer.scene.context;
if ( Cesium.defined(image.internalFormat)) {
vTexture = new Cesium.Texture({
context: context,
pixelFormat: image.internalFormat,
width: image.naturalWidth,
height: image.naturalHeight,
source: {
arrayBufferView: image.bufferView
}
});
} else {
vTexture = new Cesium.Texture({
context: context,
source: image
});
}
tetrahedronPrimitive._texture = vTexture;
});
}
//计算矩阵
function computeModelMatrix(tetrahedronPrimitive){
let enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tetrahedronPrimitive._localPosition);
let scaleMatrix = Cesium.Matrix4.fromScale(tetrahedronPrimitive._scale);
let modelMatrix = Cesium.Matrix4.multiply(enuMatrix, scaleMatrix, new Cesium.Matrix4());
tetrahedronPrimitive._scaleMatrix=scaleMatrix;
tetrahedronPrimitive._enuMatrix=enuMatrix;
return modelMatrix;
}
//计算高度
function computeHeight(tetrahedronPrimitive){
let point= Cesium.Cartesian3.fromElements(0,0,tetrahedronPrimitive._distance,new Cesium.Cartesian3());
let enuPoint = Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._enuMatrix, point, new Cesium.Cartesian3());
let upPositionEC = Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._viewer.scene.camera._viewMatrix, enuPoint, new Cesium.Cartesian3());
let upPositionPC = Cesium.Matrix4.multiplyByPoint(tetrahedronPrimitive._viewer.scene.camera.frustum.projectionMatrix, upPositionEC, new Cesium.Cartesian3());
return Cesium.Cartesian3.normalize(upPositionPC, new Cesium.Cartesian3()).z;
}
// const center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
let positions =
{
"x": -2481464.959108353,
"y": 4823824.427904163,
"z": 3343877.308220879
};
let colors = [
new Cesium.Color(1.0,1.0,0.0,1.0),
new Cesium.Color(1.0,0.0,0.0,0.8),
new Cesium.Color(0.0,1.0,0.0,0.8),
new Cesium.Color(0.0,0.0,1.0,0.8),
new Cesium.Color(0.8, 0.8, 0x0, 0.8)
];
for(let i=0;i<colors.length;i++)
{
let pt = new Cesium.Cartesian3(positions.x + i+100, positions.y , positions.z+ i * 100)
let primitive = new TetrahedronPrimitive({
position: pt,
color: colors[i]
})
viewer.scene.primitives.add(primitive);
primitive.startAnimate();
if(i === 2)
viewer.flyTo(primitive);
}
viewer.camera.setView({
destination: new Cesium.Cartesian3(
positions.x, positions.y, positions.z
),
// orientation: {
// heading: 6.276226863836136,
// pitch: -1.331128445292896,
// roll: 0.0001241421687643296
// }
});
}
function removeSinglePrimitive()
{
const primitives = viewer.scene.primitives;
const length = primitives.length;
for (let i = 0; i < length; ++i) {
const p = primitives.get(i);
// p.show = !p.show;
if(i === length -1)
viewer.scene.primitives.remove(p);
}
}
</script>
</body>
</html>
3.2.2 展示结果
4、总结
总的来说,第一种实现思路方法简单,但需要找到现有的符合自己需求的模型,之后设置旋转和上下浮动效果即可;而第二种实现思路方法更接近底层,需要深入思考,充分理解顶点(Vertex)、三角面(Triangle)、纹理(Texture)、着色器语言(Shaders)、帧动态更新等理论思想,学习Cesium
源码是一件极为有趣和快乐的事情,当然需要一定的专业知识背景和先验知识(professional knowledge background and prior knowledge
)做支撑才能学以致用,深刻领会其精髓。
正四棱锥(金字塔)结构稳定,拥有坚实的基底,在生物学的食物链、古埃及的墓穴
尽管并不是每一颗金子都能够闪闪发光,但被周围的沙子所环绕更是一种历练,逆境生长方显生命力之顽强,牢记自己的独特与唯一,坦然面对周围事物,刻在骨子里的坚强终有一日会在芸芸众生中闪耀光芒。愿大家珍惜眼前的一切,以顽强拼搏的奋斗姿态期待和迎接更加光明和美好的未来。
祝大家国庆节快乐,祖国繁荣昌盛,人民幸福安康!