拿到一份三维扫描的原始数据,很多人第一反应是兴奋,第二反应就是头疼:几百万甚至上千万个三角面、边缘处大量破碎面片、表面布满扫描噪点,这样的模型直接丢进游戏引擎或者网页端三维应用,轻则渲染卡顿,重则直接崩溃。把扫描数据转化为可用资产,中间要经历清理、重建、优化、贴图、导出五个环节,每个环节都有讲究。下面这张流程示意图可以帮助建立整体认知。

扫描数据清理:先治好模型的百病
扫描数据处理的第一步永远是清理。无论使用的是激光扫描仪、结构光设备还是消费级的深度相机,原始点云或网格都会存在几类典型问题:噪点(表面随机凸起的小颗粒)、离群点(飘在模型之外的孤立点)、破洞(遮挡导致的表面缺失)以及重复面和翻转面。这些问题如果不解决,后续所有操作都会建立在脏数据之上,效果大打折扣。
常用的清理流程是:先用统计滤波或半径滤波剔除离群点,再用拉普拉斯平滑抑制表面噪点,最后通过孔洞检测与填补算法修补破洞。以开源工具CloudCompare或MeshLab为例,统计滤波的实现思路如下伪代码所示:
import numpy as np
def statistical_filter(points, k=20, sigma_ratio=1.0):
"""基于K近邻距离的统计滤波,剔除离群点"""
# 计算每个点到其k个近邻的平均距离
mean_d = compute_knn_mean_distance(points, k)
# 全局均值与标准差
global_mean = np.mean(mean_d)
global_std = np.std(mean_d)
# 超出均值+sigma_ratio倍标准差的点视为离群点
threshold = global_mean + sigma_ratio * global_std
mask = mean_d < threshold
return points[mask]
需要特别提醒的是,平滑操作要适度。拉普拉斯平滑迭代次数过多会让模型的锐利边缘(比如家具的棱角、机械件的倒角)被磨圆,细节丢失后再也无法找回。建议的做法是分区处理:平坦区域多平滑几轮,细节丰富的区域用保特征平滑算法,或者干脆手动修整。
网格重建与拓扑优化:面数是资产的生死线
清理干净的模型面数往往依然巨大,一个扫描好的雕像可能有八百万个三角面,而游戏引擎里一个角色的预算通常只有几万面。这就是重拓扑(Retopology)要解决的问题:用规整的、四边面为主的低模网格去包裹高模扫描数据,再通过法线贴图把高模细节烘焙到低模上,实现视觉不降级、性能大幅提升。
重建网格时有两个思路可以选择。第一种是自动重建,比如Delaunay三角化和Poisson重建,速度快但布线不可控;第二种是手动或半自动重拓扑,在Blender、Maya或TopoGun中沿着模型结构走向手工绘制四边面流。对于会被动画、变形的资产(人物、动物),强烈建议手动重拓扑,因为布线方向直接决定变形质量,关节处的环状布线是动画不出问题的前提。对于静态资产(雕塑、建筑扫描件),自动重建加简化就足够了。
# 使用Open3D进行网格简化的示例思路
import open3d as o3d
mesh = o3d.io.read_triangle_mesh("scan_clean.ply")
# 顶点聚类简化:速度快,适合粗简化
voxel_size = max(mesh.get_max_bound() - mesh.get_min_bound()) / 512
simple = mesh.simplify_vertex_clustering(
voxel_size, contraction=o3d.SimplificationContract.Mean
)
# 二次误差度量简化:精度更高,保留特征边缘
quadric = simple.simplify_quadric_decimation(target_number_of_triangles=15000)
o3d.io.write_triangle_mesh("asset_lowpoly.obj", quadric)
面数预算怎么定?经验值是:移动端实时渲染的单个资产控制在1万到3万三角面,PC端可以放宽到5万到10万,网页端Three.js应用建议单个模型不超过5万面。同时要控制顶点数与三角面数的比例,避免出现大量细长三角形,它们不仅渲染效率低,烘焙法线贴图时也容易产生瑕疵。
UV展开、烘焙与格式导出:最后一公里
低模做好后,需要展开UV并烘焙贴图。扫描模型一般自带顶点色或者高分辨率纹理,烘焙就是把高模的视觉信息转移到低模的贴图上。烘焙的核心贴图有三张:法线贴图(Normal Map)记录表面凹凸细节,环境光遮蔽贴图(AO)增强角落阴影层次,反照率贴图(Albedo)承载基础颜色信息。烘焙时最常见的问题是法线贴图出现接缝和波纹,通常由UV seam切割位置不当或射线投射距离设置不合理导致,建议把切缝放在模型背面或视觉不明显的区域,并根据高模与低模的偏差调整Front和Back的距离参数。
导出格式的选择取决于目标平台。OBJ格式通用性最好但文件较大;GLTF(二进制版本为GLB)是Web端和AR应用的事实标准,支持PBR材质、 Draco压缩后体积可缩小到原来的十分之一左右;FBX适合进Unity或Unreal做动画资产;USD则是大型影视管线的趋势。一个值得养成的习惯是:保留一份高模原始数据存档,工作资产用低模加贴图,两者分开管理,避免日后需要重烘焙时找不到高模源文件。
# 使用gltf-transform命令行压缩模型 npm install -g @gltf-transform/cli # 对模型做Draco几何压缩并优化纹理尺寸 gltf-transform optimize input.glb output.glb \ --compress draco \ --texture-size 2048
最后别忽视坐标与单位的统一。扫描软件输出的模型可能是任意朝向和真实米制单位,而引擎通常要求Y轴或Z轴朝上、单位为米或厘米。导出前务必检查并烘焙好变换矩阵,否则模型进引擎后出现躺倒、缩小一百倍的情况会非常常见。把清理、重拓扑、烘焙、导出固化成一套标准管线,用脚本串联重复环节,才能让扫描资产的产出效率和稳定性都达到工程化的水准。