当前位置: 首页 > news >正文

Cesium中的倒立四棱锥:从几何结构到交互式3D可视化

        在数字地球和地理空间可视化领域,Cesium凭借其强大的3D地球渲染能力,已成为开发者构建沉浸式地理空间应用的首选工具。本文将深入探讨一个创新的Cesium应用实例——倒立四棱锥场景,通过剖析其几何结构、自定义渲染和交互设计,揭示如何将基础几何体转化为富有表现力的3D可视化元素。

一、Cesium与3D可视化:从基础到创新

        Cesium是一个开源的JavaScript库,专注于创建高性能、跨平台的3D地球和地图应用。它利用WebGL技术实现硬件加速的3D渲染,支持高精度地形、卫星影像和矢量数据的可视化。本案例中,Cesium不仅展示了其基础功能,更通过自定义几何体和动画效果,探索了3D可视化的新边界。

二、倒立四棱锥的几何结构:解构与重构

        传统四棱锥(金字塔)的几何结构通常为底面正方形,顶部尖点。本案例的"倒立四棱锥"则采用创新的几何设计:

// 定义5个顶点:4个底部顶点 + 1个顶部顶点
var positions = new Float64Array(5 * 3);// 底部顶点 (z = 0) - 平的底部
positions[0] = 1.0; positions[1] = 1.0; positions[2] = 0.0; // 前右
positions[3] = -1.0; positions[4] = 1.0; positions[5] = 0.0; // 前左
positions[6] = -1.0; positions[7] = -1.0; positions[8] = 0.0; // 后左
positions[9] = 1.0; positions[10] = -1.0; positions[11] = 0.0; // 后右
positions[12] = 0.0; positions[13] = 0.0; positions[14] = 2.0; // 顶部顶点

关键创新在于倒立设计:通过computeModelMatrix函数中的180度X轴旋转实现:

// 创建绕X轴旋转180度的矩阵(使四棱锥倒立)
let rotationX = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(180))
);

这种几何结构使四棱锥的"尖"朝下,"底"朝上,创造出视觉上独特的倒置效果,为3D场景增添了艺术感和科技感。

三、自定义几何体与着色器:Cesium高级渲染技术

        Cesium的高级可视化能力很大程度上依赖于自定义几何体和着色器。本案例中,我们实现了两个关键组件:

1. 面部渲染(Face Rendering)

function createFaceVertexShader() {var vertexShader = `attribute vec3 position;attribute vec3 normal;attribute vec2 st;varying vec3 v_positionEC;varying vec3 v_normalEC;varying vec2 v_st;void main() {v_positionEC = (czm_modelView * vec4(position, 1.0)).xyz;v_normalEC = czm_normal * normal;v_st = st;gl_Position = czm_modelViewProjection * vec4(position, 1.0);}`;return vertexShader;
}

面部渲染使用Phong光照模型,确保棱角分明:

material.alpha = color.a;
material.diffuse = color.rgb;
material.specular = 0.5;
material.shininess = 10.0;
gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);

2. 边线渲染(Edge Rendering)

为增强四棱锥的立体感,添加了黑色边线:

function createEdgeVertexShader() {var vertexShader = `attribute vec3 position;void main() {gl_Position = czm_modelViewProjection * vec4(position, 1.0);}`;return vertexShader;
}

边线渲染禁用背面剔除(rawRenderState.cull.enabled = false),确保所有边线可见,并设置线宽为2.0:

rawRenderState.lineWidth = 2.0;

四、动画效果:动态视觉表达

本案例的动画效果通过startAnimate方法实现,利用了Cesium的帧渲染机制:

TetrahedronPrimitive.prototype.startAnimate = function () {let that = this;this._setInterval = setInterval(animateFunc, 5);function animateFunc() {that._angle = that._angle + 0.01 * that._speed;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);}
};

动画包含两个关键运动:

  1. 垂直摆动:通过_height变量实现上下摆动
  2. 水平旋转:通过Z轴旋转实现缓慢旋转

这种组合运动创造出优雅的"摇摆"效果,使四棱锥在场景中更具生命力。

五、交互式控制:用户友好设计

本案例的UI设计体现了"用户中心"理念,通过精心设计的控制面板实现:

