站内搜索

输入关键词开始搜索

Cesium 无人机管道航道:Tube 几何与菲涅尔流光材质

文章目录

无人机航道案例把离散航点连接成空心透明管道,并叠加边缘辉光和沿路径移动的流光。正常、告警和阻断状态使用不同颜色表达。

三条不同状态的无人机管道航道与实时参数面板

实现思路导图

flowchart TD
  ROOT[无人机三维航道]
  ROOT --> PATH[路径准备]
  PATH --> ENU[转为 ENU 局部坐标]
  PATH --> DENSE[插值加密与累计距离]
  ROOT --> FRAME[并行移动标架]
  ROOT --> TUBE[管状网格]
  TUBE --> RING[圆形截面环]
  TUBE --> INDEX[环间三角形与 UV]
  ROOT --> MATERIAL[流光材质与告警状态]
  ROOT --> DRONE[无人机等速动画]

数据对象不依赖 Cesium UI

CorridorData 保存航点、半径、分段数、透明度、流速和状态。页面先创建三个示例,再交给 CorridorManager:

const corridor = CorridorData.createDefault(options);
corridor.applyStatusColor();
manager.add(corridor);

这种数据对象可以序列化,也便于把业务状态和 Cesium Primitive 分开。

从中心线生成 Tube

离散航点先平滑插值。对中心线每个采样点计算切线,再建立与切线垂直的局部截面。圆周上的相邻点和前后截面连接成三角形。

管道分段数控制截面圆滑度,路径细分数控制弯道平滑度。两者相乘决定顶点量,不能为了视觉平滑无限提高。

const geometry = TubeGeometryBuilder.build(corridorData);
if (!geometry) return;

材质表达空心、辉光和流动

片元 Shader 使用视线方向与法线夹角计算 Fresnel,靠近轮廓的片元更亮。流光坐标来自沿路径方向的 UV:

float rim = pow(1.0 - abs(dot(normal, viewDir)), glowPower);
float flow = fract(v_st.x * flowDensity - time * flowSpeed);

hollowness 降低管道正面中心的不透明度,让航道看起来像能穿行的空间,而不是实心圆柱。

选择与状态切换

Primitive 使用航道 id 作为拾取标识。左键拾取后,面板展示该航道的快照,修改参数再由 Manager 更新几何或 uniform。

半径、截面数和路径平滑会改变几何,需要重建;颜色、透明度、流速和方向可以只更新材质。把两类参数分开能避免拖动滑块时持续重建网格。

路径坐标

航点以经纬度和绝对高度保存。长距离航道需要注意局部坐标基准和地球曲率;本案例是城市尺度,逐点转换为 ECEF 后建立截面即可满足展示。

实现主线是:航点数据 → 平滑中心线 → Tube 三角网 → 菲涅尔与流光材质 → Manager 管理选择、状态和生命周期。

截图中的三条航道表达三种状态

蓝色是正常主航道,黄色是告警航道,红色是阻断航道。白色点是中间航点,绿色和红色端点分别表示起点、终点。截图来自示例数据初始化完成后,不需要用户再绘制才能理解结果。

面板修改的是当前选中航道。点击管道时,Cesium 拾取返回 Primitive 的 id,Manager 再用 id 找到 CorridorData,不会从渲染几何反推业务参数。

平滑中心线怎样避免拐角扭曲

原始航点数量少,直接连接会形成折线。案例先在相邻航点间插值,再为每个中心点估计切线。截面需要一组连续的法线、副法线基;如果每个点都独立用世界 Z 轴叉乘,切线接近 Z 轴时截面会突然翻转。

稳定做法是从前一截面传播方向,必要时使用最小旋转更新局部标架。闭合路径还要在首尾之间分配累积扭转,否则接缝处会出现明显旋转跳变。

Tube 顶点包含哪些属性

每个截面有 tubeSegments 个圆周点。相邻截面连接后,顶点至少需要:

  • position:管道表面位置;
  • normal:用于 Fresnel 和光照;
  • st:横向圆周坐标与沿路径累计坐标;
  • 可选的路径进度或状态属性。

沿路径的 UV 应按累计距离归一化,而不是按采样点索引。这样不同密度的平滑点不会让流光忽快忽慢。

空心效果不是删除正面片元

hollowness 根据法线与视线夹角降低正面 alpha,轮廓处仍保持可见。它产生“透明管道”的视觉,但几何仍是一层外壳,没有真实内壁。

