网格优化Remesh——Tangential Smooth

一、Laplace平滑

简单的拉普拉斯平滑算法的原理是将每个顶点都移动到相邻顶点的平均位置,即采用所谓伞状算子:

在伞状结构中表示这样的过程如下图:

拉普拉斯平滑算法有很多进一步的变形,首先在求取平均位置时,可以采用不同的加权策略,例如对不同的邻接点采用不同的权值。一般来说,距离中心点P较远的邻接点,我们可以让他对P平滑后的位置影响小一点。这样就可以采用一种距离的倒数为权值的拉普拉斯平滑。

有时为了控制平滑的速率,也会引入参数lambda来控制平滑的速率,即从原来所执行的:

转变成同时,平滑算法往往可以反复对Mesh执行,使得Mesh越来越光顺,迭代次数T也是平滑算法中重要的参数。

二、切向Laplace平滑

 右边图中的第三幅,代表reproject的效果,需要额外实现(比如用AABBtree等),在公式中没有体现。

另一种写法:

区别在于(I - ni . ni.T),还有正负号 

三、改进的切向Laplace平滑(基于面积的切向Laplace平滑)

 首先计算gi:

 然后用以下公式将gi被投影会pi的切面:

 其中ni是pi的法向,λ是阻尼系数用来防止震荡。

一些Voronoi面积大的节点拥有更大的重力,所以吸引了其它节点,因此减小了他们自身的面积。一般通过<20次迭代,面积方差可以减小5倍,最终产生一个每个节点面积大致相同的网格,如下图所示。

代码:

Eigen::Vector3d tangential_smooth(const Tuple& t)
{
    auto one_ring_tris = get_one_ring_tris_for_vertex(t);
    if (one_ring_tris.size() < 2) return vertex_attrs[t.vid(*this)].pos;
    Eigen::Vector3d after_smooth = smooth(t);  // 计算了普通Laplace平滑后的点位置
    // get normal and area of each face
    auto area = [](auto& m, auto& verts) {
        return ((m.vertex_attrs[verts[0].vid(m)].pos - m.vertex_attrs[verts[2].vid(m)].pos)
                    .cross(
                        m.vertex_attrs[verts[1].vid(m)].pos - m.vertex_attrs[verts[2].vid(m)].pos))
                   .norm() /
               2.0;
    };
    auto normal = [](auto& m, auto& verts) {
        return ((m.vertex_attrs[verts[0].vid(m)].pos - m.vertex_attrs[verts[2].vid(m)].pos)
                    .cross(
                        m.vertex_attrs[verts[1].vid(m)].pos - m.vertex_attrs[verts[2].vid(m)].pos))
            .normalized();
    };
    auto w0 = 0.0;
    Eigen::Vector3d n0(0.0, 0.0, 0.0);
    for (auto& e : one_ring_tris) {
        auto verts = oriented_tri_vertices(e);
        w0 += area(*this, verts);
        n0 += area(*this, verts) * normal(*this, verts);
    }
    n0 /= w0;
    if (n0.norm() < 1e-10) return vertex_attrs[t.vid(*this)].pos;
    n0 = n0.normalized();
    after_smooth += n0 * n0.transpose() * (vertex_attrs[t.vid(*this)].pos - after_smooth);
    assert(check_mesh_connectivity_validity());
    return after_smooth;
}

四、非流形网格的平滑 

对于non-manifold网格,上面的公式是否还适用,有待调研。

参考文献:

sgp04 M. Botsch and L. Kobbelt / Remeshing for Multiresolution Modeling

Polygon mesh processing (Botsch M., et al.) Chapter6 Section 6.5 Triangle-based Remeshing

03-19
您提到的“Unknown term, possibly a name or non-IT related word”可能是一个误解或者是表述上的误差。以下是关于 **Remesh** 的详细介绍: ### 什么是 RemeshRemesh 是一种基于 CQRS(命令查询职责分离)模式的 DDD(领域驱动设计)框架[^5],它旨在帮助开发人员构建健壮且易于维护的应用程序。通过将业务逻辑与视图层解耦,Remesh 提供了一种灵活的方式来管理和扩展应用程序的状态和行为。 #### 主要特点 1. **独立于视图层的设计** - Remesh 的核心特性之一是它的业务逻辑完全独立于任何具体的前端技术栈。这意味着无论是在 React 或 Vue 中使用该框架,都可以无缝迁移而不需要重新编写业务逻辑代码[^3]。 2. **模块化架构支持** - 开发者能够利用 NPM 上已有的社区贡献插件或自行定制模块来增强功能。这些模块可以覆盖状态管理、中间件以及与其他视图层组件的集成需求。 3. **官方资源和支持** - 官方文档提供了详尽的例子说明如何定义域模型并实现具体的功能。例如,在 GitHub 存储库中有针对 React 用户的具体指南[^2]。此外,加入活跃的社区可以帮助开发者更深入理解此工具及其最佳实践方法[^1]。 4. **高级算法整合可能性** - 对于某些复杂场景下的性能调优问题,如网格优化中的 Tangential Smooth 方法被提及作为改进三角形重划分的有效手段之一[^4]。这表明除了常规应用外,Remesh 还能应用于涉及几何处理等领域。 ### 示例代码片段展示基本概念 下面给出一段简单的 JavaScript/TypeScript 风格伪代码用来演示如何初始化一个最基础版本的 Remesh 应用: ```typescript // 导入必要的包 import { createDomain } from 'remesh'; // 创建一个新的 Domain 实例 const counterDomain = createDomain('Counter'); // 定义增加计数器的行为 counterDomain.command((state: number) => state + 1); // 初始化初始状态 const initialState = 0; export default counterDomain; ``` 上述例子展示了怎样快速搭建起具备简单增减操作能力的小型计数器服务端口原型。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值