Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Square/Planar Artifacts While Volume Rendering NVDB Files Using Optix #1812

Closed
KaavayGupta opened this issue May 13, 2024 · 1 comment

Comments

@KaavayGupta
Copy link

KaavayGupta commented May 13, 2024

Environment

Operating System: Ubuntu
Version / Commit SHA: 08409f0
Others: Optix 7.7.0

Describe the bug

In the sample provided by OptiX, optixVolumeViewer, I am getting square artifacts while raymarching using HDDA that correspond to the vdb grid subdivisions. The volume I am using is wdas_cloud_half from here. I converted it to .nvdb using the shipped nanovdb_convert tool. I also replaced the nanovdb library in optixVolumeViewer with the newer one to handle the new volume.
These are the artifacts I am talking about:

image image

They are only visible from one side and disappear when viewed from the opposite side.
The code used in optixVolumeViewer's volume.cu that does volume rendering is (comments and other parts omitted):

template<typename AccT>
inline __device__ float transmittanceHDDA(
    const nanovdb::Vec3f& start,
    const nanovdb::Vec3f& end,
    AccT& acc, const float opacity )
{
    float transmittance = 1.f;
    auto dir = end - start;
    auto len = dir.length();
    nanovdb::Ray<float> ray( start, dir / len, 0.0f, len );
    nanovdb::Coord ijk = nanovdb::RoundDown<nanovdb::Coord>( ray.start() ); // first hit of bbox

    nanovdb::HDDA<nanovdb::Ray<float> > hdda( ray, acc.getDim( ijk, ray ) );

    float t = 0.0f;
    float density = acc.getValue( ijk ) * opacity;
    while( hdda.step() )
    {
        float dt = hdda.time() - t; // compute length of ray-segment intersecting current voxel/tile
        transmittance *= expf( -density * dt );
        t = hdda.time();
        ijk = hdda.voxel();

        density = acc.getValue( ijk ) * opacity;
        hdda.update( ray, acc.getDim( ijk, ray ) ); // if necessary adjust DDA step size
    }

    return transmittance;
}
extern "C" __global__ void __closesthit__radiance_volume()
{
    const HitGroupData* sbt_data = reinterpret_cast<HitGroupData*>( optixGetSbtDataPointer() );

    const auto* grid = reinterpret_cast<const nanovdb::FloatGrid*>(
        sbt_data->geometry_data.volume.grid );
    const auto& tree = grid->tree();
    auto        acc  = tree.getAccessor();

    const float3 ray_orig = optixGetWorldRayOrigin();
    const float3 ray_dir  = optixGetWorldRayDirection();

    const float t0 = optixGetRayTmax();
    const float t1 = __uint_as_float( optixGetPayload_0() );

    PayloadRadiance payload = {};
    traceRadiance(
        params.handle,
        ray_orig,
        ray_dir,
        0.0f,
        1e16f,
        params.solid_objects, // visibility mask - limit intersections to solid objects
        &payload
    );

    const auto ray = nanovdb::Ray<float>( reinterpret_cast<const nanovdb::Vec3f&>( ray_orig ), reinterpret_cast<const nanovdb::Vec3f&>( ray_dir ) );
    auto start = grid->worldToIndexF( ray( t0 + 1e-3) );
    auto end   = grid->worldToIndexF( ray( fminf( payload.depth, t1 ) ) );

    auto bbox = grid->indexBBox();
    confine( bbox, end );

    const float opacity = sbt_data->material_data.volume.opacity;
    float  transmittance = transmittanceHDDA( start, end, acc, opacity );

    float3 result = payload.result * transmittance;

    optixSetPayload_0( __float_as_uint( result.x ) );
    optixSetPayload_1( __float_as_uint( result.y ) );
    optixSetPayload_2( __float_as_uint( result.z ) );
    optixSetPayload_3( __float_as_uint( 0.0f ) );
}

Complete code can be found in the OptiX-Samples shipped with their SDK

To Reproduce

Steps to reproduce the behavior:

  1. Download and build OpenVDB with NanoVDB
  2. Download Optix 7.7.0
  3. Replace the nanovdb folder in optixVolumeViewer with the include/nanovdb folder from the directory where openvdb was installed. And then build it.
  4. Download wdas_cloud_half.vdb from here.
  5. Use nanovdb_convert in nanovdb to convert wdas_cloud_half.vdb to wdas_cloud_half.nvdb.
  6. Run optixVolumeViewer executable with argument --volume wdas_cloud_half.nvdb.

Additional Context

The sample smoke volume provided by Optix does not show these artifacts.

@KaavayGupta
Copy link
Author

After alot tinkering around, I've finally figured out the solution.
The problem was in example code provided by OptiX. It was accessing the volume coordinates in the raymarching loop.

In the function template<typename AccT> inline __device__ float transmittanceHDDA(const nanovdb::Vec3f& start, const nanovdb::Vec3f& end, AccT& acc, const float opacity ):

It used ijk = hdda.voxel() to get the current coordinate.
Replacing it to ijk = nanovdb::RoundDown<nanovdb::Coord>(ray(hdda.time() + 1.0001f)); solved this issue.

Though, I still cannot figure out the exact reason why this solved it.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants