Optix Prime入门

OptiX通常用于表示整个算法实现,无论是渲染,可见性,辐射传输还是其他任何方式。 OptiX的许多用户可编程部分允许应用程序表达复杂的操作,例如着色,这些操作与光线跟踪操作紧密混合,通常递归,并以单射线编程模型表示。通过封装算法的可编程部分并拥有整个算法,OptiX可以在GPU上执行整个算法,并在发布时优化每个新GPU的执行。
        有时,只需要光线跟踪功能,例如可见性、平凡光线投射渲染和非常大批次的光线跟踪计算等等。 OptiX Prime是一套专为这些用例设计的OptiX API。Prime专门用于高性能的光线与一组三角形相交的计算。Prime是一种更薄,更简单的API。 Prime也适用于一些快速实验和爱好项目。

        Optix Prime包含如下对象:

BufferDesc: 缓冲区描述符;

context:资源;

model:三角形和加速结构;

query:求射线与model的交点。

11.2 上下文context
OptiX Prime的context对象有两个功能。 第一个功能是管理由api创建的对象。 上下文可以创建对象,其中一些也可以创建其他对象。所有这些对象都在上下文中注册,并在上下文被销毁时销毁。第二个功能是封装执行实际计算的后端。
目前支持以下上下文类型:RTP_CONTEXT_TYPE_CPU和RTP_CONTEXT_TYPE_CUDA。
RTP_CONTEXT_TYPE_CPU旨在GPU设备不支持CUDA时作为后备。 它允许应用程序以较低性能运行。

RTP_CONTEXT_TYPE_CUDA默认只使用最快的可用设备。也可以向rtpContextSetCudaDeviceNumbers提供设备号列表指定上下文所使用的特定设备(或设备列表)。最快的设备在此列表中用作主要设备。加速结构将建立在该设备上并复制到其他设备。指定的设备必须具有SM 3.0或更高。所有设备都将用于光线跟踪,工作按每个设备的计算能力比例分配。请注意,如果射线存储在设备存储器中,这种分配可能相当昂贵。为了最大限度地提高效率,建议每个上下文选择一个设备。
在调用rtpContextCreate或rtpContextSetCudaDeviceNumbers之后,主要设备为当前设备。
以下代码演示了如何创建上下文并指定要使用的设备。在在此示例中,将创建CPU上下文作为备份。

  RTPcontext context;
  CHK_PRIME( rtpContextCreate( contextType, &context ) );
  if (contextType == RTP_CONTEXT_TYPE_CPU) {
    std::cerr << "Using cpu context\n";
  } else {
    unsigned int device = 0;
    CHK_PRIME( rtpContextSetCudaDeviceNumbers( context, 1, &device ) );
    std::cerr << "Using cuda context\n";
  }

11.3缓冲区描述符
用于从OptiX Prime发送和接收数据的缓冲区由应用程序管理。缓冲区描述符是一个提供缓冲区信息的对象,例如其格式和位置,以及指向缓冲区数据的指针。 OptiX Prime支持以下缓冲类型:
RTP_BUFFER_TYPE_HOST
RTP_BUFFER_TYPE_CUDA_LINEAR
通过调用rtpBufferDescCreate创建缓冲区描述符。RTP_BUFFER_TYPE_CUDA_LINEAR类型的缓冲区驻留在当前的CUDA设备上。该缓冲区的设备编号可以通过显式调用rtpBufferDescSetCudaDeviceNumber指定。
通过调用rtpBufferDescSetRange指定缓冲区哪一部分用于输入或输出。范围是根据元素的数量来指定的。
对于包含顶点数据的缓冲区,可以在单元之间设置stride of bytes。这对包含交错顶点属性的顶点缓冲区很有用:

  //
  // Create buffers for geometry data 
  //
  RTPbufferdesc indicesDesc;
  CHK_PRIME( rtpBufferDescCreate(
        context,
        RTP_BUFFER_FORMAT_INDICES_INT3,
        RTP_BUFFER_TYPE_HOST,
        mesh.getVertexIndices(),
        &indicesDesc)
      );
  CHK_PRIME( rtpBufferDescSetRange( indicesDesc, 0, mesh.num_triangles ) );
  
  RTPbufferdesc verticesDesc;
  CHK_PRIME( rtpBufferDescCreate(
        context,
        RTP_BUFFER_FORMAT_VERTEX_FLOAT3,
        RTP_BUFFER_TYPE_HOST,
        mesh.getVertexData(), 
        &verticesDesc )
      );
  CHK_PRIME( rtpBufferDescSetRange( verticesDesc, 0, mesh.num_vertices ) );

接受缓冲区描述符作为参数的函数具有复制语义。 这意味着,当在调用中使用缓冲区描述符来设置要查询的光线时,也导致对缓冲区描述符的更改,对缓冲区描述符的更改将不会对查询可见。 但是,更改缓冲区本身的内容可能会影响查询。
缓冲区描述符是轻量级对象,可以根据需要创建和销毁。
任何类型的缓冲区都可以传递给OptiX Prime函数,并且会自动复制到适当的位置。 例如,主机上的顶点缓冲区将被复制到
上下文类型为RTP_CONTEXT_TYPE_CUDA时的主设备。 虽然方便,这种自动复制可能需要在设备上分配内存并且可以
对性能产生负面影响。 为获得最佳性能,建议采用以下缓冲区和上下文类型一起使用:

Context type Buffer type
RTP_CONTEXT_TYPE_CPU RTP_BUFFER_TYPE_HOST
RTP_CONTEXT_TYPE_CUDA RTP_BUFFER_TYPE_CUDA_LINEAR(on the primary device)

