1 如何在光线跟踪和碰撞检测上下文中使用NanoVDB 库的示例-德赢Vwin官网 网
0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何在光线跟踪和碰撞检测上下文中使用NanoVDB 库的示例

星星科技指导员 来源:NVIDIA 作者:NVIDIA 2022-04-28 14:22 次阅读

开放式 VDB 是奥斯卡奖获奖的稀疏动态卷的行业标准库。在整个视觉效果行业中,它被用于vwin 和渲染水、火、烟、云和大量其他依赖于稀疏体积数据的效果。该库包括一个分层的、动态的数据结构和一套工具,用于高效地存储和操作三维网格上离散的稀疏体数据。库由 学院软件基金会( ASWF ) 维护。有关详细信息,请参见 VDB :具有动态拓扑的高分辨率稀疏卷 。

尽管 OpenVDB 提供了性能优势,但它的设计并没有考虑到 GPUs 。它对几个外部库的依赖使得利用 GPUs 上的 VDB 数据变得很麻烦,这正是本文主题的动机。我们将向您介绍 NanoVDB 库,并提供一些如何在光线跟踪和碰撞检测上下文中使用它的示例。

NanoVDB 简介

最初在 NVIDIA 开发的 NanoVDB 库是一个 ASWF OpenVDB 项目的新增功能 。它提供了一个与 OpenVDB 的核心数据结构完全兼容的简化表示,具有在 NanoVDB 和 OpenVDB 数据结构之间来回转换、创建和可视化数据的功能。

poYBAGJqMpWAAPy-AAD5OmBKd9k476.png

图 1 OpenVDB 和 NanoVDB 数据结构的图示。

NanoVDB 采用了 VDB 树结构的压缩、线性化、只读表示(图 1 ),这使得它适合于树层次结构的快速传输和快速、无指针遍历。为了提高效率,数据流经过调整,可以在 GPUs 和 CPU 上使用。

创建 NanoVDB 网格

尽管 NanoVDB 网格是一种只读数据结构,但该库包含生成或加载数据的功能。

所有的 OpenVDB 网格类 – LevelSets 、 FogVolumes 、 PointIndexGrids 和 PointDataGrids ——都支持 NanoVDB 表示,并且可以直接从 OpenVDB 文件(即 。 vdb 系统 文件)加载。还可以将数据加载或保存到 NanoVDB 自己的文件格式中或从中保存,该格式本质上是其内存流的一个转储,其中包含用于高效检查的附加元数据。

以下代码示例从 OpenVDB 文件转换:

以下代码示例从 OpenVDB 文件转换:

openvdb::io::File file(fileName);
auto vdbGrid = file.readGrid(gridName);
auto handle = nanovdb::openToNanoVDB(vdbGrid);

虽然从现有的 OpenVDB 数据加载是典型的用例,但是附带的网格生成器工具允许您直接在内存中构建 NanoVDB 网格。提供了一些简单原语的函数来帮助您入门:

// generate a sparse narrow-band level set (i.e. truncated signed distance field) representation of a sphere.
auto handle = nanovdb::createLevelSetSphere(50, nanovdb::Vec3f(0));

下面的示例显示了如何使用 lambda 函数生成小而密集的体积(图 2 ):

nanovdb::GridBuilder builder(0);
auto op = [](const nanovdb::Coord& ijk) -> float { return menger(nanovdb::Vec3f(ijk) * 0.01f);
};
builder(op, nanovdb::CoordBBox(nanovdb::Coord(-100), nanovdb::Coord(100)));
// create a FogVolume grid called "menger" with voxel-size 1
auto handle = builder.getHandle<>(1.0, nanovdb::Vec3d(0), "menger", nanovdb::GridClass::FogVolume);

网格控制柄

网格句柄 是一个简单的类,它拥有它分配的缓冲区的所有权,允许网格的范围划分( RAII )。

它还用于封装不透明的网格数据。尽管网格数据本身是以数据类型(如 浮动 为模板的),但句柄提供了一种方便的方法来访问网格的元数据,而不必知道网格的数据类型 MIG 是什么。这很有用,因为您可以纯粹从句柄确定 GridType 。

下面的代码示例验证是否有包含级别集函数的 32 位浮点网格:

const nanovdb::GridMetaData* metadata = handle.gridMetaData();
if (!metadata->isLevelSet() || !metadata->gridType() == GridType::Float) throw std::runtime_error("Not the right stuff!");

网格缓冲区

NanoVDB 被设计成支持许多不同的平台, CPU , CUDA 甚至图形 api 。为了实现这一点,数据结构被存储在一个平坦的连续内存缓冲区中。

使这个缓冲区驻留在 CUDA 设备上很简单。为了完全控制,您可以使用 CUDA api 分配设备内存,然后将句柄的数据上载到其中。

void* d_gridData;
cudaMalloc(&d_gridData, handle.size());
cudaMemcpy(d_gridData, handle.data(), handle.size(), cudaMemcpyHostToDevice);
const nanovdb::FloatGrid* d_grid = reinterpret_cast(d_gridData);

NanoVDB 的 GridHandle 模板化在缓冲区类型上,缓冲区类型是其内存分配的包装器。它默认为使用主机系统内存的主机缓冲区;然而, NanoVDB 还提供了CUDA 缓冲器,以便轻松创建 CUDA 设备缓冲区。

auto handle = nanovdb::openToNanoVDB<nanovdb::CudaDeviceBuffer>(vdbGrid);
handle->deviceUpload();
const nanovdb::FloatGrid* grid = handle->deviceGrid();

将数据流解释为纳米网格类型后,可以使用这些方法访问网格中的数据。有关更多详细信息,请参阅相关 API 的文档。本质上,它反映了 OpenVDB 中只读方法的基本子集。

auto hostOrDeviceOp = [grid] __host__ __device__ (nanovdb::Coord ijk) -> float {
  // Note that ReadAccessor (see below) should be used for performance.
  return grid->tree().getValue(ijk);
};

可以构造自定义缓冲区来处理不同的内存空间。有关创建可与图形 API 交互操作的缓冲区的示例的更多信息,请参阅存储库中的示例。

致使

由于 NanoVDB 网格提供了一个紧凑的只读 VDB 树,因此它们很适合渲染任务。光线将 VDB 网格跟踪到图像中。使用每线程一条光线,并使用一个自定义的 雷吉诺 functor 生成光线,该函数接受像素偏移并创建世界空间光线。完整的代码在存储库示例中可用。

考虑到沿射线采样具有空间相干性这一事实,可以使用 读写器 来加速采样。当光线向前移动时,这会缓存树遍历堆栈,从而允许自底向上的树遍历,这比传统的自上而下遍历要快得多,后者涉及相对较慢的无界根节点。

auto renderTransmittanceOp = [image, grid, w, h, rayGenOp, imageOp, dt] __host__ __device__ (int i) {
    nanovdb::Ray wRay = rayGenOp(i, w, h);
    // transform the ray to the grid's index-space...
    nanovdb::Ray iRay = wRay.worldToIndexF(*grid);
    // clip to bounds.
    if (iRay.clip(grid->tree().bbox()) == false) {
        imageOp(image, i, w, h, 1.0f);
        return;
    }
    // get an accessor.
    auto acc = grid->tree().getAccessor();
    // integrate along ray interval...
    float transmittance = 1.0f;
    for (float t = iRay.t0(); t < iRay.t1(); t+=dt) {
        float sigma = acc.getValue(nanovdb::Coord::Floor(iRay(t)));
        transmittance *= 1.0f - sigma * dt;
    }
    imageOp(image, i, w, h, transmittance );
};

由于光线与水平集网格相交是一项常见任务, NanoVDB 实现了一个零交叉功能,并使用分层 DDA ( HDDA )作为沿光线的根搜索的一部分来清空空间跳跃(图 5 )。有关 HDDA 的更多信息,请参阅 OpenVDB 中高效光线行进的分层数字微分分析仪 。下面是代码示例:

...
    auto acc = grid->tree().getAccessor();
    // intersect with zero level-set...
    float iT0;
    nanovdb::Coord ijk;
    float v;
    if (nanovdb::ZeroCrossing(iRay, acc, ijk, v, iT0)) { 
        // convert intersection distance (iT0) to world-space
        float wT0 = iT0 * grid->voxelSize();
        imageOp(image, i, w, h, wT0);
    } else {
        imageOp(image, i, w, h, 0.0f);
    }
...

碰撞检测

碰撞检测和解决是 NanoVDB 的另一项任务,因为它们通常需要有效地查找实体碰撞对象的有符号距离值。窄带电平集表示非常理想,因为它们用符号对内部/外部拓扑信息(碰撞检测所需)进行了紧凑编码。此外,最近点变换(冲突解决所需的)很容易从水平集函数的梯度计算。

下面的代码示例是一个用于处理冲突的函数。使用 读写器 是很有用的,因为用于冲突解决的梯度计算涉及到同一空间附近的多个提取。