1. 控制面板设计

控制面板采用现代UI设计语言:

  • 半透明玻璃效果(backdrop-filter: blur(10px)
  • 精心设计的渐变按钮(linear-gradient
  • 平滑的动画过渡(transition: all 0.3s ease

2. 核心控制功能

控制项功能描述技术实现
添加/移除四棱锥动态创建和销毁几何体viewer.scene.primitives.add/remove
颜色选择实时更改四棱锥颜色Cesium.Color.fromCssColorString
动画速度调整动画节奏更新_speed属性并同步到所有四棱锥
大小调整控制四棱锥尺寸更新_scale属性
随机位置控制四棱锥生成位置通过useRandomPosition标志切换生成逻辑

3. 位置生成算法

if (this.useRandomPosition) {const latOffset = (Math.random() - 0.5) * 0.05;const lonOffset = (Math.random() - 0.5) * 0.05;const height = Math.random() * 200 + 50;position = Cesium.Cartesian3.fromDegrees(116.39 + lonOffset,39.9 + latOffset,height);
} else {const offset = this.primitives.length * 0.01;position = Cesium.Cartesian3.fromDegrees(116.39 + offset,39.9,100);
}

这种位置生成逻辑提供了两种模式:随机位置(更自然的分布)和线性排列(便于观察)。

六、架构设计:模块化与可维护性

本案例采用了清晰的架构设计,确保代码的可维护性和扩展性:

  1. TetrahedronManager:管理所有四棱锥实例,提供统一的接口
  2. TetrahedronPrimitive:自定义几何体类,封装渲染和动画逻辑
  3. 控制面板:与管理器交互,更新状态

这种分层设计使代码结构清晰,便于后续扩展。例如,添加新的几何体类型只需实现新的Primitive类,而无需修改主控制逻辑。

七、代码全景

<!DOCTYPE html>
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Cesium 倒立四棱锥场景</title><style>* {margin: 0;padding: 0;box-sizing: border-box;}body,html {width: 100%;height: 100%;overflow: hidden;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;background: #000;}#cesiumContainer {width: 100%;height: 100%;}.cesium-widget-credits,.cesium-viewer-toolbar {display: none !important;}.control-panel {position: fixed;top: 20px;left: 20px;background: rgba(25, 25, 35, 0.9);border-radius: 12px;padding: 18px;box-shadow: 0 8px 25px rgba(0, 0, 0, 0.6);color: white;min-width: 280px;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.15);z-index: 1000;transition: all 0.3s ease;}.panel-header {display: flex;justify-content: space-between;align-items: center;margin-bottom: 18px;padding-bottom: 12px;border-bottom: 1px solid rgba(255, 255, 255, 0.2);}.panel-title {font-size: 1.3rem;font-weight: 600;color: #6aabf2;}.panel-toggle {background: rgba(255, 255, 255, 0.1);border: none;color: white;font-size: 1.2rem;cursor: pointer;padding: 6px 10px;border-radius: 6px;transition: background 0.3s;}.panel-toggle:hover {background: rgba(255, 255, 255, 0.2);}.control-group {margin-bottom: 18px;}.control-label {display: block;margin-bottom: 10px;font-size: 0.95rem;color: #ccc;font-weight: 500;}.button-group {display: grid;grid-template-columns: 1fr 1fr;gap: 12px;margin-bottom: 18px;}button {background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);border: none;color: white;padding: 12px 16px;border-radius: 8px;cursor: pointer;font-size: 0.95rem;font-weight: 500;transition: all 0.3s ease;box-shadow: 0 4px 10px rgba(0, 0, 0, 0.3);}button:hover {transform: translateY(-3px);box-shadow: 0 6px 15px rgba(0, 0, 0, 0.4);}button:active {transform: translateY(0);}button:disabled {background: #555;cursor: not-allowed;transform: none;box-shadow: none;}.remove-btn {background: linear-gradient(135deg, #ff416c 0%, #ff4b2b 100%);}.clear-btn {background: linear-gradient(135deg, #ff9a00 0%, #ff5e00 100%);}.color-picker {display: flex;gap: 10px;flex-wrap: wrap;}.color-option {width: 28px;height: 28px;border-radius: 50%;cursor: pointer;border: 2px solid transparent;transition: transform 0.2s, border-color 0.2s;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);}.color-option:hover {transform: scale(1.2);}.color-option.active {border-color: white;transform: scale(1.2);}.stats {position: fixed;bottom: 20px;left: 20px;background: rgba(25, 25, 35, 0.9);color: white;padding: 12px 18px;border-radius: 10px;font-size: 0.95rem;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.15);box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);}.slider-container {display: flex;align-items: center;gap: 12px;}.slider-value {min-width: 45px;text-align: right;font-weight: 500;}input[type="range"] {flex: 1;height: 6px;border-radius: 5px;background: #444;outline: none;-webkit-appearance: none;}input[type="range"]::-webkit-slider-thumb {-webkit-appearance: none;width: 20px;height: 20px;border-radius: 50%;background: #6aabf2;cursor: pointer;box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);}.toggle-switch {position: relative;display: inline-block;width: 54px;height: 28px;}.toggle-switch input {opacity: 0;width: 0;height: 0;}.toggle-slider {position: absolute;cursor: pointer;top: 0;left: 0;right: 0;bottom: 0;background-color: #555;transition: .4s;border-radius: 28px;}.toggle-slider:before {position: absolute;content: "";height: 20px;width: 20px;left: 4px;bottom: 4px;background-color: white;transition: .4s;border-radius: 50%;}input:checked+.toggle-slider {background-color: #6aabf2;}input:checked+.toggle-slider:before {transform: translateX(26px);}.panel-collapsed .panel-content {display: none;}.info-box {position: fixed;top: 20px;right: 20px;background: rgba(25, 25, 35, 0.9);color: white;padding: 18px;border-radius: 12px;max-width: 320px;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.15);box-shadow: 0 8px 25px rgba(0, 0, 0, 0.6);}.info-title {font-size: 1.2rem;margin-bottom: 12px;color: #6aabf2;font-weight: 600;}.info-box p {margin-bottom: 8px;line-height: 1.5;color: #ddd;}.camera-controls {position: fixed;bottom: 80px;left: 20px;background: rgba(25, 25, 35, 0.9);color: white;padding: 12px;border-radius: 10px;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.15);box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);}.camera-controls button {padding: 8px 12px;margin: 5px;font-size: 0.9rem;}.coordinates {position: fixed;bottom: 20px;right: 20px;background: rgba(25, 25, 35, 0.9);color: white;padding: 12px 18px;border-radius: 10px;font-size: 0.9rem;backdrop-filter: blur(10px);border: 1px solid rgba(255, 255, 255, 0.15);box-shadow: 0 4px 15px rgba(0, 0, 0, 0.4);}</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">
</head><body><div id="cesiumContainer"></div><div class="control-panel" id="controlPanel"><div class="panel-header"><div class="panel-title">倒立四棱锥控制器</div><button class="panel-toggle" id="togglePanel">−</button></div><div class="panel-content"><div class="button-group"><button id="addTetrahedron">添加倒立四棱锥</button><button id="removeTetrahedron" class="remove-btn">移除四棱锥</button></div><button id="clearAll" class="clear-btn" style="width: 100%; margin-bottom: 18px;">清除所有</button><div class="control-group"><div class="control-label">四棱锥颜色</div><div class="color-picker"><div class="color-option active" style="background-color: #ff3b30;" data-color="#ff3b30"></div><div class="color-option" style="background-color: #4cd964;" data-color="#4cd964"></div><div class="color-option" style="background-color: #007aff;" data-color="#007aff"></div><div class="color-option" style="background-color: #ffcc00;" data-color="#ffcc00"></div><div class="color-option" style="background-color: #ff9500;" data-color="#ff9500"></div><div class="color-option" style="background-color: #8e8e93;" data-color="#8e8e93"></div></div></div><div class="control-group"><div class="control-label">动画速度</div><div class="slider-container"><input type="range" id="speedSlider" min="0.1" max="2" step="0.1" value="1"><span class="slider-value" id="speedValue">1.0</span></div></div><div class="control-group"><div class="control-label">四棱锥大小</div><div class="slider-container"><input type="range" id="sizeSlider" min="5" max="30" step="1" value="15"><span class="slider-value" id="sizeValue">15</span></div></div><div class="control-group"><div class="control-label">随机位置</div><label class="toggle-switch"><input type="checkbox" id="randomPosition" checked><span class="toggle-slider"></span></label></div></div></div><div class="stats" id="stats">倒立四棱锥数量: <span id="tetraCount">0</span></div><div class="camera-controls"><button id="resetView">重置视角</button><button id="viewAll">查看全部</button></div><div class="coordinates" id="coordinates">经度: --<br>纬度: --<br>高度: --</div><div class="info-box"><div class="info-title">操作说明</div><p>• 点击"添加倒立四棱锥"按钮添加新的倒立四棱锥</p><p>• 点击"移除四棱锥"按钮移除最后一个四棱锥</p><p>• 使用颜色选择器更改四棱锥颜色</p><p>• 调整动画速度和四棱锥大小</p><p>• 启用/禁用随机位置生成</p></div><script>// 使用您的Cesium Ion令牌Cesium.Ion.defaultAccessToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiIzZDg3N2ZiZC05MzBiLTRmZmYtYTU1Yy1kOGNiZWU4ZDU4Y2IiLCJpZCI6MTQ0MDksInNjb3BlcyI6WyJhc2wiLCJhc3IiLCJhc3ciLCJnYyJdLCJpYXQiOjE1NjcyNTQ1NDh9.qCtL6_oYhN3VErce9lAiIZCXevo3kPOE4YKD7IDuGiY'// 初始化Cesium Viewerconst viewer = new Cesium.Viewer('cesiumContainer', {baseLayerPicker: false,shouldAnimate: true,infoBox: false,animation: false,timeline: false,fullscreenButton: false,terrainProvider: Cesium.createWorldTerrain({requestWaterMask: true,requestVertexNormals: true}),selectionIndicator: false,});// 隐藏版权信息viewer._cesiumWidget._creditContainer.style.display = "none";// 设置初始相机位置viewer.camera.setView({destination: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 1000),orientation: {heading: 0,pitch: -0.5,roll: 0}});// 四棱锥管理class TetrahedronManager {constructor() {this.primitives = [];this.selectedColor = Cesium.Color.fromCssColorString('#ff3b30');this.animationSpeed = 1.0;this.size = 15;this.useRandomPosition = true;// 使用经纬度坐标this.basePosition = Cesium.Cartesian3.fromDegrees(116.39, 39.9, 0);}addTetrahedron() {// 计算位置let position;if (this.useRandomPosition) {// 在基础位置周围随机生成位置const latOffset = (Math.random() - 0.5) * 0.05;const lonOffset = (Math.random() - 0.5) * 0.05;const height = Math.random() * 200 + 50;position = Cesium.Cartesian3.fromDegrees(116.39 + lonOffset,39.9 + latOffset,height);} else {// 线性排列const offset = this.primitives.length * 0.01;position = Cesium.Cartesian3.fromDegrees(116.39 + offset,39.9,100);}// 创建四棱锥const primitive = new TetrahedronPrimitive({position: position,color: this.selectedColor,scale: new Cesium.Cartesian3(this.size, this.size, this.size * 1.5),speed: this.animationSpeed});// 添加到场景viewer.scene.primitives.add(primitive);primitive.startAnimate();// 保存引用this.primitives.push(primitive);// 更新统计信息this.updateStats();// 如果是第一个四棱锥,飞行到它if (this.primitives.length === 1) {viewer.flyTo(primitive);}return primitive;}removeTetrahedron() {if (this.primitives.length === 0) return;// 获取最后一个四棱锥const primitive = this.primitives.pop();// 停止动画primitive.closeAnimate();// 从场景中移除viewer.scene.primitives.remove(primitive);// 更新统计信息this.updateStats();}clearAll() {while (this.primitives.length > 0) {this.removeTetrahedron();}}updateStats() {document.getElementById('tetraCount').textContent = this.primitives.length;}setColor(color) {this.selectedColor = color;}setAnimationSpeed(speed) {this.animationSpeed = speed;// 更新所有四棱锥的速度this.primitives.forEach(primitive => {primitive.setSpeed(speed);});}setSize(size) {this.size = size;}setRandomPosition(enabled) {this.useRandomPosition = enabled;}viewAll() {if (this.primitives.length === 0) return;// 计算所有四棱锥的边界const positions = this.primitives.map(p => p._localPosition);const boundingSphere = Cesium.BoundingSphere.fromPoints(positions);// 飞行到边界球viewer.camera.viewBoundingSphere(boundingSphere,new Cesium.HeadingPitchRange(0, -0.5, boundingSphere.radius * 3));}}// 创建管理器实例const tetraManager = new TetrahedronManager();// 设置UI事件监听document.getElementById('addTetrahedron').addEventListener('click', () => {tetraManager.addTetrahedron();});document.getElementById('removeTetrahedron').addEventListener('click', () => {tetraManager.removeTetrahedron();});document.getElementById('clearAll').addEventListener('click', () => {tetraManager.clearAll();});// 颜色选择document.querySelectorAll('.color-option').forEach(option => {option.addEventListener('click', function () {// 移除所有active类document.querySelectorAll('.color-option').forEach(el => {el.classList.remove('active');});// 添加active类到当前选项this.classList.add('active');// 设置颜色const color = Cesium.Color.fromCssColorString(this.dataset.color);tetraManager.setColor(color);});});// 动画速度滑块const speedSlider = document.getElementById('speedSlider');const speedValue = document.getElementById('speedValue');speedSlider.addEventListener('input', function () {const value = parseFloat(this.value);speedValue.textContent = value.toFixed(1);tetraManager.setAnimationSpeed(value);});// 大小滑块const sizeSlider = document.getElementById('sizeSlider');const sizeValue = document.getElementById('sizeValue');sizeSlider.addEventListener('input', function () {const value = parseInt(this.value);sizeValue.textContent = value;tetraManager.setSize(value);});// 随机位置开关const randomPosition = document.getElementById('randomPosition');randomPosition.addEventListener('change', function () {tetraManager.setRandomPosition(this.checked);});// 面板折叠/展开document.getElementById('togglePanel').addEventListener('click', function () {const panel = document.getElementById('controlPanel');panel.classList.toggle('panel-collapsed');this.textContent = panel.classList.contains('panel-collapsed') ? '+' : '−';});// 相机控制document.getElementById('resetView').addEventListener('click', function () {viewer.camera.setView({destination: Cesium.Cartesian3.fromDegrees(116.39, 39.9, 1000),orientation: {heading: 0,pitch: -0.5,roll: 0}});});document.getElementById('viewAll').addEventListener('click', function () {tetraManager.viewAll();});// 更新坐标显示function updateCoordinates() {const cartesian = viewer.camera.position;const cartographic = Cesium.Cartographic.fromCartesian(cartesian);const longitude = Cesium.Math.toDegrees(cartographic.longitude).toFixed(5);const latitude = Cesium.Math.toDegrees(cartographic.latitude).toFixed(5);const height = cartographic.height.toFixed(2);document.getElementById('coordinates').innerHTML =`经度: ${longitude}<br>纬度: ${latitude}<br>高度: ${height}`;}viewer.scene.postRender.addEventListener(updateCoordinates);// 添加一些初始四棱锥setTimeout(() => {tetraManager.addTetrahedron();tetraManager.addTetrahedron();tetraManager.addTetrahedron();}, 1000);// 重新设计四棱锥几何结构,添加边线function TetrahedronPrimitive(options) {this.show = true;this._faceCommand = undefined;this._edgeCommand = undefined;this._enuMatrix = undefined;this._scaleMatrix = undefined;this._localPosition = options.position;this._createFaceCommand = createFaceCommand;this._createEdgeCommand = createEdgeCommand;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, 1.0));this._edgeColor = new Cesium.Color(0.0, 0.0, 0.0, 1.0); // 黑色边线this._scale = Cesium.defaultValue(options.scale, new Cesium.Cartesian3(10, 10, 15));this._texture = undefined;// 计算模型矩阵this._modelMatrix = computeModelMatrix(this);this._height = computeHeight(this);}TetrahedronPrimitive.prototype.update = function (frameState) {if (!this.show) {return;}// 创建面命令if (!Cesium.defined(this._faceCommand)) {this._faceCommand = this._createFaceCommand(frameState.context, this);this._faceCommand.pickId = 'v_pickColor';}// 创建边线命令if (!Cesium.defined(this._edgeCommand)) {this._edgeCommand = this._createEdgeCommand(frameState.context, this);this._edgeCommand.pickId = 'v_pickColor';}// 添加到渲染列表if (Cesium.defined(this._faceCommand)) {frameState.commandList.push(this._faceCommand);}if (Cesium.defined(this._edgeCommand)) {frameState.commandList.push(this._edgeCommand);}};TetrahedronPrimitive.prototype.isDestroyed = function () {return false;};TetrahedronPrimitive.prototype.destroy = function () {if (Cesium.defined(this._faceCommand)) {this._faceCommand.shaderProgram = this._faceCommand.shaderProgram && this._faceCommand.shaderProgram.destroy();}if (Cesium.defined(this._edgeCommand)) {this._edgeCommand.shaderProgram = this._edgeCommand.shaderProgram && this._edgeCommand.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 * that._speed;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);};// 设置动画速度TetrahedronPrimitive.prototype.setSpeed = function (speed) {this._speed = speed;};// 创建面的绘制命令function createFaceCommand(context, tetrahedronPrimitive) {var translucent = false;var closed = true;var vs = createFaceVertexShader();var fs = createFaceFragmentShader();var rawRenderState = Cesium.Appearance.getDefaultRenderState(translucent, closed, undefined);// 启用背面剔除,确保棱角分明rawRenderState.cull = {enabled: true};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;}};let attributeLocations = {position: 0,textureCoordinates: 1};var shaderProgram = Cesium.ShaderProgram.fromCache({context: context,vertexShaderSource: vertexShaderSource,fragmentShaderSource: fragmentShaderSource,attributeLocations: attributeLocations});return new Cesium.DrawCommand({vertexArray: createFaceVertexArray(context),primitiveType: Cesium.PrimitiveType.TRIANGLES,renderState: renderState,shaderProgram: shaderProgram,uniformMap: uniformMap,owner: this,pass: Cesium.Pass.TRANSLUCENT,modelMatrix: tetrahedronPrimitive._modelMatrix,});}// 创建边线的绘制命令function createEdgeCommand(context, tetrahedronPrimitive) {var translucent = false;var closed = true;var vs = createEdgeVertexShader();var fs = createEdgeFragmentShader();var rawRenderState = Cesium.Appearance.getDefaultRenderState(translucent, closed, undefined);// 禁用背面剔除,确保边线可见rawRenderState.cull = {enabled: false};// 设置线宽rawRenderState.lineWidth = 2.0;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._edgeColor;}};let attributeLocations = {position: 0};var shaderProgram = Cesium.ShaderProgram.fromCache({context: context,vertexShaderSource: vertexShaderSource,fragmentShaderSource: fragmentShaderSource,attributeLocations: attributeLocations});return new Cesium.DrawCommand({vertexArray: createEdgeVertexArray(context),primitiveType: Cesium.PrimitiveType.LINES,renderState: renderState,shaderProgram: shaderProgram,uniformMap: uniformMap,owner: this,pass: Cesium.Pass.TRANSLUCENT,modelMatrix: tetrahedronPrimitive._modelMatrix,});}function createFaceVertexArray(context) {let attributeLocations = {position: 0,textureCoordinates: 1};var positionsAndIndice = createFacePositionsAndIndice();var geometry = new Cesium.Geometry({attributes: {position: new Cesium.GeometryAttribute({componentDatatype: Cesium.ComponentDatatype.FLOAT,componentsPerAttribute: 3,values: positionsAndIndice.positions}),textureCoordinates: new Cesium.GeometryAttribute({componentDatatype: Cesium.ComponentDatatype.FLOAT,componentsPerAttribute: 2,values: positionsAndIndice.sts}),},indices: positionsAndIndice.indices,primitiveType: Cesium.PrimitiveType.TRIANGLES,boundingSphere: Cesium.BoundingSphere.fromVertices(positionsAndIndice.positions)});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 createEdgeVertexArray(context) {let attributeLocations = {position: 0};var positionsAndIndice = createEdgePositionsAndIndice();var geometry = new Cesium.Geometry({attributes: {position: new Cesium.GeometryAttribute({componentDatatype: Cesium.ComponentDatatype.FLOAT,componentsPerAttribute: 3,values: positionsAndIndice.positions})},indices: positionsAndIndice.indices,primitiveType: Cesium.PrimitiveType.LINES,boundingSphere: Cesium.BoundingSphere.fromVertices(positionsAndIndice.positions)});var vertexArray = Cesium.VertexArray.fromGeometry({context: context,geometry: geometry,attributeLocations: attributeLocations,bufferUsage: Cesium.BufferUsage.STATIC_DRAW,});return vertexArray;}// 创建面的顶点和索引function createFacePositionsAndIndice() {// 定义5个顶点:4个底部顶点 + 1个顶部顶点var positions = new Float64Array(5 * 3);// 底部顶点 (z = 0) - 平的底部// 顶点0: 前右positions[0] = 1.0;positions[1] = 1.0;positions[2] = 0.0;// 顶点1: 前左positions[3] = -1.0;positions[4] = 1.0;positions[5] = 0.0;// 顶点2: 后左positions[6] = -1.0;positions[7] = -1.0;positions[8] = 0.0;// 顶点3: 后右positions[9] = 1.0;positions[10] = -1.0;positions[11] = 0.0;// 顶点4: 顶部顶点 (尖朝上)positions[12] = 0.0;positions[13] = 0.0;positions[14] = 2.0;// 定义三角形索引 - 确保所有面都定义// 6个三角形:4个侧面 + 2个底部三角形var indices = new Uint16Array(6 * 3);// 侧面1: 前面 (顶点0, 1, 4)indices[0] = 0;indices[1] = 1;indices[2] = 4;// 侧面2: 左面 (顶点1, 2, 4)indices[3] = 1;indices[4] = 2;indices[5] = 4;// 侧面3: 后面 (顶点2, 3, 4)indices[6] = 2;indices[7] = 3;indices[8] = 4;// 侧面4: 右面 (顶点3, 0, 4)indices[9] = 3;indices[10] = 0;indices[11] = 4;// 底部1: 三角形1 (顶点0, 3, 2)indices[12] = 0;indices[13] = 3;indices[14] = 2;// 底部2: 三角形2 (顶点0, 2, 1)indices[15] = 0;indices[16] = 2;indices[17] = 1;// 纹理坐标var sts = new Float32Array([// 前面0.0, 0.0,1.0, 0.0,0.5, 1.0,// 左面0.0, 0.0,1.0, 0.0,0.5, 1.0,// 后面0.0, 0.0,1.0, 0.0,0.5, 1.0,// 右面0.0, 0.0,1.0, 0.0,0.5, 1.0,// 底部10.0, 0.0,1.0, 0.0,1.0, 1.0,// 底部20.0, 0.0,1.0, 1.0,0.0, 1.0]);return {indices: indices,positions: positions,sts: sts};}// 创建边线的顶点和索引function createEdgePositionsAndIndice() {// 使用与面相同的顶点位置var positions = new Float64Array(5 * 3);// 底部顶点 (z = 0) - 平的底部// 顶点0: 前右positions[0] = 1.0;positions[1] = 1.0;positions[2] = 0.0;// 顶点1: 前左positions[3] = -1.0;positions[4] = 1.0;positions[5] = 0.0;// 顶点2: 后左positions[6] = -1.0;positions[7] = -1.0;positions[8] = 0.0;// 顶点3: 后右positions[9] = 1.0;positions[10] = -1.0;positions[11] = 0.0;// 顶点4: 顶部顶点 (尖朝上)positions[12] = 0.0;positions[13] = 0.0;positions[14] = 2.0;// 定义边线索引 - 8条边var indices = new Uint16Array(8 * 2);// 底部边线indices[0] = 0;indices[1] = 1;indices[2] = 1;indices[3] = 2;indices[4] = 2;indices[5] = 3;indices[6] = 3;indices[7] = 0;// 侧边线indices[8] = 0;indices[9] = 4;indices[10] = 1;indices[11] = 4;indices[12] = 2;indices[13] = 4;indices[14] = 3;indices[15] = 4;return {indices: indices,positions: positions};}function createFaceVertexShader() {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 coordinatesv_normalEC = czm_normal * normal;                               // normal in eye coordinatesv_st = st;//v_pickColor = czm_batchTable_pickColor(batchId);gl_Position = czm_modelViewProjection * vec4(position, 1.0);}`;return vertexShader;}function createFaceFragmentShader() {var fragmentShader =`varying vec3 v_positionEC;varying vec3 v_normalEC;varying vec2 v_st;uniform vec4 color;varying vec4 v_pickColor;void main(){vec3 positionToEyeEC = -v_positionEC;vec3 normalEC = normalize(v_normalEC);// 使用正确的法线计算光照,确保棱角分明czm_materialInput materialInput;materialInput.normalEC = normalEC;materialInput.positionToEyeEC = positionToEyeEC;materialInput.st = v_st;czm_material material = czm_getDefaultMaterial(materialInput);// 使用Phong光照模型,确保棱角分明material.alpha = color.a;material.diffuse = color.rgb;material.specular = 0.5;material.shininess = 10.0;gl_FragColor = czm_phong(normalize(positionToEyeEC), material, czm_lightDirectionEC);}`;return fragmentShader;}function createEdgeVertexShader() {var vertexShader =`attribute vec3 position;void main(){gl_Position = czm_modelViewProjection * vec4(position, 1.0);}`;return vertexShader;}function createEdgeFragmentShader() {var fragmentShader =`uniform vec4 color;void main(){gl_FragColor = color;}`;return fragmentShader;}function computeModelMatrix(tetrahedronPrimitive) {let enuMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(tetrahedronPrimitive._localPosition);let scaleMatrix = Cesium.Matrix4.fromScale(tetrahedronPrimitive._scale);// 创建绕X轴旋转180度的矩阵(使四棱锥倒立)let rotationX = Cesium.Matrix4.fromRotationTranslation(Cesium.Matrix3.fromRotationX(Cesium.Math.toRadians(180)));// 先缩放,再旋转,最后定位let modelMatrix = Cesium.Matrix4.multiply(scaleMatrix, rotationX, new Cesium.Matrix4());modelMatrix = Cesium.Matrix4.multiply(enuMatrix, modelMatrix, 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;}</script>
</body></html>

http://www.dtcms.com/a/524390.html

相关文章:

  • 从传统架构到云原生,如何应对数据增长挑战?
  • Extreme Views 的3DGS!
  • 南京网站开发哪家好如何在自己做的网站中顶置内容
  • LeetCode 面试经典 150_链表_随机链表的复制(59_138_C++_中等)
  • WPS 365政务版亮相2025数博会,AI生成公文可用度达90%
  • 判断网站是否被k校园类网站模板
  • wordpress删除站点怎样给建设的网站提意见
  • Zabbix Agent 安装
  • RTX5060TI 安装C++版本的onnxruntime(GPU版本)
  • MCP(trae)+ IDA-提高干活效率
  • Spring Boot微服务健康检测:保障系统稳定性的关键实践
  • 利用Java API与HDFS进行交互
  • Linux 中修改 IP 地址为 静态 IP 地址
  • 一团网站建设个人网页设计作业
  • 做网站商城赔了8万卢松松网站源码
  • 界面控件Kendo UI for Angular 2025 Q3亮点 - 全新的AI编码助手
  • 鸿蒙HarmonyOS ArkUI 状态管理装饰器详解
  • 旅游景点网站建设防疫测温健康码核验一体机
  • 一套试卷——数据结构(2020数据结构B)
  • 性能测试之性能监控详解
  • 阿里云国际站GPU:怎么通过通过VNC连接实例?
  • Elasticsearch 实现类 GitHub 关键词搜索与高亮列表展示
  • 怎么查看网站外链效果宁波seo推广哪家好
  • C#窗体实现自定义数字控件
  • ComposeView杂记(持续更新)
  • Redis的有序集合的底层实现
  • 海康威视云台相机的python sdk使用(云台控制)
  • REST 表征状态转移
  • React 04
  • 深度学习常用优化器解析