NVIDIA / gvdb-voxels

Sparse volume compute and rendering on NVIDIA GPUs
Other
673 stars 144 forks source link

OpenVDB support broken since 1.11 #83

Closed wi-re closed 4 years ago

wi-re commented 4 years ago

The sample that's supposed to load an openVDB file and render it to a file is not working in any version past the 3a59e39a2899ccefd3724b6b285f64cdab42ce74 commit (Windows, I have no way to test the sample under Linux). The output of the sample should look like this (image based on output of #3a59e39a2899ccefd3724b6b285f64cdab42ce74 , which is identical to the output shown on the samples page https://developer.nvidia.com/gvdb-samples ) img_importvdb But the result i'm getting on multiple machines (varying GPU architectures, CUDA versions and OS versions) looks like this: unknown However, due to the complexity of the 37ebd2e2a18e3303ce3a688c9de0276c0e9330a8 commit, I cannot find an exact reason as to why this happens.

The output of the program for version 1.11 (not working) is:

Starting GVDB. Device List: Max. texture3D width: 16384 Max. texture3D height: 16384 Max. texture3D depth: 16384

  1. GeForce GTX 1080 Ti Using Device: 0, GeForce GTX 1080 Ti, Context: 00000191DFD8CCA0 Starting GVDB Voxels. ver 1.11 Creating Allocator.. Creating Scene.. Loading VDB. ../source/shared_assets/bunny.vdb Reading OpenVDB file. Grid: ls_bunny Loading Grid: ls_bunny Configuring GVDB. Topology Used: 1.13 MB Compute volume bounds. Activating space. First leaf: 0 (96.000000 8.000000 232.000000) Loading bricks. 1% 2% 3% 4% 5% 6% 7% 8% 9% 10% 11% 12% 13% 14% 15% 16% 17% 18% 19% 20% 21% 22% 23% 24% 25% 26% 27% 28% 29% 30% 31% 32% 33% 34% 35% 36% 37% 38% 39% 40% 41% 42% 43% 44% 45% 46% 47% 48% 49% 50% 51% 52% 53% 54% 55% 56% 57% 58% 59% 60% 61% 62% 63% 64% 65% 66% 67% 68% 69% 70% 71% 72% 73% 74% 75% 76% 77% 78% 79% 80% 81% 82% 83% 84% 85% 86% 87% 88% 89% 90% 91% 92% 93% 94% 95% 96% 97% 98% 99% 100% Value Range: 100000004091847875962975319375216640.000000 -100000004091847875962975319375216640.000000 Saving VBX (ver 1.11) Creating screen buffer. 1024 x 768 Render volume. 0.077 ms Writing img_importvdb.png Done.

The output of the program for version 1.1 (working) is:

Starting GVDB. Device List:

  1. GeForce GTX 1080 Ti Using Device: 0, GeForce GTX 1080 Ti, Context: 000001F749DFCCA0 Starting GVDB Voxels. ver 1.11 Creating Allocator.. Creating Scene.. Creating CUDPP.. Loading VDB. ../source/shared_assets/bunny.vdb Reading OpenVDB file. Grid: ls_bunny Loading Grid: ls_bunny Configuring GVDB. Topology Used: 1.13 MB Compute volume bounds. Activating space. First leaf: 0 (96.000000 8.000000 232.000000) Loading bricks. 1% 2% 3% 4% 5% 6% 7% 8% 9% 10% 11% 12% 13% 14% 15% 16% 17% 18% 19% 20% 21% 22% 23% 24% 25% 26% 27% 28% 29% 30% 31% 32% 33% 34% 35% 36% 37% 38% 39% 40% 41% 42% 43% 44% 45% 46% 47% 48% 49% 50% 51% 52% 53% 54% 55% 56% 57% 58% 59% 60% 61% 62% 63% 64% 65% 66% 67% 68% 69% 70% 71% 72% 73% 74% 75% 76% 77% 78% 79% 80% 81% 82% 83% 84% 85% 86% 87% 88% 89% 90% 91% 92% 93% 94% 95% 96% 97% 98% 99% 100% Value Range: 100000004091847875962975319375216640.000000 -100000004091847875962975319375216640.000000 Saving VBX (ver 1.11) Creating screen buffer. 1024 x 768 Render volume. 0.102 ms Writing img_importvdb.png Done.
NBickford-NV commented 4 years ago

Yep, this is a problem, thanks for pointing this out! It looks like possibly a rendering issue rather than an OpenVDB issue so far. Maybe this might be due to how 1.1.1 now reads the transform from the OpenVDB file, so perhaps the light's now placed inside the volume or near the bunny's feet?

wi-re commented 4 years ago

Interessting Idea, to test that i quickly tried setting the light to the same position as the camera cam->setOrbit ( Vector3DF(-10,30,0), Vector3DF(14.2f,15.3f,18.0f), 130, 1.0f ); and lgt->setOrbit ( Vector3DF(-10,30,0), Vector3DF(14.2f, 15.3f, 18.0f), 130, 1.0 ); The result I'm getting is this: img_importvdb Which is very similar to the original light position, but different, i.e. the side is shaded slightly different in the large spot area image So it's probably not a problem of that. The loadVDB function simply takes the voxel size of the vdb file and applies it as a linear transform and the whole model is offset so that it's in positive space voffset = mVoxMin * -1; // offset to positive space (hack) and the camera and light should be somewhere outside of the first octant with the negative. Further, modifying the levelset rendering function to assign the normal to the color, i.e. clr = float4{ norm.x*0.5f + 0.5f, norm.y * 0.5f + 0.5f, norm.z * 0.5f + 0.5f ,1.f}; yields the following image: img_importvdb So there are certainly some issues with the geometry itself, which seem to follow some regular structure (at least in parts) image

