334 lines
10 KiB
Markdown
334 lines
10 KiB
Markdown
|
|
# SDF 隐式建模教程
|
|||
|
|
|
|||
|
|
有符号距离函数(Signed Distance Function)是 ViewDesignEngine 的核心建模方式之一。
|
|||
|
|
通过数学函数直接定义几何体,无需依赖网格或曲面表示。
|
|||
|
|
|
|||
|
|
## 核心概念
|
|||
|
|
|
|||
|
|
SDF 函数 `f(p)` 返回点 `p` 到最近表面的有符号距离:
|
|||
|
|
|
|||
|
|
- `f(p) < 0`:点在几何体**内部**
|
|||
|
|
- `f(p) = 0`:点在几何体**表面**上
|
|||
|
|
- `f(p) > 0`:点在几何体**外部**
|
|||
|
|
|
|||
|
|
## 基本图元(球、盒、环)
|
|||
|
|
|
|||
|
|
ViewDesignEngine 提供的内建 SDF 图元,定义在 `vde/sdf/sdf_primitives.h`:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/sdf/sdf_primitives.h>
|
|||
|
|
using namespace vde::sdf;
|
|||
|
|
using namespace vde::core;
|
|||
|
|
|
|||
|
|
// 球体:位于原点,半径 1.0
|
|||
|
|
double d = sphere(Point3D(1.0, 0.0, 0.0), 1.0);
|
|||
|
|
// d ≈ 0.0(点在球面上)
|
|||
|
|
|
|||
|
|
// 轴对齐盒子:以原点为中心,半长为 halfExtent
|
|||
|
|
auto box_fn = [](double x, double y, double z) {
|
|||
|
|
return box(Point3D(x, y, z), Point3D(1.5, 1.5, 0.5));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 环面(torus):major_radius = 环半径,minor_radius = 管半径
|
|||
|
|
auto torus_fn = [](double x, double y, double z) {
|
|||
|
|
return torus(Point3D(x, y, z), 2.0, 0.4);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 椭球:指定各轴半径
|
|||
|
|
auto ellipsoid_fn = [](double x, double y, double z) {
|
|||
|
|
return ellipsoid(Point3D(x, y, z), Point3D(2.0, 1.0, 1.5));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 圆柱(沿 Y 轴)
|
|||
|
|
auto cylinder_fn = [](double x, double y, double z) {
|
|||
|
|
return cylinder(Point3D(x, y, z), 0.5, 3.0);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 胶囊体:两点 + 半径
|
|||
|
|
auto capsule_fn = [](double x, double y, double z) {
|
|||
|
|
return capsule(Point3D(x, y, z),
|
|||
|
|
Point3D(0, -2, 0), Point3D(0, 2, 0), 0.4);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 无限长圆柱:沿任意轴
|
|||
|
|
auto inf_cyl = [](double x, double y, double z) {
|
|||
|
|
return infinite_cylinder(Point3D(x, y, z),
|
|||
|
|
Vector3D(0, 0, 1), 0.5);
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## CSG 操作(并、交、差、光滑并集)
|
|||
|
|
|
|||
|
|
布尔组合运算定义在 `vde/sdf/sdf_operations.h`:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/sdf/sdf_primitives.h>
|
|||
|
|
#include <vde/sdf/sdf_operations.h>
|
|||
|
|
using namespace vde::sdf;
|
|||
|
|
|
|||
|
|
// === 标准布尔运算 ===
|
|||
|
|
|
|||
|
|
// 并集 A ∪ B:取最小值(两个几何体的合并)
|
|||
|
|
auto union_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
return op_union(sphere(p, 1.0),
|
|||
|
|
box(p, Point3D(0.8, 0.8, 0.8)));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 交集 A ∩ B:取最大值(两个几何体的共同区域)
|
|||
|
|
auto intersect_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
return op_intersection(sphere(p, 1.0),
|
|||
|
|
box(p, Point3D(0.6, 0.6, 0.6)));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 差集 A \ B:从 A 中减去 B
|
|||
|
|
auto diff_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
return op_difference(sphere(p, 1.0),
|
|||
|
|
box(p, Point3D(0.4, 0.4, 0.4)));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// === 光滑布尔运算(带有机融合过渡) ===
|
|||
|
|
|
|||
|
|
// smooth union,k 控制过渡弧度
|
|||
|
|
auto smooth_union_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
double d1 = sphere(p, 1.0);
|
|||
|
|
double d2 = sphere(p - Point3D(1.5, 0, 0), 1.0);
|
|||
|
|
return op_smooth_union(d1, d2, 0.3); // k=0.3 适中的融合
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// smooth intersection
|
|||
|
|
auto smooth_isect_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
double d1 = sphere(p, 1.0);
|
|||
|
|
double d2 = box(p, Point3D(0.8, 0.8, 0.8));
|
|||
|
|
return op_smooth_intersection(d1, d2, 0.2);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// smooth difference
|
|||
|
|
auto smooth_diff_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
double d1 = box(p, Point3D(1.5, 1.5, 1.5));
|
|||
|
|
double d2 = sphere(p, 0.8);
|
|||
|
|
return op_smooth_difference(d1, d2, 0.2);
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 域变形(重复、镜像、扭转)
|
|||
|
|
|
|||
|
|
域变形算子对采样点做空间变换,再传入 SDF 函数求值:
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/sdf/sdf_operations.h>
|
|||
|
|
using namespace vde::sdf;
|
|||
|
|
|
|||
|
|
// 无限重复:将空间单元化,在每个单元内放置几何体
|
|||
|
|
auto repeated_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
// 以 (2, 2, 2) 为周期重复
|
|||
|
|
Point3D q = op_repeat(p, Point3D(2.0, 2.0, 2.0));
|
|||
|
|
return sphere(q, 0.5);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 镜像:沿 YZ 平面对称(x 方向镜像,offset 为对称轴位置)
|
|||
|
|
auto mirrored_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
Point3D q = op_mirror_x(p, 0.0); // 以 x=0 为镜像轴
|
|||
|
|
return sphere(q - Point3D(1.5, 0, 0), 0.6);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 多维镜像组合
|
|||
|
|
auto quad_mirror = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
Point3D q = op_mirror_x(op_mirror_y(p));
|
|||
|
|
return sphere(q - Point3D(2.0, 2.0, 0), 0.5);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 平移 / 旋转 / 缩放
|
|||
|
|
auto transformed_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
// sdf_operations.h 提供 op_translate, op_rotate, op_scale 等
|
|||
|
|
// 注意:是逆变换——要移动几何体,需反向移动采样点
|
|||
|
|
Point3D q = op_translate(p, Point3D(2.0, 0, 0)); // 几何体向右移 2
|
|||
|
|
return sphere(q, 1.0);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 扭转(沿 Y 轴)
|
|||
|
|
#include <cmath>
|
|||
|
|
auto twisted_shape = [](double x, double y, double z) -> double {
|
|||
|
|
double angle = y * 0.5; // 扭转角度与 y 坐标成正比
|
|||
|
|
double sx = std::sin(angle);
|
|||
|
|
double cx = std::cos(angle);
|
|||
|
|
double rx = cx * x - sx * z;
|
|||
|
|
double rz = sx * x + cx * z;
|
|||
|
|
Point3D q(rx, y, rz);
|
|||
|
|
return box(q, Point3D(0.8, 3.0, 0.3));
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 修饰算子:圆角、壳
|
|||
|
|
auto decorated_shape = [](double x, double y, double z) {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
double d = box(p, Point3D(1.0, 1.0, 1.0));
|
|||
|
|
// op_round: 对所有边做圆角(近似 round_box)
|
|||
|
|
d = op_round(d, 0.1);
|
|||
|
|
// op_onion: 创建壳层(空心厚度)
|
|||
|
|
// d = op_onion(d, 0.05);
|
|||
|
|
return d;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 从 SDF 到网格(Marching Cubes)
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/sdf/sdf_primitives.h>
|
|||
|
|
#include <vde/sdf/sdf_operations.h>
|
|||
|
|
#include <vde/mesh/marching_cubes.h>
|
|||
|
|
#include <vde/mesh/halfedge_mesh.h>
|
|||
|
|
#include <vde/foundation/io_gltf.h>
|
|||
|
|
|
|||
|
|
using namespace vde::core;
|
|||
|
|
using namespace vde::sdf;
|
|||
|
|
using namespace vde::mesh;
|
|||
|
|
using namespace vde::foundation;
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
// 定义 SDF 函数
|
|||
|
|
auto shape = [](double x, double y, double z) -> double {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
double d = box(p, Point3D(2.0, 2.0, 2.0));
|
|||
|
|
d = op_round(d, 0.15); // 圆角
|
|||
|
|
return d;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 采样区域:[-3,3]³
|
|||
|
|
Point3D bmin(-3, -3, -3), bmax(3, 3, 3);
|
|||
|
|
|
|||
|
|
// Marching Cubes:64³ 分辨率
|
|||
|
|
auto mc = marching_cubes(shape, 0.0, bmin, bmax, 64);
|
|||
|
|
|
|||
|
|
// 构建半边网格并导出
|
|||
|
|
HalfedgeMesh mesh;
|
|||
|
|
mesh.build_from_triangles(mc.vertices, mc.triangles);
|
|||
|
|
|
|||
|
|
GltfOptions opts;
|
|||
|
|
opts.binary = true;
|
|||
|
|
opts.include_normals = true;
|
|||
|
|
write_gltf("rounded_box.glb", mesh, opts);
|
|||
|
|
|
|||
|
|
std::cout << "Vertices: " << mesh.num_vertices()
|
|||
|
|
<< ", Faces: " << mesh.num_faces() << "\n";
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**分辨率选择指南:**
|
|||
|
|
|
|||
|
|
| 分辨率 | 适用场景 | 输出网格规模 |
|
|||
|
|
|--------|----------|-------------|
|
|||
|
|
| 32³ | 快速预览 | ~2K 顶点 |
|
|||
|
|
| 64³ | 一般用途 | ~8K 顶点 |
|
|||
|
|
| 128³ | 精细表面 | ~30K 顶点 |
|
|||
|
|
| 256³ | 高精度需求 | ~100K+ 顶点 |
|
|||
|
|
|
|||
|
|
## 完整示例:齿轮形状
|
|||
|
|
|
|||
|
|
```cpp
|
|||
|
|
#include <vde/sdf/sdf_primitives.h>
|
|||
|
|
#include <vde/sdf/sdf_operations.h>
|
|||
|
|
#include <vde/mesh/marching_cubes.h>
|
|||
|
|
#include <vde/mesh/halfedge_mesh.h>
|
|||
|
|
#include <vde/foundation/io_gltf.h>
|
|||
|
|
|
|||
|
|
#include <iostream>
|
|||
|
|
#include <cmath>
|
|||
|
|
|
|||
|
|
using namespace vde::core;
|
|||
|
|
using namespace vde::sdf;
|
|||
|
|
using namespace vde::mesh;
|
|||
|
|
using namespace vde::foundation;
|
|||
|
|
|
|||
|
|
int main() {
|
|||
|
|
const double M_PI = 3.14159265358979323846;
|
|||
|
|
const double gear_radius = 2.0; // 齿轮半径
|
|||
|
|
const double tooth_count = 12.0; // 齿数
|
|||
|
|
const double tooth_depth = 0.3; // 齿深
|
|||
|
|
const double gear_thickness = 0.5; // 齿轮厚度
|
|||
|
|
|
|||
|
|
auto gear_sdf = [&](double x, double y, double z) -> double {
|
|||
|
|
Point3D p(x, y, z);
|
|||
|
|
|
|||
|
|
// 圆柱主体(沿 Y 轴的薄圆盘)
|
|||
|
|
double d_cylinder = cylinder(Point3D(x, y, z), gear_radius, gear_thickness);
|
|||
|
|
|
|||
|
|
// 中心孔
|
|||
|
|
double d_hole = cylinder(Point3D(x, y, z), 0.4, gear_thickness * 2);
|
|||
|
|
|
|||
|
|
// 差集:打孔
|
|||
|
|
double d = op_difference(d_cylinder, d_hole);
|
|||
|
|
|
|||
|
|
// 齿轮齿:沿圆周等距分布
|
|||
|
|
double angle = std::atan2(z, x);
|
|||
|
|
double radius = std::sqrt(x * x + z * z);
|
|||
|
|
|
|||
|
|
// 每个齿是一个小 Box 沿径向放置
|
|||
|
|
double teeth_union = 1e10;
|
|||
|
|
for (int i = 0; i < tooth_count; i++) {
|
|||
|
|
double theta = i * 2.0 * M_PI / tooth_count;
|
|||
|
|
double ca = std::cos(theta);
|
|||
|
|
double sa = std::sin(theta);
|
|||
|
|
|
|||
|
|
// 旋转采样点到该齿的局部空间
|
|||
|
|
double lx = ca * x + sa * z;
|
|||
|
|
double lz = -sa * x + ca * z;
|
|||
|
|
Point3D lp(lx, y, lz);
|
|||
|
|
|
|||
|
|
double tooth = box(lp,
|
|||
|
|
Point3D(0.3, 0.6, gear_radius + tooth_depth));
|
|||
|
|
// 向圆心方向偏移
|
|||
|
|
double d_tooth = op_translate_along_x(lp,
|
|||
|
|
gear_radius + tooth_depth * 0.5).norm() > 0
|
|||
|
|
? box(Point3D(lx - (gear_radius + tooth_depth * 0.5), y, lz),
|
|||
|
|
Point3D(0.15, gear_thickness * 1.1, tooth_depth * 0.5))
|
|||
|
|
: 1e10;
|
|||
|
|
|
|||
|
|
// 用旋转矩阵的逆变换做齿的径向放置
|
|||
|
|
double r = std::sqrt(x * x + z * z);
|
|||
|
|
double dx = r - (gear_radius + tooth_depth * 0.5);
|
|||
|
|
double dy_abs = std::abs(y);
|
|||
|
|
double dz = std::abs(std::atan2(z, x) - theta);
|
|||
|
|
if (dz > M_PI) dz = 2 * M_PI - dz;
|
|||
|
|
dz = dz * gear_radius;
|
|||
|
|
|
|||
|
|
double tooth_dist = std::sqrt(dx * dx + dy_abs * dy_abs + dz * dz)
|
|||
|
|
- tooth_depth * 0.5;
|
|||
|
|
if (dy_abs <= gear_thickness * 0.55 && r >= gear_radius - 0.05) {
|
|||
|
|
teeth_union = std::min(teeth_union, tooth_dist);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
d = op_union(d, teeth_union);
|
|||
|
|
return d;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Marching Cubes
|
|||
|
|
Point3D bmin(-3, -3, -3), bmax(3, 3, 3);
|
|||
|
|
auto mc = marching_cubes(gear_sdf, 0.0, bmin, bmax, 128);
|
|||
|
|
|
|||
|
|
HalfedgeMesh mesh;
|
|||
|
|
mesh.build_from_triangles(mc.vertices, mc.triangles);
|
|||
|
|
|
|||
|
|
GltfOptions opts;
|
|||
|
|
opts.binary = true;
|
|||
|
|
opts.include_normals = true;
|
|||
|
|
write_gltf("gear.glb", mesh, opts);
|
|||
|
|
|
|||
|
|
std::cout << "Gear exported: " << mesh.num_vertices()
|
|||
|
|
<< " vertices, " << mesh.num_faces() << " faces\n";
|
|||
|
|
return 0;
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
编译运行,用 `viewer/index.html` 查看效果。
|