恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
OSGB转3DTiles实战:从倾斜摄影数据到Web三维可视化的完整流程与踩坑指南
首页
资讯中心
/
OSGB转3DTiles实战:从倾斜摄影数据到Web三维可视化的完整流程与踩坑指南
OSGB转3DTiles实战:从倾斜摄影数据到Web三维可视化的完整流程与踩坑指南
发布时间:2026/10/3 10:47:08
项目标题里的“OSGB转3DTiles”实际上是在说一件事怎么把倾斜摄影生产出来的那堆OSGB原始成果变成能直接在Web端让浏览器流畅加载的3DTiles格式。这个问题我前前后后折腾了不下十趟从一开始被各种报错折磨到怀疑人生到现在基本能做到“来什么数据都能在半小时内转完”中间踩过的坑确实不少。这篇文章不打算做什么全面的格式科普就围绕转换这件事把我在项目里的完整处理流程、工具选型思路、参数设置细节和踩坑记录统统摊开讲希望能让正准备接手这套流程的同行少走几步冤枉路。不管你是刚从ContextCapture或者大疆智图那边导出一堆OSGB正愁怎么传到Web上给客户看还是已经在用Cesium加载发现帧率卡得上天想优化一下数据或者只是想搞清楚“单体化”“shp转3dtiles”这些词到底跟转换有什么关系——这篇文章应该都能给你一个比较清楚的答案和可以直接照做的方案。我会尽量把原理和实操揉在一起讲因为只给步骤不给原理下次换个软件版本、换个数据源你可能又会被卡住。反过来只讲原理不给步骤也没有实战参考价值。1. 为什么非要转成3DTiles两种格式的真正差异很多刚接触倾斜摄影的人会问OSGB明明是生产软件的标准输出格式Cesium也不是不能加载为什么还要多此一举转成3DTiles这个问题我第一次接触时也很困惑后来实际跑了几个项目才彻底明白两者根本不是简单的“格式不同”而是面向完全不同的使用场景。1.1 OSGB是给本地工具用的3DTiles是给Web端用的OSGB说白了是OpenSceneGraph这套图形引擎的二进制格式它天生就为桌面端本地渲染设计。倾斜摄影生产软件如ContextCapture、大疆智图、PhotoMesh在空三解算和模型构建完成后默认输出的就是这个格式。它的特点是保留完整的多级金字塔LOD结构每一级分块组织本地文件系统访问时非常高效。但问题在于如果直接在Cesium这种Web引擎里加载OSGB要么得装插件要么得通过代理转换而且Web端的网络传输和内存管理跟本地完全不是一回事原始OSGB的目录结构和索引方式在HTTP环境下效率太低了。3DTiles则是Cesium团队主导设计的开放规范它在诞生之初就是面向Web端海量三维数据的流式加载。核心思想是“按需加载”——浏览器需要哪块数据才从服务器拉取哪块而不是像本地文件那样一次性把索引和模型数据全部读入。这个差异在实际体验上是非常明显的同样的一个县城级别倾斜模型直接让人在浏览器里拖OSGB文件夹大概率卡到连视角都转不动转成3DTiles之后只要服务器带宽和配置不差普通笔记本也能比较流畅地浏览。1.2 转换前后的坐标与组织方式变化除了渲染引擎的差异转换过程中还有个容易被忽略的点坐标组织和数据组织方式。OSGB自身通常带有一套本地坐标系或工程坐标系层级目录按“Tile_编号”文件夹组织金字塔层级由文件夹深度表达每个Tile里面是带LOD节点树的模型文件。而3DTiles把这种结构抽象成了tileset.json 一系列b3dm/glb瓦片文件tileset.json里用包围盒、几何误差、变换矩阵来描述瓦片的层级和空间关系浏览器由根节点开始逐级判断“该加载哪些瓦片”。所以转换不是简单的文件改名而是要做“数据重组织”把OSGB的目录层级语义翻译成3DTiles的tileset.json语义把模型几何和纹理封装进新的瓦片容器同时还要处理顶点坐标、纹理压缩、LOD简化等一系列问题。这也是为什么市面上的转换工具五花八门但出来的效果却千差万别——真正理解这套重组织逻辑的工具转换结果既小又快只是简单封装的工具转换后数据量甚至比原始OSGB还大加载也慢。后面我会讲到怎么判断一个工具“懂不懂”这个逻辑。2. 动手转换之前必须搞懂的数据素养问题转换工作开始之前建议先花点时间检查你的OSGB数据和确认各项基础信息。这一步省不了因为后续转换的参数设置、坐标系处理、结果验证都依赖这些数据。我见过不少人拿到数据就直接拖进转换工具结果转出来的模型位置跑飞十万八千里最后查了半天才发现是坐标系没理清。2.1 元数据与坐标系转换不出错的第一前提OSGB数据通常会带一个metadata.xml文件里面记录着坐标系信息、原点坐标、分块大小等内容。拿到数据先打开这个文件看一眼这是最快速摸清底细的办法。重点看几个字段空间参考系是CGCS2000还是WGS84还是地方坐标系、投影方式高斯投影带号是多少、原点坐标值OriginX/OriginY等。如果metadata.xml缺失或者信息不完整也别慌还可以从OSGB文件本身去试探。多数生产软件会在模型文件的辅助信息里写坐标或者从相邻Tile的坐标推算原点。实在不行就直接在Cesium里加载原始数据如果位置明显偏移或旋转逐步调整。但说实话这一步最好的方案还是在源头解决——如果数据是你自己生产的确保导出时坐标系选择正确后面所有环节会省心很多。这里有一个我在项目里反复强调的点3DTiles最常见的坐标系是WGS84经纬度和Web Mercator但倾斜摄影生产时往往使用高斯投影的工程坐标系。转换工具要做的事就是在这两者之间做换算和偏移矫正。如果你发现转出来的模型在Cesium里位置整体平移到了海里大概率就是坐标转换时“椭球体基准”或者“中央经线带号”设置错了。2.2 数据完整性检查清理碎块和冗余Tile倾斜摄影原始数据经常存在一些“碎片信息”航拍范围外的孤立块、飞行的碎屑噪声点、分块交接处的重复面以及在自动建模过程中产生的一些畸变模型。这些数据如果不处理就转3DTiles一方面会白白增加数据量拖慢加载效率另一方面在视觉上会出现悬浮物、异常凸起等问题特别影响交付效果。我处理的流程一般是用ContextCapture或者模方软件先大概检查一下整个项目标记出明显有问题的区域。用“裁剪”功能把有效范围框出来无效区域直接裁掉。检查有没有空洞或破面较严重的Tile如果数量不多可以接受如果太多建议重新处理原始航飞数据而不是在转换这一步硬扛。具体到操作层面如果你用的是模方软件来裁剪OSGB模型需要注意裁剪后生产出来的数据虽然范围变小了但可能会导致LOD层级重排最好确认你裁剪后的数据包含完整的LOD金字塔也就是还有多级文件夹结构而不是只导出了一个单层精模。如果只有一层转换成3DTiles后Cesium的LOD机制就无法正常调度远处看会加载巨量三角形近处又可能出现闪烁闪烁的情况。这个细节很多人踩过值得提前检查。2.3 纹理和三角面检查易被忽略的隐藏问题纹理和三角网质量是决定3DTiles表现的另一层关键因素。转换工具往往会把OSGB的纹理重新压缩成Web友好的格式如果原始纹理本身就是超大尺寸或者异常格式转换时会报错或输出体积异常。几个快速检查手段用OSGViewerOSG自带的查看器打开OSGB看纹理是否正常显示是否出现大面积黑块或拉伸。检查纹理文件大小如果一个Tile的纹理明显大于周边可能存在异常大纹理。用专业工具查看三角面数量如果某些Tile面数高得离谱通常是模型重建过程中的“网格爆炸”问题需要在生产阶段重新优化。这些检查虽然看起来费时间但跟转换到一半报错、或者转完在Web端出现各种渲染异常比起来这点时间成本是值得的。我在团队里立过一条规矩“转换之前必须做5分钟数据体检”就是这个原因。3. 转换工具选型谁才是效率与质量的平衡点市面上的OSGB转3DTiles工具不算多但真要用起来每个工具都有自己的脾气。我尝试过CesiumLab、osgb23dtiles、23dtiles开源工具以及一些基于FME的流程各有优劣。这一节我就把自己在项目里的选型对比和理由交代清楚。3.1 主流转换工具横向对比工具/方案适用场景优点明显缺点CesiumLab系列中小数据量、快速出活界面图形化参数直观支持常用坐标系输出稳定大数据量时可能内存吃紧部分功能收费osgb23dtiles开源有一定命令行基础、追求可控性免费开源转换逻辑透明可以脚本批处理无图形界面需要手动调参数对新手不友好23dtiles开源工具数据量较大、服务器部署支持分块并发转换效率高适合批量生产需要一定的部署配置能力FME等专业GIS转换平台复杂坐标与多源数据融合灵活性最强可定制流程坐标处理能力强授权成本高学习曲线陡峭小项目不划算我自己常用的组合是小数据量快速验证时用CesiumLab大批量生产时用osgb23dtiles或23dtiles的命令行方式跑。两者配合基本覆盖了绝大多数项目场景。如果你告诉我你需要“转完直接放服务器上给Cesium加载”那我会建议优先考虑开源命令行工具因为它的输出干净不依赖某个商业软件运行时环境部署到Linux服务器上很稳。3.2 免费方案和收费方案的抉择逻辑关于收费和免费的选择我的看法很朴素要看项目的时间成本和数据量。如果数据量只有一两个小区级别、时间又比较紧用CesiumLab这类图形化工具最快减少试错成本授权费相对换取的是时间收益。但如果项目是“城市级”或“县域级”的海量数据后续还有自动化更新的需求我会建议优先搭建开源方案因为命令行工具可以纳入脚本形成固定的生产流水线而不是每次打开GUI手动点一遍。开源工具通常对硬件资源利用更充分在服务器上可以并行处理多个分块。授权成本为零可以放心部署到生产环境不用考虑“换台机器需要重新激活授权”这类问题。当然免费方案不是没有代价它的代价就是你需要花时间理解参数。比如osgb23dtiles这类工具你在命令行里要指定输入目录、输出目录、图层类型、纹理质量、几何误差缩放等等每个参数都影响最终效果。但一旦你把这些参数吃透了效率会非常高。3.3 为什么我不推荐“手动改数据再转换”有些同事习惯先把OSGB导入建模软件重新导出成通用格式比如OBJ/FBX再丢给转换工具转3DTiles。我的态度很明确除非万不得已不要自找麻烦。因为倾斜摄影的OSGB最大的优势之一就是自带金字塔LOD层级。如果在中间环节转成OBJ/FBXLOD信息基本就丢了变成一个“一大坨模型”再转3DTiles时工具只能重新计算简化和LOD效果往往不如原始OSGB直接转换来得自然数据量还会变大。如果你确实只有OBJ或FBX格式的数据没有原始OSGB那也可以转3DTiles但一定要记得后续手动设置简化率、最大误差之类的参数尽量弥补丢失的LOD。不过能做好的工具不多效果相当勉强。这也就是为什么我一直强调生产端导出OSGB的时候把元数据、金字塔层级、坐标系信息都保留好是最好的“防呆”手段。4. 转换实操全流程从数据准备到最终发布前面理论铺垫差不多了这一节进入正题完整跑一遍转换流程。我以一个实际项目为例数据源是ContextCapture生产的某园区OSGB大约15GB包含5级LOD坐标系是CGCS2000下的高斯投影最终目标是在服务器上部署为Cesium可加载的3DTiles。我使用的工作流是“数据体检 - CesiumLab快速验证 - 命令行工具批量产出 - 本地验证 - 上线发布”整套流程走下来大约需要1小时其中大部分时间是在等待处理人工干预很少。4.1 数据预处理裁剪、空三质量分析与坐标确认第一步拿模方软件把原始OSGB中无效区域裁剪掉。这个园区项目里无人机起飞点附近有几条航迹的重叠区域建模异常出现了“融化”现象还有几块屋顶玻璃反射严重导致的畸变。用模方的场景裁剪功能把这些区域框出去只保留有效建筑和地面范围。裁剪完成后重新检查metadata.xml确认坐标和原点没有因为裁剪而变化。很多裁剪工具会重新生成一套裁剪后的数据如果原点变化了务必记录下来在转换时填对。同时用模方或OSGViewer随机抽几个Tile查看确认裁剪后没有出现大面积破洞或颜色断裂。坐标确认这一步因为我项目里metadata显示的是CGCS2000高斯投影带号是39我需要把这个信息在后续转换时正确填入。如果在CesiumLab里有坐标系统下拉框可以直接选注意不要选成WGS84下的经纬度否则位置会偏移得非常离谱。在开源命令行工具中通常是通过经纬度偏移量或者EPSG代码来控制我这次用的是EPSG:4527对应CGCS2000 / 3-degree Gauss-Kruger zone 39转换时直接指定。4.2 分块与LOD参数转换质量的生命线在命令行工具以osgb23dtiles的配置为例中需要重点理解几个参数最大层级Max Level决定输出3DTiles的金字塔层级上限。一般跟OSGB的层级一致或略小因为OSGB自身有时会生成很深层的瓦片但到了某个层级以上细节增量已经很小还白白浪费存储和带宽。我一般控制在18-20级左右不同工具定义不同需要看输出日志判断。几何误差Geometric Error这个参数直接影响LOD切换的时机。简单说几何误差越大LOD切换越早远处就开始加载低精度越小越晚切换。如果发现Cesium加载时频繁闪跳可能是几何误差设置不均匀。纹理压缩Texture Compression一般选WebP或JPEG。WebP压缩率更高文件体积小但兼容性要确认目标浏览器支持JPEG兼容性最好但体积略大。我的经验是Web端项目优先WebP只要不是老旧的IE环境基本没问题。简化率Simplification控制每个LOD层的顶点简化程度。简化率太高远处模型细节丢失严重看起来像“纸片楼”太低则数据量下不来。我通常先用默认值转换然后在Cesium里拉远看整体观感再调高或调低重转一次。拿这次园区项目的实操来说原始OSGB数据15GB我设置的输出最大层级是19级几何误差按“层级越高误差越小”的LOD设计逻辑从根节点的大误差逐级递减到叶子节点的小误差。纹理统一压缩成WebP质量设为85。转出来后3DTiles总体积约8.5GB压缩率差不多45%在Cesium里浏览时近处看楼体外立面纹理清晰远处看没有“忽隐忽现”的撕裂感整体表现是可以接受的。4.3 实操步骤与命令行配置示例我日常用的命令行转换流程大概是这样的以osgb23dtiles为例思路通用# 1. 数据体检 # 用文本编辑器打开 metadata.xml确认坐标系和原点 # 用 OSGViewer 抽查关键 Tile 的纹理和几何状况 # 2. 安装工具依赖如 Node.js 环境 npm install -g osgb23dtiles # 3. 创建输出目录 mkdir -p /data/3dtiles/park # 4. 执行转换 osgb23dtiles \ --input /data/osgb/park \ --output /data/3dtiles/park \ --tilesetName park \ --maxLevel 19 \ --textureFormat webp \ --textureQuality 85 \ --geometricErrorScale 1.0执行过程中要留意日志输出正常情况会逐层输出“处理Tile xxx”之类的信息如果某块儿Tile报错会明确提示路径和原因。最常见的问题是“某个Tile纹理文件缺失或损坏”这时可以先用工具跳过坏块后续再单独补转。转换完成后检查输出目录里是否有完整的tileset.json和b3dm/glb文件。打开tileset.json确认根节点变换矩阵里的坐标值跟原始metadata对比一下确保位置没有跑偏。4.4 输出验证本地起服务快速查错转完之后千万别直接丢服务器先在本地起一个HTTP服务验证一下。最简单的# 在输出目录下启动静态服务 npx serve /data/3dtiles/park然后用浏览器打开Cesium页面指定tileset.json的URL看看模型位置是否正确、层级切换是否正常、纹理是否清晰。如果发现模型位置偏移通常要回头查坐标转换参数如果模型是黑的多半是纹理压缩格式或法线方向问题如果加载到某块区域就卡住或崩溃可能那个区域的Tile数据不完整。本地验证通过后才是服务器部署环节。将转换好的3DTiles目录同步到服务器Nginx或对象存储下确保路径能通过HTTP访问即可。需要注意3DTiles没有绝对的“规定后缀名”只要服务器能正确返回文件内容Cesium就能加载。但有些服务器默认对.b3dm或.glb的MIME类型识别不对需要在Nginx里加一行application/octet-stream的映射否则下载解析可能异常。5. 常见问题与排查技巧实录转换这件事最磨人的不是操作本身而是出了状况不知道去哪查。这里我把这些年高频踩坑的问题整理成一个速查表下面再挑几个有代表性的展开说每一类的排查思路和解决方向也一并写出来希望能帮你少走弯路。5.1 高频报错与解决方向速查问题现象可能原因排查思路与建议动作转出来的模型整体不在目标位置坐标系选择错误、原点偏移未设置核对metadata.xml中的坐标系和原点值重新设置转换参数中间某块Tile缺失加载时出现黑洞原始OSGB存在损坏Tile或磁盘空间不足导致写入中断用工具跳过坏块并单独补转检查磁盘剩余空间加载时模型一闪一闪LOD抖动几何误差设置不合理、LOD层级跳变过大调整几何误差缩放系数或降低最大层级让LOD切换更平滑远处看模型很糊但数据量巨大简化率过高导致低层级细节过早丢失或纹理压缩质量不足提高纹理质量设置降低简化率重新生成低层级Tile模型表面发黑或纹理丢失纹理压缩格式兼容问题或原始纹理加载失败换成JPEG格式测试检查原始纹理是否损坏浏览器加载tileset.json报跨域错误服务器未正确配置跨域头在服务器上添加CORS头允许GET请求服务端部署后找不到b3dm文件文件路径大小写不一致、MIME类型配置错误检查目录路径和Nginx配置确保静态文件服务路径正确5.2 坐标偏移的几种坑从“差一点”到“差十万八千里”坐标问题在转换中出现的频率最高而且症状千奇百怪。有一种是整体偏了百八十米这种通常是中央经线选错比如数据本身是3度分带但你选了6度分带或者带号多加了一个。另一种是模型旋转了角度通常是投影方式搞错了比如数据是高斯克吕格但你选成了UTM两者中央经线和伪偏移的算法不同就会导致整体旋转。还有一种是“差几厘米但客户就是不满意”。这种大多出现在CGCS2000和目标坐标系的“框架转换”上因为不同历元下坐标框架有微小差异。对于绝大多数Web展示项目这个误差可以接受但如果是做精确量测或与已有GIS数据套合就要考虑增加七参数转换或使用更精确的转换服务。我的建议是先向数据生产方要一份坐标说明搞清楚数据用的到底是哪个椭球、哪个投影、哪个带号再对照转换工具里的坐标系选项基本能规避8成以上的坐标问题。5.3 内存溢出与转换慢大数据量的应对思路当你手里是几十GB甚至上百GB的OSGB时转换慢和内存溢出就是绕不开的话题。一个容易忽略的点是很多转换工具需要把整个Tile块的几何和纹理读入内存处理如果某个Tile异常巨大内存就可能崩了。这时候从硬件角度加内存只是缓兵之计更治本的方法是“分块转换再合并”。具体操作是把OSGB数据按原始分块目录切成若干子集分别转换最后用工具把多个tileset合并成一个根tileset。这个方案既有可操作性又能大幅降低单次转换的压力。合并时注意各子集的空间范围不要重叠否则会出现叠面闪烁。另外如果目标平台支持也可以考虑直接用“稀疏化”策略把远端的低层级瓦片删除或替换成占位模型减少初始加载体积。还有个容易被忽略的性能瓶颈是磁盘I/O。转换本质上是大量小文件的读写操作机械硬盘在这种场景下会非常拖后腿。我自己的服务器用的事NVMe固态同样数据量下转换时间几乎是机械硬盘的三分之一。所以如果你打算经常搞数据转换把工作目录放到固态盘上是很值得的投资。6. 进阶场景单体化、shp转3dtiles与Cesium加载实践转换本身只是数据“上云”的前半段真正要交付一个能用的三维Web项目至少还会碰到单体化、结合矢量数据、性能优化这几个进阶场景。这些内容跟“转换”高度相关但很多人都是转换完才发现还有后续要处理所以这里一并聊一聊。6.1 Cesium 3DTiles单体化转换之后的下一个关键词“单体化”在倾斜摄影项目里是个高频词。它的本质是让“一整片连在一起的倾斜模型”变成“一个个可以被鼠标选中、单独查询属性”的独立对象。转换后的3DTiles如果只是一整块meshCesium默认情况下点击一个建筑选中的是整个瓦片而不是那栋建筑本身。要实现点击选中一栋楼并弹出它的楼层、面积、权属信息就必须走单体化流程。常见的单体化方案是“ID单体化”或“矢量面切割单体化”。前者是给3DTiles里每个建筑分配一个唯一标识Feature ID通过glTF的EXT_mesh_features扩展或者Cesium的Classification机制来选中高亮后者是提前准备好建筑轮廓的shp矢量面在Cesium里用Cesium3DTileset配合Cesium3DTileFeature进行拾取再通过矢量面与模型的对应关系查出属性。如果是从shp转3dtiles的需求切入通常思路是把shp转为GeoJSON再把GeoJSON作为数据源发布与倾斜模型3DTiles叠加展示。这样建筑的单体属性名称、楼层、用途都在shp属性表里前端点击时通过射线拾取模型得到位置再做空间查询匹配到对应shp面。这个方案三角形不多性能压力小工程上最稳妥也是我推荐的首选方案。前提是shp里的建筑轮廓要画得准跟倾斜模型套合得好否则点击查询对不上位置会很尴尬。6.2 转换结果与Cesium加载的适配技巧Cesium加载3DTiles模型本身不复杂代码量很少const tileset await Cesium.Cesium3DTileset.fromUrl( https://your-server.com/3dtiles/park/tileset.json ); viewer.scene.primitives.add(tileset); viewer.zoomTo(tileset);但加载之后的体验好不好取决于转换时的很多前置细节。有几个我实测后的感受第一注意屏幕空间误差SSE和最大屏幕空间误差的比例。Cesium默认的maximumScreenSpaceError是16这个值偏向于“少加载高精度瓦片”如果模型交互频繁、需要看清细节可以下调到8左右视觉精度明显提升但代价是请求量上去了。理想的方案是在转换时就把瓦片切得合理一些而不是靠前端疯狂调参来“找补”。第二设置preloadFlightDestinations或dynamicScreenSpaceError之类的优化项要谨慎。有些优化项在特定场景下会带来负面效果比如动态屏幕空间误差可能导致远近视角切换时出现“瓦片加载迟滞”。我的做法是如果数据量和场景复杂度可控尽量保持默认参数先跑通再优化。第三服务器端Gzip压缩对b3dm效果不明显因为b3dm内部通常已经做了压缩。但tileset.json和同级的json文件压缩收益很大建议服务器开启Gzip/Brotli至少能让较深的LOD树结构加载快不少。6.3 更省流量的思路纹理压缩与Draco压缩的选择如果最终用户的网络环境一般转换时就要在“体积”和“质量”之间找平衡。目前业界常用的几何压缩方案是Draco压缩能把顶点数据压到很夸张的小体积但代价是解压需要额外的CPU计算。纹理压缩方面WebP、KTX2是主流KTX2支持GPU直接解压但浏览器兼容性和工具链支持要确认好。以我的项目经验普通Web展示项目用WebP就够用了配合Draco压缩几何整体数据量能再降20%-30%。但要注意过度压缩会带来模型细节丢失和纹理糊掉的问题最终效果一定要在Cesium里多视角验收不要只看数据量下降就以为“优化成功了”。7. 写在最后的经验补充转换工作做到现在我最大的体会是这活儿没有一招鲜的万能解每个数据源都有自己的脾气。同样的工具换个项目可能参数就要调整同样的设置换台机器可能表现就不一样。我刚入行时也特别迷信某个工具或某套参数后来被现实教育多了才明白保持“先体检再转换后验证”的流程习惯比记住任何一个具体参数都重要。再补一个小技巧是我踩过几次坑之后养成的习惯转换之前先拿一块最小的Tile子集跑通整个流程。也就是把OSGB里面积很小的一个角落单独拷出来先转成3DTiles在本地起服务加载看看坐标、层级、纹理是否正常。这一小步最多花十分钟却能提前暴露绝大多数全局性问题避免你花两小时转换完一大坨数据才发现坐标系选错了再从头来一遍。希望这篇东西能帮你在OSGB转3DTiles这条路上省点时间。如果你在实操中遇到上面没提到的怪问题也可以顺着“先查坐标、再查LOD、后查纹理”的思路去定位大概率能找到答案。这一篇就到此为止下次有机会再聊聊单体化选型或者大规模数据更新的自动化流程。