若要进入管道内部观察,需要关闭背面剔除或创建内壁,并正确处理深度排序。当前案例使用透明混合且 depthMask: false,多条航道交叉时仍可能出现顺序相关的透明问题。

哪些修改需要重建 Primitive

航点、半径、圆周分段和路径平滑会改变顶点,CorridorRenderer.update 会移除旧 Primitive 并重建。颜色、流速、流向、密度和亮度只改变 uniforms,可保留几何。

状态切换同时改变业务 status 和配色。隐藏航道会从 Manager 的可见集合中移除渲染,但数据对象仍保留,重新显示时可以重建。

时间如何驱动流光

渲染器在场景更新时累加秒数并写入 u_time。流光相位是路径 UV、密度、方向和时间的组合。使用真实 deltaTime 能让 60Hz 与 144Hz 屏幕看到相同流速。

页面卸载时必须解除场景监听。否则 u_time 更新闭包会继续引用已经移除的 Appearance 和 Viewer。

从零复现:无人机三维航道教程

案例源码位于 src/demos/cesiumdemos/uav-corridor/。管道几何的难点不是画圆,而是让圆形截面沿弯曲路径连续旋转而不突然翻面。

第 1 步:准备航线与局部坐标

将经纬高控制点转换为 Cartesian3,再以首点或路径中心建立 ENU 参考系,把全部点转为局部米制坐标。

const localPoint = Cesium.Matrix4.multiplyByPoint(
  inverseFrame,
  worldPoint,
  new Cesium.Cartesian3(),
);

完成标志:局部路径长度与 Cesium 测距接近,高度差保留,坐标量级不再是数百万米。

第 2 步:加密路径

按最大分段长度对相邻控制点插值。弯曲航线可以使用 Catmull-Rom,但必须限制过冲,避免曲线穿过地面或越过禁飞区。

每个采样点保存累计距离。后续 UV、无人机动画和流光速度都使用这条统一距离轴。

第 3 步:构建并行移动标架

首点根据切线选择一个不平行的参考向量,叉乘得到法线与副法线。后续点把上一帧标架绕相邻切线的最小旋转传递过去。

tangent[i]   = normalize(path[i+1] - path[i-1])
normal[i]    = transport(normal[i-1], tangent[i-1], tangent[i])
binormal[i]  = normalize(tangent[i] × normal[i])

不要每个点都用世界 Z 轴重新叉乘。路径接近竖直时会退化,并产生管道截面突然翻转。

第 4 步:生成截面环

对每个路径点按 radialSegments 生成圆环:

vertex =
  center +
  normal * Math.cos(angle) * radius +
  binormal * Math.sin(angle) * radius;

相邻两个环按索引连接成四边形,再拆成两个三角形。UV 的 U 使用圆周比例,V 使用累计距离比例。

第 5 步:制作半透明流动材质

片元 Shader 根据 V 坐标、密度、方向和 u_time 生成移动亮带。状态颜色、透明度和速度使用 uniform,管径和径向分段属于几何参数。

深度测试保持开启,透明度小于 1 时通常关闭深度写入,并按相机距离或明确 render order 处理多条航道排序。

第 6 步:让无人机沿统一距离轴运动

用速度乘时间得到路径距离,二分查找所在分段并插值位置。航向使用当前切线,姿态再叠加机体轴向修正。

const distance = (elapsed * speed) % totalLength;
const segment = findSegmentByDistance(distance);

这样路径几何、流光和无人机速度都以米为基准,不会因某段控制点更密而忽快忽慢。

第 7 步:区分更新成本

颜色、透明度、流速只改 uniform;管径和截面分段重建几何;控制点变化要重新加密、计算标架、构建网格和累计距离。面板使用 change 与 finishChange 区分实时预览和昂贵重建。

验收与排错

  • 管道在某个弯道扭转 180°:检查并行移动标架。
  • 无人机速度忽快忽慢:动画应按累计距离而不是点索引插值。
  • 半透明管道互相遮挡错误:检查深度写入和透明排序。
  • 路径修改后流光断裂:重新计算连续 UV 与总长度。

相关文章

Cesium FFT 矩形水面:GPU 频谱海浪与随波船体

在 Cesium Primitive 中接入多级联 FFT 海浪,完成位移、法线、清澈度、LOD 网格和船体姿态采样

Cesium 体积火焰:光线步进、噪声与点线面绘制

使用自定义 Primitive 绘制可调体积火焰,并扩展区域实例、火花、动态光照和自动质量控制