開(kāi)放式 VDB 是奧斯卡獎(jiǎng)獲獎(jiǎng)的稀疏動(dòng)態(tài)卷的行業(yè)標(biāo)準(zhǔn)庫(kù)。在整個(gè)視覺(jué)效果行業(yè)中,它被用于模擬和渲染水、火、煙、云和大量其他依賴(lài)于稀疏體積數(shù)據(jù)的效果。該庫(kù)包括一個(gè)分層的、動(dòng)態(tài)的數(shù)據(jù)結(jié)構(gòu)和一套工具,用于高效地存儲(chǔ)和操作三維網(wǎng)格上離散的稀疏體數(shù)據(jù)。庫(kù)由 學(xué)院軟件基金會(huì)( ASWF ) 維護(hù)。有關(guān)詳細(xì)信息,請(qǐng)參見(jiàn) VDB :具有動(dòng)態(tài)拓?fù)涞母叻直媛氏∈杈?。
盡管 OpenVDB 提供了性能優(yōu)勢(shì),但它的設(shè)計(jì)并沒(méi)有考慮到 GPUs 。它對(duì)幾個(gè)外部庫(kù)的依賴(lài)使得利用 GPUs 上的 VDB 數(shù)據(jù)變得很麻煩,這正是本文主題的動(dòng)機(jī)。我們將向您介紹 NanoVDB 庫(kù),并提供一些如何在光線(xiàn)跟蹤和碰撞檢測(cè)上下文中使用它的示例。
NanoVDB 簡(jiǎn)介
最初在 NVIDIA 開(kāi)發(fā)的 NanoVDB 庫(kù)是一個(gè) ASWF OpenVDB 項(xiàng)目的新增功能 。它提供了一個(gè)與 OpenVDB 的核心數(shù)據(jù)結(jié)構(gòu)完全兼容的簡(jiǎn)化表示,具有在 NanoVDB 和 OpenVDB 數(shù)據(jù)結(jié)構(gòu)之間來(lái)回轉(zhuǎn)換、創(chuàng)建和可視化數(shù)據(jù)的功能。
圖 1 OpenVDB 和 NanoVDB 數(shù)據(jù)結(jié)構(gòu)的圖示。
NanoVDB 采用了 VDB 樹(shù)結(jié)構(gòu)的壓縮、線(xiàn)性化、只讀表示(圖 1 ),這使得它適合于樹(shù)層次結(jié)構(gòu)的快速傳輸和快速、無(wú)指針遍歷。為了提高效率,數(shù)據(jù)流經(jīng)過(guò)調(diào)整,可以在 GPUs 和 CPU 上使用。
創(chuàng)建 NanoVDB 網(wǎng)格
盡管 NanoVDB 網(wǎng)格是一種只讀數(shù)據(jù)結(jié)構(gòu),但該庫(kù)包含生成或加載數(shù)據(jù)的功能。
所有的 OpenVDB 網(wǎng)格類(lèi) – LevelSets 、 FogVolumes 、 PointIndexGrids 和 PointDataGrids ——都支持 NanoVDB 表示,并且可以直接從 OpenVDB 文件(即 。 vdb 系統(tǒng) 文件)加載。還可以將數(shù)據(jù)加載或保存到 NanoVDB 自己的文件格式中或從中保存,該格式本質(zhì)上是其內(nèi)存流的一個(gè)轉(zhuǎn)儲(chǔ),其中包含用于高效檢查的附加元數(shù)據(jù)。
以下代碼示例從 OpenVDB 文件轉(zhuǎn)換:
以下代碼示例從 OpenVDB 文件轉(zhuǎn)換:
openvdb::io::File file(fileName); auto vdbGrid = file.readGrid(gridName); auto handle = nanovdb::openToNanoVDB(vdbGrid);
雖然從現(xiàn)有的 OpenVDB 數(shù)據(jù)加載是典型的用例,但是附帶的網(wǎng)格生成器工具允許您直接在內(nèi)存中構(gòu)建 NanoVDB 網(wǎng)格。提供了一些簡(jiǎn)單原語(yǔ)的函數(shù)來(lái)幫助您入門(mén):
// 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 函數(shù)生成小而密集的體積(圖 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);
網(wǎng)格控制柄
網(wǎng)格句柄 是一個(gè)簡(jiǎn)單的類(lèi),它擁有它分配的緩沖區(qū)的所有權(quán),允許網(wǎng)格的范圍劃分( RAII )。
它還用于封裝不透明的網(wǎng)格數(shù)據(jù)。盡管網(wǎng)格數(shù)據(jù)本身是以數(shù)據(jù)類(lèi)型(如 浮動(dòng) 為模板的),但句柄提供了一種方便的方法來(lái)訪(fǎng)問(wèn)網(wǎng)格的元數(shù)據(jù),而不必知道網(wǎng)格的數(shù)據(jù)類(lèi)型 MIG 是什么。這很有用,因?yàn)槟梢约兇鈴木浔_定 GridType 。
下面的代碼示例驗(yàn)證是否有包含級(jí)別集函數(shù)的 32 位浮點(diǎn)網(wǎng)格:
const nanovdb::GridMetaData* metadata = handle.gridMetaData(); if (!metadata->isLevelSet() || !metadata->gridType() == GridType::Float) throw std::runtime_error("Not the right stuff!");
網(wǎng)格緩沖區(qū)
NanoVDB 被設(shè)計(jì)成支持許多不同的平臺(tái), CPU , CUDA 甚至圖形 api 。為了實(shí)現(xiàn)這一點(diǎn),數(shù)據(jù)結(jié)構(gòu)被存儲(chǔ)在一個(gè)平坦的連續(xù)內(nèi)存緩沖區(qū)中。
使這個(gè)緩沖區(qū)駐留在 CUDA 設(shè)備上很簡(jiǎn)單。為了完全控制,您可以使用 CUDA api 分配設(shè)備內(nèi)存,然后將句柄的數(shù)據(jù)上載到其中。
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 模板化在緩沖區(qū)類(lèi)型上,緩沖區(qū)類(lèi)型是其內(nèi)存分配的包裝器。它默認(rèn)為使用主機(jī)系統(tǒng)內(nèi)存的主機(jī)緩沖區(qū);然而, NanoVDB 還提供了CUDA 緩沖器,以便輕松創(chuàng)建 CUDA 設(shè)備緩沖區(qū)。
auto handle = nanovdb::openToNanoVDB<nanovdb::CudaDeviceBuffer>(vdbGrid); handle->deviceUpload(); const nanovdb::FloatGrid* grid = handle->deviceGrid();
將數(shù)據(jù)流解釋為納米網(wǎng)格類(lèi)型后,可以使用這些方法訪(fǎng)問(wèn)網(wǎng)格中的數(shù)據(jù)。有關(guān)更多詳細(xì)信息,請(qǐng)參閱相關(guān) API 的文檔。本質(zhì)上,它反映了 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); };
可以構(gòu)造自定義緩沖區(qū)來(lái)處理不同的內(nèi)存空間。有關(guān)創(chuàng)建可與圖形 API 交互操作的緩沖區(qū)的示例的更多信息,請(qǐng)參閱存儲(chǔ)庫(kù)中的示例。
致使
由于 NanoVDB 網(wǎng)格提供了一個(gè)緊湊的只讀 VDB 樹(shù),因此它們很適合渲染任務(wù)。光線(xiàn)將 VDB 網(wǎng)格跟蹤到圖像中。使用每線(xiàn)程一條光線(xiàn),并使用一個(gè)自定義的 雷吉諾 functor 生成光線(xiàn),該函數(shù)接受像素偏移并創(chuàng)建世界空間光線(xiàn)。完整的代碼在存儲(chǔ)庫(kù)示例中可用。
考慮到沿射線(xiàn)采樣具有空間相干性這一事實(shí),可以使用 讀寫(xiě)器 來(lái)加速采樣。當(dāng)光線(xiàn)向前移動(dòng)時(shí),這會(huì)緩存樹(shù)遍歷堆棧,從而允許自底向上的樹(shù)遍歷,這比傳統(tǒng)的自上而下遍歷要快得多,后者涉及相對(duì)較慢的無(wú)界根節(jié)點(diǎn)。
auto renderTransmittanceOp = [image, grid, w, h, rayGenOp, imageOp, dt] __host__ __device__ (int i) { nanovdb::RaywRay = 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 ); };
由于光線(xiàn)與水平集網(wǎng)格相交是一項(xiàng)常見(jiàn)任務(wù), NanoVDB 實(shí)現(xiàn)了一個(gè)零交叉功能,并使用分層 DDA ( HDDA )作為沿光線(xiàn)的根搜索的一部分來(lái)清空空間跳躍(圖 5 )。有關(guān) HDDA 的更多信息,請(qǐng)參閱 OpenVDB 中高效光線(xiàn)行進(jìn)的分層數(shù)字微分分析儀 。下面是代碼示例:
... 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); } ...
碰撞檢測(cè)
碰撞檢測(cè)和解決是 NanoVDB 的另一項(xiàng)任務(wù),因?yàn)樗鼈兺ǔP枰行У夭檎覍?shí)體碰撞對(duì)象的有符號(hào)距離值。窄帶電平集表示非常理想,因?yàn)樗鼈冇梅?hào)對(duì)內(nèi)部/外部拓?fù)湫畔ⅲㄅ鲎矙z測(cè)所需)進(jìn)行了緊湊編碼。此外,最近點(diǎn)變換(沖突解決所需的)很容易從水平集函數(shù)的梯度計(jì)算。
下面的代碼示例是一個(gè)用于處理沖突的函數(shù)。使用 讀寫(xiě)器 是很有用的,因?yàn)橛糜跊_突解決的梯度計(jì)算涉及到同一空間附近的多個(gè)提取。
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; };
同樣,完整的代碼可以在存儲(chǔ)庫(kù)中找到。
基準(zhǔn)
NanoVDB 被開(kāi)發(fā)成在主機(jī)和設(shè)備上同樣運(yùn)行良好。使用 modernCUDA 中的擴(kuò)展 lambda 支持,您可以輕松地在兩個(gè)平臺(tái)上運(yùn)行相同的代碼。
本節(jié)包括比較英特爾線(xiàn)程構(gòu)建塊和 CPU CUDA 上光線(xiàn)跟蹤和碰撞檢測(cè)性能的基準(zhǔn)測(cè)試。計(jì)時(shí)以毫秒為單位,與 NVIDIA NVIDIA 8000 相比,是在 Xeon E5-2696 v4 x2 –( 88 個(gè) CPU 線(xiàn)程)上生成的。使用的 FogVolume 是兔子云, LevelSet 是 dragon 數(shù)據(jù)集。兩者都可以從 OpenVDB 網(wǎng)站下載。渲染的分辨率為 1024×1024 。這次碰撞試驗(yàn)?zāi)M了一億顆彈道粒子。
雖然基準(zhǔn)測(cè)試(圖 6 和下表)顯示了 NanoVDB 高效表示加速 CPU 上 OpenVDB 的好處,但它真正突出了使用 GPU 對(duì) VDB 數(shù)據(jù)進(jìn)行只讀訪(fǎng)問(wèn)以進(jìn)行碰撞檢測(cè)和光線(xiàn)跟蹤的好處。
結(jié)論
NanoVDB 是一個(gè)小而強(qiáng)大的庫(kù),它通過(guò)使用 GPUs 來(lái)加速某些 OpenVDB 應(yīng)用程序。開(kāi)源存儲(chǔ)庫(kù)現(xiàn)在可用了!要下載源代碼、構(gòu)建示例
關(guān)于作者
Wil Braithwaite 在倫敦和洛杉磯的工作室工作了 15 年的視覺(jué)特效。他的職位包括研究、技術(shù)指導(dǎo)、合成、 CG 監(jiān)督和 MOCAP 監(jiān)督。他開(kāi)創(chuàng)了圖形硬件在 VFX 管道中的應(yīng)用,在 NVIDIA 擔(dān)任高級(jí)應(yīng)用工程師,專(zhuān)門(mén)從事咨詢(xún)、培訓(xùn)和使用 NVIDIA 技術(shù)協(xié)助 VFX 工作室項(xiàng)目的開(kāi)發(fā)。
Ken Museth 是模擬技術(shù)的高級(jí)主管,并于 2020 年初加入 NVIDIA ,當(dāng)時(shí)他發(fā)起了 NanoVDB 的開(kāi)發(fā)。他以前在開(kāi)發(fā)虛擬現(xiàn)實(shí)技術(shù)的時(shí)候,一直致力于虛擬現(xiàn)實(shí)的開(kāi)發(fā)。他是 VDB 的創(chuàng)建者和 OpenVDB 的首席架構(gòu)師,也是其技術(shù)指導(dǎo)委員會(huì)的主席。此外,肯在 SpaceX 公司工作了六年,對(duì)新的猛禽火箭發(fā)動(dòng)機(jī)進(jìn)行大規(guī)模流體動(dòng)力學(xué)模擬。在 2017 年加入 Weta 之前,他在夢(mèng)工廠動(dòng)畫(huà)和數(shù)字領(lǐng)域工作了 10 年,在此之前,他曾在加州理工學(xué)院和林科平大學(xué)擔(dān)任研究員和全職教授。他擁有哥本哈根大學(xué)量子動(dòng)力學(xué)博士學(xué)位,并獲得電影藝術(shù)與科學(xué)學(xué)院頒發(fā)的技術(shù)成就獎(jiǎng)。肯是 SIGGRAPH 2020 技術(shù)論文委員會(huì)成員。
審核編輯:郭婷
-
NVIDIA
+關(guān)注
關(guān)注
14文章
4936瀏覽量
102814 -
gpu
+關(guān)注
關(guān)注
28文章
4700瀏覽量
128697
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論