1 // Copyright Contributors to the OpenVDB Project
2 // SPDX-License-Identifier: MPL-2.0
3 
4 #if defined(NANOVDB_USE_OPENVDB)
5 
6 #include <cmath>
7 #include <chrono>
8 
9 #include <openvdb/openvdb.h>
10 #include <openvdb/math/Ray.h>
11 #include <openvdb/tools/LevelSetSphere.h>
12 
13 #include <nanovdb/util/CudaDeviceBuffer.h>
14 #include <nanovdb/util/NanoToOpenVDB.h>
15 
16 #include "common.h"
17 
18 #if defined(NANOVDB_USE_CUDA)
19 using BufferT = nanovdb::CudaDeviceBuffer;
20 #else
21 using BufferT = nanovdb::HostBuffer;
22 #endif
23 
runOpenVDB(nanovdb::GridHandle<BufferT> & handle,int numIterations,int width,int height,BufferT & imageBuffer)24 void runOpenVDB(nanovdb::GridHandle<BufferT>& handle, int numIterations, int width, int height, BufferT& imageBuffer)
25 {
26     using GridT = openvdb::FloatGrid;
27     using CoordT = openvdb::Coord;
28     using RealT = float;
29     using Vec3T = openvdb::math::Vec3<RealT>;
30     using RayT = openvdb::math::Ray<RealT>;
31 
32     auto srcGrid = nanovdb::nanoToOpenVDB(handle);
33     std::cout << "Exporting to OpenVDB grid[" << srcGrid->getName() << "]...\n";
34 
35     auto h_grid = (GridT*)srcGrid.get();
36 
37     float* h_outImage = reinterpret_cast<float*>(imageBuffer.data());
38 
39     auto  indexBBox = h_grid->evalActiveVoxelBoundingBox();
40     auto  gridXform = h_grid->transformPtr();
41     auto  worldBBox = gridXform->indexToWorld(indexBBox);
42     float wBBoxDimZ = (float)worldBBox.extents()[2] * 2;
43     Vec3T wBBoxCenter = Vec3T(worldBBox.min() + worldBBox.extents() * 0.5f);
44 
45     RayGenOp<Vec3T> rayGenOp(wBBoxDimZ, wBBoxCenter);
46     CompositeOp     compositeOp;
47 
48     openvdb::CoordBBox treeIndexBbox;
49     treeIndexBbox = h_grid->evalActiveVoxelBoundingBox();
50     std::cout << "Bounds: " << treeIndexBbox << std::endl;
51 
52     auto renderOp = [width, height, rayGenOp, compositeOp, treeIndexBbox] __hostdev__(int start, int end, float* image, const GridT* grid) {
53         // get an accessor.
54         auto acc = grid->getAccessor();
55 
56         for (int i = start; i < end; ++i) {
57             Vec3T rayEye;
58             Vec3T rayDir;
59             rayGenOp(i, width, height, rayEye, rayDir);
60             // generate ray.
61             RayT wRay(rayEye, rayDir);
62             // transform the ray to the grid's index-space.
63             RayT iRay = wRay.worldToIndex(*grid);
64             // clip to bounds.
65             if (iRay.clip(treeIndexBbox) == false) {
66                 compositeOp(image, i, width, height, 0.0f, 0.0f);
67                 return;
68             }
69             // integrate...
70             const float dt = 0.5f;
71             float       transmittance = 1.0f;
72             for (float t = iRay.t0(); t < iRay.t1(); t += dt) {
73                 float sigma = acc.getValue(CoordT::floor(iRay(t))) * 0.1f;
74                 transmittance *= 1.0f - sigma * dt;
75             }
76             // write transmittance.
77             compositeOp(image, i, width, height, 0.0f, 1.0f - transmittance);
78         }
79     };
80 
81     {
82         float durationAvg = 0;
83         for (int i = 0; i < numIterations; ++i) {
84             float duration = renderImage(false, renderOp, width, height, h_outImage, h_grid);
85             //std::cout << "Duration(OpenVDB-Host) = " << duration << " ms" << std::endl;
86             durationAvg += duration;
87         }
88         durationAvg /= numIterations;
89         std::cout << "Average Duration(OpenVDB-Host) = " << durationAvg << " ms" << std::endl;
90 
91         saveImage("raytrace_fog_volume-openvdb-host.pfm", width, height, (float*)imageBuffer.data());
92     }
93 }
94 
95 #endif
96