auto collisionOp = [grid, positions, velocities, dt] __host__ __device__ (int i) {
    nanovdb::Vec3f wPos = positions[i];
    nanovdb::Vec3f wVel = velocities[i];
    nanovdb::Vec3f wNextPos = wPos + wVel * dt;
    // transform the position to a custom space...
    nanovdb::Vec3f iNextPos = grid.worldToIndexF(wNextPos);
    // the grid index coordinate.
    nanovdb::Coord ijk = nanovdb::Coord::Floor(iNextPos);
    // get an accessor.
    auto acc = grid->tree().getAccessor();
    if (tree.isActive(ijk)) { // are you inside the narrow band?
        float wDistance = acc.getValue(ijk);
        if (wDistance <= 0) { // are you inside the levelset?
            // get the normal for collision resolution.
            nanovdb::Vec3f normal(wDistance);
            ijk[0] += 1;
            normal[0] += acc.getValue(ijk);
            ijk[0] -= 1;
            ijk[1] += 1;
            normal[1] += acc.getValue(ijk);
            ijk[1] -= 1;
            ijk[2] += 1;
            normal[2] += acc.getValue(ijk);
            normal.normalize();
            
            // handle collision response with the surface.
            collisionResponse(wPos, wNextPos, normal, wDistance, wNextPos, wNextVel);
        }
    }
    positions[i] = wNextPos;
    velocities[i] = wNextVel;
};

同样,完整的代码可以在存储库中找到。

基准

NanoVDB 被开发成在主机和设备上同样运行良好。使用 modernCUDA 中的扩展 lambda 支持,您可以轻松地在两个平台上运行相同的代码。

本节包括比较英特尔线程构建块和 CPU CUDA 上光线跟踪和碰撞检测性能的基准测试。计时以毫秒为单位,与 NVIDIA NVIDIA 8000 相比,是在 Xeon E5-2696 v4 x2 –( 88 个 CPU 线程)上生成的。使用的 FogVolume 是兔子云, LevelSet 是 dragon 数据集。两者都可以从 OpenVDB 网站下载。渲染的分辨率为 1024×1024 。这次碰撞试验模拟了一亿颗弹道粒子。

虽然基准测试(图 6 和下表)显示了 NanoVDB 高效表示加速 CPU 上 OpenVDB 的好处,但它真正突出了使用 GPU 对 VDB 数据进行只读访问以进行碰撞检测和光线跟踪的好处。

图 6 Intel TBB 与 CUDA 的比较(越小越好)。

结论

NanoVDB 是一个小而强大的库,它通过使用 GPUs 来加速某些 OpenVDB 应用程序。开源存储库现在可用了!要下载源代码、构建示例

关于作者

Wil Braithwaite 在伦敦和洛杉矶的工作室工作了 15 年的视觉特效。他的职位包括研究、技术指导、合成、 CG 监督和 MOCAP 监督。他开创了图形硬件在 VFX 管道中的应用,在 NVIDIA 担任高级应用工程师,专门从事咨询、培训和使用 NVIDIA 技术协助 VFX 工作室项目的开发。

Ken Museth 是模拟技术的高级主管,并于 2020 年初加入 NVIDIA ,当时他发起了 NanoVDB 的开发。他以前在开发虚拟现实技术的时候,一直致力于虚拟现实的开发。他是 VDB 的创建者和 OpenVDB 的首席架构师,也是其技术指导委员会的主席。此外,肯在 SpaceX 公司工作了六年,对新的猛禽火箭发动机进行大规模流体动力学模拟。在 2017 年加入 Weta 之前,他在梦工厂动画和数字领域工作了 10 年,在此之前,他曾在加州理工学院和林科平大学担任研究员和全职教授。他拥有哥本哈根大学量子动力学博士学位,并获得电影艺术与科学学院颁发的技术成就奖。肯是 SIGGRAPH 2020 技术论文委员会成员。

审核编辑:郭婷

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表德赢Vwin官网 网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • NVIDIA
    +关注

    关注

    14

    文章

    4978

    浏览量

    102981
  • gpu
    gpu
    +关注

    关注

    28

    文章

    4729

    浏览量

    128887
