简介
OpenSubdiv 是一套开源库,用于在大规模并行 CPU 和 GPU 架构上求值和渲染细分曲面。由 Pixar 开发,现由 Academy Software Foundation 维护,它是细分曲面计算的行业标准。
细分曲面允许艺术家使用简单的控制笼对复杂的有机形状进行建模,在渲染时进行平滑细分。
什么是细分曲面?
细分曲面由粗糙的控制网格和一组细分规则定义。在每个级别,网格通过根据这些规则添加新的顶点和面来进行细化。
| 模型类型 | 控制网格 | 细分结果 |
|---|
| 简单立方体 | 6 个面,8 个顶点 | 平滑的类球体形状 |
| 角色头部 | ~500 个面 | 数百万个平滑多边形 |
| 车辆 | ~2000 个面 | 平滑的面板和曲线 |
为什么使用细分曲面?
| 优势 | 描述 |
|---|
| 平滑曲面 | 在奇异点处 C2 连续 |
| 拓扑灵活性 | 任何网格拓扑都适用 |
| 内存效率 | 小控制笼,详细结果 |
| 动画友好 | 变形控制网格,而非密集网格 |
| 行业标准 | 所有主要 DCC 工具都使用 |
细分方案
OpenSubdiv 支持多种细分方案。
| 方案 | 创建者 | 属性 | 用例 |
|---|
| Catmull-Clark | Catmull & Clark | 规则处 C2,奇异处 C1 | 电影角色 |
| Loop | Charles Loop | 基于三角形,规则处 C2 | 游戏角色 |
| Bilinear | - | 简单边分割 | 平面面板 |
| 元素 | 规则 | 结果 |
|---|
| 面点 | 面顶点的平均值 | 每个面的中心 |
| 边点 | 边端点 + 相邻面点的平均值 | 平滑的边中点 |
| 顶点点 | 邻域的加权平均值 | 平滑的顶点位置 |
求值模式
OpenSubdiv 提供多种求值方法。
| 模式 | 描述 | 性能 | 用例 |
|---|
| CPU Uniform | 标准 CPU 细分 | 良好 | 离线渲染 |
| CPU Feature Adaptive | 自适应细化 | 更好 | 动画 |
| GPU Compute | OpenCL/CUDA 内核 | 最快 | 实时预览 |
| GPU Vertex | 顶点着色器方法 | 快 | 游戏引擎 |
安装
从源码构建
git clone https://github.com/AcademySoftwareFoundation/OpenSubdiv.git
cd OpenSubdiv
mkdir build && cd build
cmake .. \
-DCMAKE_INSTALL_PREFIX=/usr/local \
-DNO_TBB=OFF \
-DNO_CUDA=OFF \
-DNO_OPENCL=OFF
make -j$(nproc)
sudo make install
依赖项
| 依赖 | 必需 | 用途 |
|---|
| CMake | 是 | 构建系统 |
| TBB | 可选 | 多线程 |
| OpenCL | 可选 | GPU 计算 |
| CUDA | 可选 | NVIDIA GPU 计算 |
| OpenGL | 可选 | GPU 渲染 |
| GLFW | 可选 | 示例 |
包管理器
| 平台 | 命令 |
|---|
| macOS | brew install opensubdiv |
| Ubuntu | sudo apt install libopensubdiv-dev |
| vcpkg | vcpkg install opensubdiv |
核心概念
拓扑细化
拓扑细化决定网格连接在每个级别如何变化。
| 级别 | 描述 | 顶点数倍增 |
|---|
| 0 | 控制笼 | 1x |
| 1 | 第一次细分 | ~4x (Catmull-Clark) |
| 2 | 第二次细分 | ~16x |
| N | 第 N 次细分 | ~4^N x |
面片表
面片表存储细分面片的求值数据。它们对于高效的 GPU 求值至关重要。
| 面片类型 | 顶点数 | 形状 | 用途 |
|---|
| Regular | 16 | 4x4 网格 | 内部面片 |
| Boundary | 12 | 边面片 | 网格边界 |
| Corner | 9 | 角面片 | 网格角落 |
| Gregory | 20 | Gregory 面片 | 奇异点 |
特征自适应细化
特征自适应细化不是均匀地细分整个网格,而是仅在奇异顶点和锐利特征附近进行细分。
| 区域 | 细分 | 原因 |
|---|
| 规则内部 | 解析面片 | 高效 GPU 求值 |
| 奇异点附近 | 额外级别 | 平滑收敛 |
| 锐利折痕 | 额外级别 | 精确折痕求值 |
| 边界 | 额外级别 | 正确的边界行为 |
模板表
模板表预先计算求值曲面上任意点所需的权重。
#include <opensubdiv/far/topologyDescriptor.h>
#include <opensubdiv/far/stencilTableFactory.h>
using namespace OpenSubdiv;
// 定义网格拓扑
Far::TopologyDescriptor desc;
desc.numVertices = numVertices;
desc.numFaces = numFaces;
desc.numVertsPerFace = vertsPerFace;
desc.vertIndicesPerFace = faceIndices;
// 创建拓扑
Far::TopologyRefiner *refiner =
Far::TopologyRefinerFactory<Far::TopologyDescriptor>::Create(desc);
// 细化拓扑
refiner->RefineUniform(Far::TopologyRefiner::UniformOptions(maxLevel));
// 构建模板表
Far::StencilTableFactory::Options options;
options.generateControlVerts = true;
options.generateIntermediateLevels = true;
Far::StencilTable const *stencilTable =
Far::StencilTableFactory::Create(*refiner, options);
求值
// 在模板点求值位置
std::vector<float> newPositions(stencilTable->GetNumStencils() * 3);
stencilTable->UpdateValues(controlPositions.data(), newPositions.data());
OpenSubdiv 有三个主要层。
| 层 | 名称 | 用途 | 性能 |
|---|
| HBR | Hierarchical Boundary Rep | 参考实现 | 慢(单线程) |
| FAR | Feature Adaptive Refinement | 生产细化 | 快(多线程) |
| OSD | OpenSubdiv | GPU 求值 | 最快(并行) |
| 层 | 用例 |
|---|
| HBR | 正确性测试、旧版兼容 |
| FAR | 生产细分、CPU 渲染 |
| OSD | 实时预览、游戏引擎 |
OpenSubdiv 通过多种后端支持 GPU 加速求值。
| 后端 | API | 供应商支持 | 性能 |
|---|
| OpenGL Compute | GLSL 4.3+ | 全部 | 非常快 |
| OpenCL | OpenCL 1.2+ | 全部 | 非常快 |
| CUDA | CUDA | NVIDIA | 最快 |
| DirectX Compute | HLSL | Windows | 非常快 |
#include <opensubdiv/osd/glPatchTable.h>
#include <opensubdiv/osd/glVertexBuffer.h>
// 创建 GPU 缓冲区
Osd::GLVertexBuffer *vbo = Osd::GLVertexBuffer::Create(
numVertices * 3, patchTable->GetPatchArrays());
// 上传控制顶点
vbo->UpdateCpuBuffer(controlPositions);
// 在 GPU 上求值
Osd::GLComputeEvaluator *evaluator = Osd::GLComputeEvaluator::Create(...);
evaluator->EvalPatches(vbo, ...);
锐利特征
折痕
折痕定义平滑曲面上的锐利边。
| 折痕权重 | 效果 |
|---|
| 0.0 | 完全平滑(默认) |
| 1.0 | 略微锐利 |
| 5.0 | 非常锐利 |
| 10.0+ | 接近硬边 |
// 设置边折痕权重
desc.numCreases = numCreaseEdges;
desc.creaseVertexIndexPairs = creaseEdges;
desc.creaseWeights = creaseWeights;
// 设置顶点折痕权重
desc.numCorners = numCornerVertices;
desc.cornerIndices = cornerIndices;
desc.cornerWeights = cornerWeights;
| 工具 | OpenSubdiv 用途 | 备注 |
|---|
| Maya | 原生 Viewport 2.0 | GPU 细分显示 |
| Houdini | 视口细分 | 实时反馈 |
| Blender | 细分修改器 | CPU 和 GPU 模式 |
| Katana | 渲染时细分 | 属性驱动 |
| 3ds Max | TurboSmooth 集成 | 基于插件 |
性能比较
| 配置 | 细分时间(1M 面到 4M 面) | 内存 |
|---|
| CPU 单线程 (HBR) | 1200ms | 低 |
| CPU 多线程 (FAR) | 180ms | 中 |
| GPU Compute (OSD) | 8ms | 较高 |
| 特征自适应 (FAR) | 90ms | 中 |
| 特征自适应 (OSD) | 4ms | 较高 |
最佳实践
| 实践 | 建议 |
|---|
| 拓扑 | 保持控制笼尽可能简单 |
| 折痕 | 谨慎使用,优先使用平滑过渡 |
| 级别 | 显示用 3-4 级,渲染用更多 |
| 面片 | 启用面片表以进行 GPU 求值 |
| 自适应 | 对复杂网格使用特征自适应 |
| 内存 | 高细分级别时监控 GPU 内存 |
附加资源
| 资源 | URL |
|---|
| 文档 | opensubdiv.org |
| GitHub | github.com/AcademySoftwareFoundation/OpenSubdiv |
| Pixar 技术备忘录 | Graphics.pixar.com/opensubdiv |
| SIGGRAPH 论文 | Graphics.pixar.com |