Files
ViewDesignEngine/docs/tutorial/02-sdf-modeling.md
T
2026-07-24 12:42:49 +00:00

10 KiB
Raw Blame History

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

#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

#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 unionk 控制过渡弧度
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 函数求值:

#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

#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 Cubes64³ 分辨率
    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+ 顶点

完整示例:齿轮形状

#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 查看效果。