11.4模型
模型表示一组三角形或一组模型实例,以及在三角形或实例上构建的加速结构。通过调用rtpModelCreate来关联上下文创建模型,并且可以使用rtpModelDestroy销毁它。

11.4.1三角模型
通过调用rtpModelSetTriangles,并提供顶点描述符和可选的顶点编号(索引)缓冲区描述符,来提供模型的三角形数据。如果没有提供顶点编号(索引)缓冲区,那么顶点缓冲区被认为是三角形顶点的列表,每组三个顶点形成一个三角形(也称为“三角汤”)。顶点缓冲区大小应为三角形数量*3(顶点缓冲区数据类型为float3),顶点编号(索引)缓冲区大小应为三角形个数(数据类型为int3)。
rtpModelUpdate在三角形上创建加速结构。重要的是在rtpModelSetTriangles中指定的顶点和索引缓冲区有效,直到
rtpModelUpdate已完成。如果指定了RTP_MODEL_HINT_ASYNC,则指定部分或全部加速结构更新可以异步运行,rtpModelUpdate在更新完成之前可以返回。 rtpModelFinish阻塞调用线程,直到更新为止。rtpModelGetFinished可用于轮询,直到更新完成。一旦更新已完成,输入缓冲区可以修改。 

  //
  // Create the Model object
  //
  RTPmodel model;
  CHK_PRIME( rtpModelCreate( context, &model ) );
  CHK_PRIME( rtpModelSetTriangles( model, indicesDesc, verticesDesc ) );
  CHK_PRIME( rtpModelUpdate( model, 0 ) );

11.4.2实例化
使用实例化,可以使用现有三角模型组合复杂场景。(请参阅“三角模型”(第103页)。)提供实例缓冲区描述符和转换缓冲区
描述符,通过调用rtpModelSetInstances实例化。这些缓冲区描述符的范围必须相同。 实例的类型缓冲区描述符必须是RTP_BUFFER_TYPE_HOST和格式RTP_BUFFER_FORMAT_INSTANCE_MODEL。 

11.4.3掩蔽Mask
        通过掩蔽,可以结合光线掩模指定每个三角形可见性信息。为了使用掩蔽,必须使用RTP_BUFFER_FORMAT_INDICES_INT3_MASK_INT的缓冲区格式指定三角形数据。用户三角形必须设置构建参数,光线格式
必须使用RTP_BUFFER_FORMAT_RAY_ORIGIN_MASK_DIRECTION_TMAX。

11.5 查询Query
查询用于对模型model执行实际光线跟踪。使用rtpQueryCreate从模型创建查询。支持以下类型的查询:
RTP_QUERY_TYPE_ANY
RTP_QUERY_TYPE_CLOSEST
沿着任何给定的射线,可能存在许多交叉点。 RTP_QUERY_TYPE_CLOSEST返回沿光线的第一个命中hit。 RTP_QUERY_TYPE_ANY返回找到的第一个命中,无论是否是最接近的或不是。查询需要一个光线缓冲区和一个命中缓冲区存储命中结果。光线和命中有几种格式。不同格式的主要优点是有些需要的存储空间比其他格式要少。这对于最小化主机和设备之间以及设备之间的光线传输时间和命中数据非常重要。
一旦指定了光线和命中缓冲区,就可以通过调用rtpQueryExecute来执行查询。在此函数返回之前,不得修改光线缓冲区。如果指定了RTP_QUERY_HINT_ASYNC,则rtpQueryExecute可以在查询实际完成之前返回。可以调用rtpQueryFinish来阻止当前线程,直到查询完成,或者可以使用rtpQueryGetFinished进行轮询,直到查询完成为止。此时,保证返回所有命中,并且可以安全地修改光线缓冲区。

  //
  // Create buffer for ray input 
  //
  RTPbufferdesc raysDesc;
  Buffer<Ray> raysBuffer( 0, bufferType, LOCKED ); 
  createRaysOrtho( raysBuffer, width, &height, mesh.getBBoxMin(),mesh.getBBoxMax(), 0.05f );
  CHK_PRIME( rtpBufferDescCreate( 
        context, 
        Ray::format, /*RTP_BUFFER_FORMAT_RAY_ORIGIN_TMIN_DIRECTION_TMAX*/ 
        raysBuffer.type(), 
        raysBuffer.ptr(), 
        &raysDesc )
      );
  CHK_PRIME( rtpBufferDescSetRange( raysDesc, 0, raysBuffer.count() ) );

  //
  // Create buffer for returned hit descriptions
  //
  RTPbufferdesc hitsDesc;
  Buffer<Hit> hitsBuffer( raysBuffer.count(), bufferType, LOCKED );
  CHK_PRIME( rtpBufferDescCreate( 
        context, 
        Hit::format, /*RTP_BUFFER_FORMAT_HIT_T_TRIID_U_V*/ 
        hitsBuffer.type(), 
        hitsBuffer.ptr(), 
        &hitsDesc )
      );
  CHK_PRIME( rtpBufferDescSetRange( hitsDesc, 0, hitsBuffer.count() ) );

  //
  // Execute query
  //
  RTPquery query;
  CHK_PRIME( rtpQueryCreate( model, RTP_QUERY_TYPE_CLOSEST, &query ) );
  CHK_PRIME( rtpQuerySetRays( query, raysDesc ) );
  CHK_PRIME( rtpQuerySetHits( query, hitsDesc ) );
  CHK_PRIME( rtpQueryExecute( query, 0 /* hints */ ) );

CHK_PRIME在putil\preprocessor.h中定义。

Buffer在putil\buffer.h中定义,为模板类。

发布了80 篇原创文章 · 获赞 14 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/novanova2009/article/details/90552645