在实际增材制造项目中,晶格体(lattice structure)是轻量化设计中最常用的结构形式之一。它由重复单元胞组成,在保留受力路径的同时大幅减少材料用量,适合鞋底、座椅、支架、缓冲件等场景。但晶格体一旦生成,表面往往是整齐重复的网格肌理,很难直接表达产品标识、防滑纹理或设计纹样。这里说的“自定义表面图形晶格体包”,指的就是一套把二维表面图形叠加到晶格体表面、生成真实凹凸浮雕的网格处理流程。它不是在切片软件里更换颜色,而是修改 STL 网格顶点,把 logo、纹理、编号或防滑纹路变成可打印的几何特征。下面用一个 Python 网格处理工具包为主线,从环境准备、STL 检查、灰度图生成、顶点位移浮雕、切片验证到生产交付,完整走一遍。
1. 先理解晶格体与表面图形组合的关键概念
1.1 晶格体解决什么问题
晶格体是一种以规则或不规则胞元堆叠而成的多孔结构。它的核心价值在于用更少的材料承担相近的结构功能。常见胞元类型包括体心立方(BCC)、面心立方(FCC)、Gyroid、Diamond 等,不同胞元对应不同的各向异性、能量吸收和成型难度。
晶格体的关键参数可以统一整理成下面几项:
| 参数 | 含义 | 典型范围 | 影响 |
|---|---|---|---|
| 胞元尺寸(cell) | 单个晶胞的边长或直径 | 2~8 mm | 越小越密,打印时间越长,力学性能越接近实体 |
| 壁厚或杆径 | 晶格实体部分的厚度 | 0.4~1.2 mm | 决定强度和打印可行性 |
| 孔隙率 | 空腔体积占比 | 30%~80% | 影响重量和缓冲性能 |
| 晶胞数量 | 三个方向上的重复次数 | 由产品尺寸决定 | 决定表面网格密度 |
在生成晶格体之后,表面通常不具备产品标识、防滑纹路等视觉与功能特征。这时就需要“自定义表面图形”。表面图形不只是装饰,它可以是防滑斜纹、品牌 logo、编码、散热片、装配定位凸点。这些图形在传统 CAD 中逐个绘制很繁琐,尤其当模型是由数千个晶胞组成时,手动操作不现实。
1.2 自定义表面图形的本质是几何修改
二维平面上的“贴图”是颜色信息,切片软件不会把它真的打印成凹凸特征。自定义表面图形的本质是修改三维网格的顶点位置:
- 把二维灰度图映射到模型表面的三维坐标;
- 根据灰度值沿表面法向移动顶点;
- 白色区域凸起,黑色区域凹陷,灰色区域形成过渡。
这种处理方式有两个优点。第一,它是参数化的,图案在哪个区域、凸起多高、过渡多宽都可以由脚本控制。第二,它不依赖具体 CAD 软件,只要拿到 STL 或 OBJ,就可以批量处理多个晶格体模型。
但也要理解顶点位移的边界:位移量过大时,顶点可能越过相邻面,产生自交面,切片软件会报错或生成混乱支撑。因此,表面图形深度通常控制在 0.2~0.8 mm,具体值由打印层高和材料决定。
1.3 三条技术路线怎么选
实现“自定义表面图形 + 晶格体”通常有三条路线,适用场景差异很大。
| 技术路线 | 实现方式 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| CAD 参数化设计 | 在 Fusion 360、SolidWorks 中建纹理草图并拉伸 | 参数可改,模型质量高 | 晶格体数据量大时操作卡顿 | 单个产品的结构设计 |
| 切片软件图案 | 在 PrusaSlicer、Cura 中叠加文字或图案 | 操作简单 | 只能做表面装饰,无法精确控制深度 | 快速验证和低精度打印 |
| Python 网格处理 | 读取 STL,用代码生成纹理浮雕 | 可批量、可复现、可集成到自动化流程 | 需要写脚本和排查网格问题 | 批量模型、二次开发、参数化试制 |
本文选择第三条路线。它以代码为核心,适合需要批量处理不同晶格体模型、反复调整纹理深度的工程场景。后面所有脚本都围绕“输入 STL + 灰度图,输出新 STL”这条主线展开。
1.4 本文工具包的目标输出
这套处理流程的最终成果是:
- 输入一个封闭流形的晶格体 STL;
- 输入一张灰度 PNG 作为表面图形;
- 脚本自动选择目标表面区域;
- 生成带浮雕特征的 STL 并导出;
- 在切片软件中验证可打印性。
后续所有环境准备、代码实现、参数说明和排错内容,都围绕这五步展开。
2. 准备 Python 网格处理环境和输入文件
2.1 环境与依赖
建议使用 Python 3.9 及以上版本。核心库如下:
| 库 | 用途 | 安装命令 |
|---|---|---|
| trimesh | STL/OBJ 读写、网格属性检查 | pip install trimesh |
| numpy | 向量和数组计算 | pip install numpy |
| scipy | 灰度采样、高斯模糊 | pip install scipy |
| Pillow | 生成和读取 PNG 灰度图 | pip install Pillow |
| scikit-image | gyroid 等值面生成 | pip install scikit-image |
| pyvista | 可选,三维可视化排查 | pip install pyvista |
推荐先创建虚拟环境,避免污染系统 Python。
python -m venv .venv source .venv/bin/activateWindows 环境使用.venv\Scripts\activate激活。然后一次性安装依赖:
pip install trimesh numpy scipy Pillow scikit-image pip install pyvistapyvista 不是必须项,但后续排查模型破面、纹理方向问题时,可视化能节省大量时间。
2.2 输入晶格体 STL 的基本要求
不是所有 STL 都适合直接做顶点位移。输入模型必须满足几个条件:
- 必须是三角网格文件,STL 或 OBJ 均可;
- 最好是封闭流形(watertight),没有破面;
- 单位统一为毫米;
- 模型尺寸要和实际打印尺寸一致,不能出现 1:25.4 之类的比例错误。
拿到模型后,先用下面脚本检查,不要直接生成纹理。
import trimesh mesh = trimesh.load("input/lattice_sole.stl", force="mesh") print("bounds min:", mesh.bounds[0]) print("bounds max:", mesh.bounds[1]) print("is_watertight:", mesh.is_watertight) print("vertex_count:", len(mesh.vertices)) print("face_count:", len(mesh.faces)) print("volume_mm3:", mesh.volume) print("area_mm2:", mesh.area)如果is_watertight输出为 False,先不要继续,否则位移后裂缝会被放大。这时可以用微软 Netfabb、Blender 的 3D Print Toolbox 或 trimesh 的mesh.process()做基础修复。必须明确:自动修复不能解决所有问题,破损严重的模型需要回到设计软件重新导出。
2.3 用 Pillow 生成测试用灰度图
真实项目里表面图形通常来自设计稿。测试阶段可以用脚本生成一张灰度图,用于验证整体流程。
下面代码生成 512×512 灰度图,背景为 128,上面画三个菱形防滑纹理和一组圆点:
from PIL import Image, ImageDraw, ImageFilter size = 512 img = Image.new("L", (size, size), color=128) draw = ImageDraw.Draw(img) for i in range(3): cx = 96 + i * 160 cy = 256 r = 40 draw.polygon( [(cx, cy - r), (cx + r, cy), (cx, cy + r), (cx - r, cy)], fill=220, ) draw.ellipse((cx - 10, cy - 10, cx + 10, cy + 10), fill=50) img = img.filter(ImageFilter.GaussianBlur(1.5)) img.save("input/surface_texture.png")灰度约定:大于 128 表示凸起,小于 128 表示凹陷。图像尺寸中的宽高比要和模型目标区域的平面投影宽高比接近,否则纹理会被拉伸变形。
2.4 项目目录结构
建议按下面的结构组织文件,后续脚本和输出路径不会乱。
lattice_surface/ ├── input/ │ ├── lattice_sole.stl │ └── surface_texture.png ├── output/ ├── scripts/ │ ├── check_mesh.py │ ├── generate_texture.py │ ├── apply_relief.py │ └── generate_gyroid.py └── config.json脚本路径和输出路径用相对位置,不要写死成C:\xxx这类绝对路径,方便换机器后仍然可运行。
3. 实现把灰度图形映射到晶格体表面的浮雕生成器
3.1 核心思路:通过顶点位移生成浮雕
浮雕生成的算法可以概括为四步:
- 找到晶格体表面中需要加纹理的区域;
- 把该区域的顶点坐标转换成纹理图像的 UV 坐标;
- 用纹理灰度值计算每个顶点的偏移量;
- 沿顶点法向移动顶点,导出新网格。
这一步使用顶点位移而不是布尔减除,原因很实际:逐顶点位移计算量小,可控性强,不会因为 STL 面片数量大而卡死;同时避免了三方布尔库在复杂网格上稳定性不足的问题。代价是位移深度必须控制在一定范围内,不能像建模软件里的“拉伸切除”那样做出很深的结构。
3.2 选择目标表面并建立 UV 映射
先加载晶格体模型,再选择顶面附近的顶点。这里假设晶格体摆放方向是 z 轴朝上,表面图形贴在顶部。
import trimesh import numpy as np mesh = trimesh.load("input/lattice_sole.stl", force="mesh") verts = mesh.vertices normals = mesh.vertex_normals z_min = verts[:, 2].min() z_max = verts[:, 2].max() z_band = z_min + (z_max - z_min) * 0.85 mask = (normals[:, 2] > 0.8) & (verts[:, 2] >= z_band) idx = np.where(mask)[0] print("selected vertices:", len(idx))筛选条件里normals[:, 2] > 0.8表示顶点法向接近 z 正方向,也就是朝上的面;verts[:, 2] >= z_band表示只处理靠近模型最高面的顶点。这样能避免把晶格侧壁和底面也加上纹理。
得到目标顶点后,把它们的 xy 坐标归一化到 0~1 区间,作为纹理采样坐标:
xs = verts[idx, 0] ys = verts[idx, 1] x_min, x_max = xs.min(), xs.max() y_min, y_max = ys.min(), ys.max() u = (xs - x_min) / max(x_max - x_min, 1e-9) v = (ys - y_min) / max(y_max - y_min, 1e-9)如果模型表面是一块弧面,xy 投影映射会造成一定变形。测试阶段可以接受,生产场景建议改用圆柱映射或基于最近面片的投影采样。
3.3 用灰度图计算每个顶点的偏移量
使用scipy.ndimage.map_coordinates从灰度图中采样,采样点数量与目标顶点数量相同。
from PIL import Image from scipy.ndimage import map_coordinates, gaussian_filter img = Image.open("input/surface_texture.png").convert("L") gray = np.array(img).astype(np.float32) / 255.0 gray = gaussian_filter(gray, sigma=2.0) h, w = gray.shape rows = (1.0 - v) * (h - 1) cols = u * (w - 1) samples = map_coordinates(gray, [rows, cols], order=1, mode="nearest")这里rows使用1.0 - v是因为图像坐标原点在左上角,而模型的 y 轴通常朝上。如果采样后纹理上下颠倒,把这一行改成rows = v * (h - 1)即可。
接着把灰度值转换为偏移量。灰度 0.5 对应零偏移,大于 0.5 凸起,小于 0.5 凹陷:
depth = 0.5 offsets = (samples - 0.5) * depth最后沿法向移动选中的顶点:
before = verts.copy() verts = verts.copy() verts[idx] += normals[idx] * offsets[:, None] textured_mesh = trimesh.Trimesh(vertices=verts, faces=mesh.faces, process=False) textured_mesh.export("output/lattice_sole_textured.stl")process=False很重要。trimesh 默认会重新处理网格,可能会重新排列顶点索引,导致之前在顶点上做的偏移和面索引对不上。导出前也可以检查体积变化:
volume_change = textured_mesh.volume - mesh.volume print("volume change:", volume_change)如果纹理区域小、深度不大,体积变化通常只有几十立方毫米。如果体积变化为 0,说明没有顶点被选中,要回到 3.2 节检查筛选条件。
3.4 边缘过渡与文字图案处理
直接按灰度采样会产生明显台阶。因为灰度图里图形边缘的像素值通常从 220 直接跳到 50,映射到网格上就是一道陡坎,FDM 打印时容易出现拉丝,光固化打印时会导致图案边缘积料。
解决办法是给灰度图做高斯模糊,也就是 3.3 节代码中的gaussian_filter(gray, sigma=2.0)。sigma 参数与图案特征尺寸有关:
| sigma | 过渡宽度 | 适用场景 |
|---|---|---|
| 0 | 无过渡 | 文字轮廓强,但打印风险高 |
| 1~2 | 较窄过渡 | 防滑纹理、小型 logo |
| 3~5 | 较宽过渡 | 大面积渐变色面,装饰性纹理 |
如果图案是文字,还建议把文字笔画宽度控制在一倍线宽以上。FDM 常用 0.4 mm 喷嘴,线宽约 0.42~0.48 mm。也就是说,文字笔画或纹理线条至少要有 0.8 mm 宽,否则打印出来会糊成一片。
3.5 把浮雕函数封装成可复用工具
上面 3.2~3.4 的代码可以合并成独立的工具函数,便于批量处理多个模型。
def apply_relief_to_mesh( input_path, texture_path, output_path, depth=0.4, blur=2.0, z_fraction=0.85, ): mesh = trimesh.load(input_path, force="mesh") if not mesh.is_watertight: raise ValueError("input mesh is not watertight") verts = mesh.vertices normals = mesh.vertex_normals z_min = verts[:, 2].min() z_max = verts[:, 2].max() z_band = z_min + (z_max - z_min) * z_fraction mask = (normals[:, 2] > 0.8) & (verts[:, 2] >= z_band) idx = np.where(mask)[0] tex = np.array(Image.open(texture_path).convert("L")).astype(np.float32) / 255.0 tex = gaussian_filter(tex, sigma=blur) xs = verts[idx, 0] ys = verts[idx, 1] u = (xs - xs.min()) / max(xs.max() - xs.min(), 1e-9) v = (ys - ys.min()) / max(ys.max() - ys.min(), 1e-9) rows = (1.0 - v) * (tex.shape[0] - 1) cols = u * (tex.shape[1] - 1) samples = map_coordinates(tex, [rows, cols], order=1, mode="nearest") offsets = (samples - 0.5) * depth new_verts = verts.copy() new_verts[idx] += normals[idx] * offsets[:, None] new_mesh = trimesh.Trimesh(vertices=new_verts, faces=mesh.faces, process=False) new_mesh.export(output_path) print("vertices:", len(idx)) print("volume change:", new_mesh.volume - mesh.volume) print("watertight:", new_mesh.is_watertight)调用方式:
apply_relief_to_mesh( "input/lattice_sole.stl", "input/surface_texture.png", "output/lattice_sole_textured.stl", depth=0.4, )4. 如果要从零生成自己的晶格体:Gyroid 等值面工具
4.1 为什么需要程序化生成晶格体
有些场景下并没有现成晶格体 STL,而是需要从零生成。手动在 CAD 里画几百个胞元既慢又占内存。数学公式定义的 Gyroid 极小曲面是一个不错选择,它的表达式是:
sin(x)*cos(y) + sin(y)*cos(z) + sin(z)*cos(x) = iso当iso = 0时得到标准 Gyroid 曲面。调整等值面阈值可以改变实体和空腔的比例。生成流程是用scikit-image的 Marching Cubes 从体积场中提取等值面。
4.2 使用 Marching Cubes 生成 Gyroid 表面
下面脚本生成一个可导出 OBJ/STL 的 Gyroid 曲面片:
import numpy as np from skimage import measure import trimesh def gyroid_field(x, y, z, iso=0.0): return ( np.sin(x) * np.cos(y) + np.sin(y) * np.cos(z) + np.sin(z) * np.cos(x) - iso ) def generate_gyroid_surface(cell=2.0, repeats=(3, 3, 3), samples_per_cell=20): nx, ny, nz = repeats cell = float(cell) spacing = cell / samples_per_cell x = np.linspace(0, cell * nx, samples_per_cell * nx, endpoint=False) y = np.linspace(0, cell * ny, samples_per_cell * ny, endpoint=False) z = np.linspace(0, cell * nz, samples_per_cell * nz, endpoint=False) gx, gy, gz = np.meshgrid(x, y, z, indexing="ij") vol = gyroid_field(gx, gy, gz, 0.0) verts, faces, normals, _ = measure.marching_cubes( vol, level=0.0, spacing=(spacing, spacing, spacing) ) mesh = trimesh.Trimesh( vertices=verts, faces=faces, vertex_normals=normals, process=False, ) return mesh mesh = generate_gyroid_surface() mesh.export("output/gyroid_surface.obj")spacing参数把体素索引转换为真实尺寸。上面代码中 cell 以 mm 为单位,导出模型的坐标单位也是 mm。repeats控制三个方向上的晶胞数量。
要特别说明的是,Marching Cubes 输出的是无限曲面的截断片,它不是一个有壁厚的封闭实体,不能直接切片打印。要真正打印,通常有三种处理方式:
- 把曲面导入 CAD 软件,做加厚操作(Solidify/Offset);
- 在 Meshmixer、Blender 等软件中做表面加厚;
- 仅作为造型验证使用,正式晶格体仍从专业晶格软件导出。
用这个流程时不要跳过这一步,否则切片软件得到的模型只是零厚度的面,无法正确生成层。
4.3 在程序化晶格体上叠加表面图形
如果把 4.2 导出的模型先加厚成封闭实体,再调用 3.5 节的apply_relief_to_mesh,就可以在程序化晶格体表面叠加自定义图形。这样,一套脚本覆盖了两类场景:已有晶格体加纹理,以及从零生成晶格体再加纹理。
实际项目中,程序化生成的 Gyroid 往往表面是波动的,顶点法向方向不统一。此时 3.2 节基于normals[:, 2] > 0.8的筛选不再可靠。更稳妥的做法是:先确定纹理区域的最小 z 值,再把该 z 值以上的所有顶点选中,并限制最大法向夹角。代码可以改成:
mask = (verts[:, 2] >= z_band) & (np.arccos(np.clip(normals[:, 2], -1, 1)) < np.radians(45))这样允许法向偏转 45 度以内的表面点参与浮雕,纹理沿曲面自然过渡,而不是只贴在正顶面。
5. 切片验证、打印前检查和生产注意事项
5.1 在切片软件中验证模型
导出 STL 后先别急着打印,导入 PrusaSlicer 或 Cura 检查。
重点看三个地方:
- 模型是否完整封闭,切片软件是否会提示“非流形边”;
- 纹理区域是否清晰可见,有没有因为深度太小而变成完全光滑;
- 纹理区域附近有没有大量悬空点或细小孤岛。
建议参数配置如下(FDM 验证):
层高: 0.2 mm 线宽: 0.4 mm 壁厚: 0.8 mm 填充率: 取决于晶格体本身,若模型自身已经是晶格,设置 15%~30% 打印速度: 60 mm/s 回抽: 开启如果纹理深度只有 0.1 mm,而层高是 0.2 mm,打印机根本不会输出这个细节,因为一层已经超过浮雕高度。纹理深度至少应该是层高的 2~3 倍,0.2 mm 层高对应 0.4~0.6 mm 深度。
5.2 打印前检查清单
以下清单可以在每次批量输出前过一遍:
| 检查项 | 验证方式 | 通过标准 |
|---|---|---|
| 单位正确 | 检查 bounds 范围 | 与 CAD 设计尺寸一致 |
| 封闭流形 | 脚本输出 is_watertight | True |
| 面片数量合理 | 查看 face_count | 晶格体常见 10 万~200 万面 |
| 纹理方向正确 | pyvista 预览或切片俯视图 | 图案没有镜像和旋转 |
| 深度合适 | 查看纹理区 z 高度差 | 不小于层高的 2 倍 |
| 体积变化合理 | 对比原模型与处理模型 | 变化量不大且方向符合预期 |
| 无自交面 | 切片软件报错检查 | 无“非流形”“自交”提示 |
5.3 学习环境与生产环境的差异
学习环境里可以只跑通一个脚本,把 STL 拖进切片软件看一眼就结束。生产环境至少要多做四件事:
- 参数文件化。把深度、模糊系数、纹理缩放、模型路径放进 JSON/YAML,而不是散落在脚本里。
- 过程日志。脚本要输出选中顶点数量、处理耗时、体积变化,方便追溯。
- 原始文件备份。生成过的 STL 可能占用几百 MB,建议只保留原始 STL、灰度图和参数文件,STL 可以随时重新生成。
- 试打验证。第一次纹理生成后一定要打一个 20×20 mm 的局部测试块,确认纹理实际效果后再批量应用。
一个最小化的参数文件如下:
{ "input": "input/lattice_sole.stl", "texture": "input/surface_texture.png", "output": "output/lattice_sole_textured.stl", "depth_mm": 0.4, "blur_sigma": 2.0, "z_fraction": 0.85 }脚本运行时读取这个文件,改参数不需要修改代码:
import json with open("config.json", "r", encoding="utf-8") as f: cfg = json.load(f) apply_relief_to_mesh( cfg["input"], cfg["texture"], cfg["output"], depth=cfg["depth_mm"], blur=cfg["blur_sigma"], z_fraction=cfg["z_fraction"], )6. 常见问题排查
顶点位移浮雕在工程中的问题大多集中在方向、深度和网格完整性上。下面按现象列出排查路径。
| 问题现象 | 可能原因 | 检查方式 | 处理建议 |
|---|---|---|---|
| 纹理方向上下颠倒 | 图像行坐标和模型 y 轴方向不一致 | 打印预览或 pyvista 查看纹理纵向位置 | 把rows = (1.0 - v)改为rows = v |
| 纹理左右镜像 | uv 映射时 x 方向反了 | 对比图案与模型左右关系 | 将u改为1.0 - u后重新采样 |
| 凸起和凹陷相反 | 灰度约定反了 | 看一个已知黑色区域是否凹陷 | 把samples - 0.5改为0.5 - samples |
| 导出后模型破面 | 位移深度过大,面片自交 | 切片软件提示非流形,脚本 watertight=False | 减小 depth,增加 blur 平滑过度 |
| 纹理只有局部出现 | z_fraction 或法向夹角筛选过严 | 打印selected vertices数量 | 降低 z_fraction,扩大法向角度 |
| 图片在表面拉长变形 | 图像宽高比和模型目标区域宽高比不一致 | 对比纹理图案整体形状 | 按模型 xy 范围等比生成灰度图 |
| 生成速度很慢 | 晶格体面片数太大,顶点采样耗内存 | 查看 face_count | 导出前缩减面片,或用 mesh.simplify_quadric_decimation 减面 |
| 打印时纹理消失 | 深度小于等于层高或线宽 | 切片层视图观察纹理层 | 调大 depth,或改用更小层高 |
如果selected vertices输出为 0,优先检查模型摆放方向。有些 STL 默认把 z 轴作为厚度方向,有些则把 y 轴朝上。这时要么在代码里把筛选轴改掉,要么用 trimesh 的mesh.apply_transform先把模型旋转到 z 轴朝上,再执行浮雕处理。
最推荐的排查顺序是:先确认模型方向和单位,再确认灰度图方向,然后确认顶点筛选数量,最后才调深度。跳跃式排查容易同时改动多个变量,出问题后很难定位。
7. 最佳实践与扩展方向
7.1 容易踩的三个坑
第一,不要在未检查is_watertight的模型上直接做顶点位移。STL 模型只要有少量破面,顶点位移后裂缝会进一步扩大,切片软件会尝试自动补缝,生成难以预期的表面。
第二,不要把纹理深度一次拉满。0.8 mm 在以 0.2 mm 层高打印时已经算很深,过深会导致相邻顶点位移方向交叉,局部出现倒扣面。建议从 0.3 mm 起步,打印一次测试块后再决定是否加大。
第三,不要把 pyvista 可视化放在生产链路之外。用代码处理网格,单靠“导出 STL 后用肉眼看”效率太低。在脚本里增加一行预览逻辑,能显著减少来回导出导入的时间。
import pyvista as pv def preview(mesh, title="mesh"): plotter = pv.Plotter() plotter.add_mesh(pv.wrap(mesh), show_edges=False) plotter.add_title(title) plotter.show()7.2 可复用发布清单
每次生成自定义表面图形晶格体前,按清单核对一遍:
- 原始 STL 已检查,结构完整且水密;
- 模型单位与打印平台一致;
- 灰度图对比度清晰,边缘过渡合理;
- 纹理灰度约定统一,白色凸起、黑色凹陷;
- 目标区域筛选数量正常;
- 深度不低于层高的 2 倍;
- 导出后体积变化在预期范围;
- 切片软件能正常识别且无非流形提示;
- 参数文件和原始文件已备份。
7.3 扩展方向
当顶点位移方案满足不了生产需求时,可以往下几个方向扩展。
一是引入布尔运算。用manifold3d作为 trimesh 的布尔后端,把凸起图形做成独立的实体,再与晶格体做并集,可以得到更深的浮雕结构;凹刻则做差集。布尔运算是稳定但慢的方案,适合小批量高精度模型。
二是曲面 uv 映射。本文中 xy 投影适合平面或近似平面表面。真实的晶格鞋底表面通常是复杂曲面,需要先包围盒映射到圆柱坐标或球坐标,再采样纹理。此时灰度图像素的坐标不再是直接对应 xy。
三是体素纹理。把灰度图沿打印方向扩展成体数据,再用 Marching Cubes 提取封闭表面,可以直接生成带表面纹理的实体晶格。这个方案能避免顶点位移自交问题,但需要更多内存和更强的网格修复能力。
7.4 建议练习路径
如果第一次接触这类处理,建议按三步练习:
- 用一个 30×30×10 mm 的实心长方体跑通 3.5 节工具,确认浮雕能正确生成并导出;
- 换成一个已经做好的简单晶格体 STL,调整筛选条件和深度,观察纹理在晶格杆上的表现;
- 再尝试 4.2 节生成 Gyroid,加厚成实体后叠加表面图形,完成从零到成品晶格体的完整链路。
这三步走完,自定义表面图形晶格体就不再是一个黑盒概念,而是一套可调参数、可排查、可批量复用的工程工具。实际项目里换模型、换图案、换打印工艺,都是调整同一组参数的事。