NBickford-NV commented 4 years ago

Thanks! What I've got so far is that there's one bug where the light and the camera are in different coordinate spaces when the transformation matrix isn't the identity (in this case, it's a scaling matrix of 1/20 in each axis), and another bug where the ray direction wasn't of unit length (in this case, it had length 20), causing volume undersampling. I'm working on fixing the gridline artifacts at the moment - my best guess is that they might be due to not updating the apron correctly, though I'm not 100% sure on that yet.

wi-re commented 4 years ago

Scaling the VDB file to have a voxel size of 1 (forcing the transform matrix to be an identity matrix) seems to help in avoiding many of the artifacts but still leaves some amount of gridline artifacts img_importvdb image Which seem to be primarily aligned with the ray direction. Manually moving the vdb model so that the minimum voxel position (-24 -8 -16) is on the origin (0 0 0) makes no difference to the result (which should align the index space of the vdb file exactly with the gvdb index space and avoid issues caused by the offsetting of the read leaf nodes)

NBickford-NV commented 4 years ago

OK, I think I've finally figured out what was going on here! Fix coming soon.

The gridline artifacts are in part due to the switch to using texture and surface objects instead of using (deprecated) texture and surface references. The texture references GVDB 1.1 used used point filtering by default, while the new texture objects use linear filtering with wrapping. Because kernelCopyTexZYX (which is used to copy bricks from the VDB volume to the GVDB volume) samples at the corners of voxels instead of the centers of voxels, voxels on the face of each brick are being averaged with voxels on the other side of the brick, resulting in the gridline discontinuities you can see every 8 voxels here.

(Postscript: Checking further down the pipeline as well and backporting the fixed SaveVBX code, this makes it so that the two versions' .vbx files match up as well except for the transformation matrix.)

NBickford-NV commented 4 years ago

Hi Rene,

I've just pushed a commit that should fix this issue - here's what the output from gImportVDB should look like now! It looks like it also fixes the shadow undersampling issue that you can see on the side of the bunny. Please try this out when you get the chance and let me know if this fixes your issue.

img_importvdb

There's more information in dae0e22f, but briefly, there wound up being four issues: the default filter mode was changed to linear from point sampling (I changed it back), SetSteps uses application-space units (so scaling the volume wound up causing the sample to undersample; the values passed to SetSteps have been changed, since this avoids changing how the API works at the moment, although I think this might be revisited in the future), lights were in the wrong coordinate space (GVDB now converts to index space when preparing to render), and the value of epsilon introduced in 07fa281 wound up being too small for the size of this volume (I may also be revisiting GVDB's HDDA in the future).

This commit also adds some fixes for building with OpenVDB 7.0 and vcpkg - it looks like there was some work to rename the debug OpenVDB library and DLL, but it wasn't quite finished, so the README now includes a workaround for that.

Thanks again!

wi-re commented 4 years ago

Hey,

I just checked out the current github version and it also works the same for me. Loading from the saved vbx file (i.e. via gInteractiveGL) also works properly now. Just to make sure I get how this import works, the process to actually load and query a openvdb volume via gvdb would be:

To transform from an actual global position to the correct position w.r.t. the vdb file, the position would need to be translated by voffset and then scaled by voxelsize, i.e. SetTransform(-voffset, voxelsize,...) and not just scaled by voxelsize as is done right now. Finally, the apron makes sure that getGradientLevelSet (which uses positions offset by .5 voxels in each direction) are in the same brick as the position.

So to summarize: if one was to write their own query function using an imported vdb file the steps would be

  1. call LoadVDB and save voxelsize and voffset
  2. call prepareVDB
  3. call a global function with cuVDBInfo as a VDBInfo* and voxelsize and voffset
  4. for a position (in world space) offset it by voffset and divide by voxelsize to find the index space position
  5. call getNodeAtPoint with the cuVDBInfo and the index space position to get the correct brick
  6. if the brick is 0x0 skip
  7. calculate the position in brick space float3 p = offs + (wp-vmin)/vdel;
  8. find texture value at this position tex3D<float> ( gvdb->volIn[chan], p.x, p.y, p.z );
  9. to find gradient call getGradient with p
NBickford-NV commented 4 years ago

Yes, this looks about right. For the first set of bullet points, note that LoadVDB takes care of the steps through and including FinishTopology. Additionallly, the xform appears to only be used for the rendering functions (it transforms from index/world-space to the application's coordinate space), so if you're querying voxels rather than ray tracing, you might be able to skip calling PrepareRender.

The coordinate spaces can be a bit confusing at the moment, since voxelsize was removed inside the library itself (i.e. for GVDB, index-space is now the same as world-space), but there's an arbitrary 4x4 (really 3x4, as the fourth component of the output is ignored) matrix for converting from GVDB's space to the space in which you're sending rays, which will include the scaling factor from OpenVDB. It's also confusing because I don't think there's a standardized name for the coordinate space that xform maps to at the moment - I'm using "application-space" for now. (I'm hoping to clean this up, possibly renaming things to make the coordinate spaces more explicit, in the future.)

The numbered bullet points look good to me!

(Sorry for the delay, just got back to this!)

wi-re commented 4 years ago

Thanks for the response, this should close the issue for now and considering more specific problems of using gvdb in my use case I would send you a message directly should any questions arise as these are not really issues related to the library as a whole.

Thanks again for your help!