收藏 人收藏

    评论

    相关推荐

    Wi-Fi资产跟踪应用示例概述

    SiliconLabs(亦称“芯科科技”)近期在GitHub资源中发布了一个基于最新SiWx917 Wi-Fi SoC的Wi-Fi资产跟踪(Asset Tracking)应用示例。此应用程序演示如何将SiWG917模块配置为A
    的头像 发表于 11-08 14:47 314次阅读

    3D霍尔传感器在扫地机器人中用于碰撞检测

    德赢Vwin官网 网站提供《3D霍尔传感器在扫地机器人中用于碰撞检测.pdf》资料免费下载
    发表于 09-06 10:26 0次下载
    3D霍尔传感器在扫地机器人中用于<b class='flag-5'>碰撞检测</b>

    SystemView上下文统计窗口识别阻塞原因

    SystemView工具可以记录嵌入式系统的运行时行为,实现可视化的深入分析。在新发布的v3.54版本中,增加了一项新功能:上下文统计窗口,提供了对任务运行时统计信息的深入分析,使用户能够彻底检查每个任务,帮助开发人员识别阻塞原因。
    的头像 发表于 08-20 11:31 427次阅读

    何在FreeRTOS操作系统上跑RT-Thread?

    我现在有个项目用的MCU 内核是很小众的,芯片厂家仅支持freertos,我现在想把rt-thread弄上去跑,不知道该怎么实现开关中断以及上下文切换等,能提供帮助吗? 底层继续使用freertos,我在应用中使用rt-thread
    发表于 07-09 08:30

    何在IDF框架中使用自定义的静态和动态

    基于商业需要,我们需要在 ESP-IDF v4.0-rc 这个版本的IDF中开发与使用自定义,有如下问题请协助: 1如何利用IDF框架编写自定义静态和动态? 2如何在IDF框架
    发表于 06-25 07:57

    鸿蒙Ability Kit(程序框架服务)【应用上下文Context】

    [Context]是应用中对象的上下文,其提供了应用的一些基础信息,例如resourceManager(资源管理)、applicationInfo(当前应用信息)、dir(应用文件路径)、area
    的头像 发表于 06-06 09:22 478次阅读
    鸿蒙Ability Kit(程序框架服务)【应用<b class='flag-5'>上下文</b>Context】

    编写一个任务调度程序,在上下文切换后遇到了一些问题求解

    大家好, 我正在编写一个任务调度程序,在上下文切换后遇到了一些问题。 为下一个任务恢复上下文后: __builtin_tricore_mtcr_by_name(\"pcxi\"
    发表于 05-22 07:50

    JPEG LS算法局部梯度值计算原理

    如果同一个上下文中对少量元素进行编码,通常无法获得足够的上下文编码信息。但是如果对大量元素进行编码又会带来存储空间变大的问题。因此要对局部梯度值进行量化处理。
    的头像 发表于 04-25 10:46 476次阅读
    JPEG LS算法局部梯度值计算原理

    Linux内存故障追踪的实用技术指南

    使用kprobe跟踪swap_readpage()内核函数,这会在触发换页所在的进程上下文中进行,可以跟踪触发换页操作的进程的信息。展示了哪个进程正在从换页设备中换入页,前提是系统中有正在使用的换页设备。
    发表于 04-01 14:27 606次阅读
    Linux内存故障追踪的实用技术指南

    TC397收到EVAL_6EDL7141_TRAP_1SH 3上下文管理EVAL_6EDL7141_TRAP_1SH错误怎么解决?

    我收到EVAL_6EDL7141_TRAP_1SH 3 类(TIN4-Free 上下文列表下溢)上下文管理EVAL_6EDL7141_TRAP_1SH错误。 请告诉我解决这个问题的办法。
    发表于 03-06 08:00

    请问risc-v中断还需要软件保存上下文和恢复吗?

    risc-v中断还需要软件保存上下文和恢复吗?
    发表于 02-26 07:40

    何在测试中使用ChatGPT

    Dimitar Panayotov 在 2023 年 QA Challenge Accepted 大会 上分享了他如何在测试中使用 ChatGPT。
    的头像 发表于 02-20 13:57 744次阅读

    何在ModustoolBox中使用XMC4000系列

    我发现 BSP Assistant 只能用于 XMC7000 系列,但我使用的是 XMC4000 系列。 如何在 ModustoolBox 中使用 XMC4000 系列
    发表于 01-24 06:16

    TC39x如何在用户模式下访问外围设备?

    你好, 我已经在 TC39x 中启用了用户模式 1,并希望在用户模式下允许访问一些外围设备和功能。 我主要想在用户模式下使用 cpu endinit(在启用和禁用看门狗的上下文中)。
    发表于 01-22 06:52

    ISR的上下文保存和恢复是如何完成的?

    函数:ifxCPU_enableInterrupts ();如果我让更高优先级的 ISR 中断优先级较低的 ISR,那么 ISR 的上下文保存和恢复是如何完成的?
    发表于 01-22 06:28