optix


インストール

普通にOptixとCUDAのSDKをインストーラーからインストールする。
また、サンプルも、普通にCMakeでプロジェクトを作って普通にビルドすると普通に通る。
分からない場合は以下に詳しく書いてある。

Nvidia OptiX 入門(環境構築編)


.cuファイルのコンパイル方法(VisualStudio)

.cuをnvccで.ptxに変換する。
VisualStudioでは、新規プロジェクト作成時にCUDAのテンプレートを選択すると、
cuファイルをコンパイルしてくれるプロジェクトができる



既存プロジェクトにこの設定を追加する場合は、ソリューションエクスプローラーのプロジェクトを右クリックし、
ビルド依存関係→ビルドのカスタマイズ から追加できる。


デフォルトでは、ptx出力になっていないので、プロジェクトのプロパティから
NVCC Compilation Typeをptxのものに変更し、Compiler outputの拡張子部分をptxに変更する。


これでビルドを行うと、.cuがコンパイルされて、.ptxファイルができるようになる。

Optix Tutorial


tutorialを読んでいく。



Tutorial 0

RT_PROGRAM void closest_hit_radiance0()
{
prd_radiance.result = normalize(rtTransformNormal(
RT_OBJECT_TO_WORLD,
shading_normal))
*0.5f+0.5f;
}
これはレイが(最も近い面に)ヒットしたときに呼ばれるコードである。
rtTransformNormalという用意された関数を使い、法線をワールド空間に変換している。
normalizeすると-1~1の範囲になるので、resultに0~1として入れたいので* 0.5 + 0.5f している。

ここでshading_normalやprd_radianceという関数内で未定義の変数があるが、
これはファイル上部で、
rtDeclareVariable(float3,
shading_normal,
attribute shading_normal, );
というoptixの定義マクロを使って定義する。
1つめの引数は型、2つめの引数は変数名、3つめの引数はOptixにこの変数の用途を認識させるための、セマンティクスである。
prd_radianceのほうはこうなっている。
struct PerRayData_radiance
{
float3 result;
float importance;
int depth;
};
rtDeclareVariable(PerRayData_radiance,
prd_radiance, rtPayload, );
こちらは最終結果を格納する構造体を定義しているようだ。
shading_normal
という変数は、別の場所で値を代入している。
具体的には各プリミティブの衝突判定コード内で代入されている。
// box.cu

RT_PROGRAM void box_intersect(int)
{
  float3 t0 = (boxmin - ray.origin)/ray.direction;
  float3 t1 = (boxmax - ray.origin)/ray.direction;
  float3 near = fminf(t0, t1);
  float3 far = fmaxf(t0, t1);
  float tmin = fmaxf( near );
  float tmax = fminf( far );

  if(tmin <= tmax) {
    bool check_second = true;
    if( rtPotentialIntersection( tmin ) ) {
       texcoord = make_float3( 0.0f );
       shading_normal = geometric_normal = boxnormal( tmin );
       if(rtReportIntersection(0))
         check_second = false;
    } 
    if(check_second) {
      if( rtPotentialIntersection( tmax ) ) {
        texcoord = make_float3( 0.0f );
        shading_normal = geometric_normal = boxnormal( tmax );
        rtReportIntersection(0);
      }
    }
  }
}
これはbox.cuのコードだが、こちらでもclosest_hit~のファイルと同様に、shading_normalを使うために、
rtDeclareVariableの宣言が必要なようだ。

さて、レイが物体に当たらなかったときのコードも用意する必要がある。いわゆる背景色である。
rtDeclareVariable(float3, bg_color, , );

RT_PROGRAM void miss()
{
prd_radiance.result = bg_color;
}
それから、カメラも必要。

RT_PROGRAM void pinhole_camera()
{
size_t2 screen = output_buffer.size();
float2 d = make_float2(launch_index) /
make_float2(screen) * 2.f - 1.f;
float3 ray_origin = eye;
float3 ray_direction = normalize(d.x*U + d.y*V + W);
OptiX::Ray ray(ray_origin, ray_direction,
radiance_ray_type, scene_epsilon );
PerRayData_radiance prd;
prd.importance = 1.f;
prd.depth = 0; rtTrace(top_object, ray, prd);
output_buffer[launch_index] = make_color( prd.result );
}
output_bufferという変数が出てくるが、これは別の場所で初期化している、Optixの出力用バッファで、
Optixでは、CPU側のバッファも、GPU側のバッファの両方に対応している。
このチュートリアルではGPU側にバッファを作っている。
このGPU側のバッファはOpenGLのRGBA8のバッファとして作成しているので、OpenGL Textureとして効率的に描画できる
make_color()によってfloatのRGBをRGBA8に変換している。
optix::Buffer sutil::createOutputBuffer(
        optix::Context context,
        RTformat format,
        unsigned width,
        unsigned height,
        bool use_pbo )
{
    
    optix::Buffer buffer;
    if( use_pbo )
    {
        // First allocate the memory for the GL buffer, then attach it to OptiX.

        // Assume ubyte4 or float4 for now
        unsigned int elmt_size = format == RT_FORMAT_UNSIGNED_BYTE4 ?  4 : 16;

        GLuint vbo = 0;
        glGenBuffers( 1, &vbo );
        glBindBuffer( GL_ARRAY_BUFFER, vbo );
        glBufferData( GL_ARRAY_BUFFER, elmt_size * width * height, 0, GL_STREAM_DRAW);
        glBindBuffer( GL_ARRAY_BUFFER, 0 );

        buffer = context->createBufferFromGLBO(RT_BUFFER_OUTPUT, vbo);
        buffer->setFormat( format );
        buffer->setSize( width, height );
    }
    else
    {
        buffer = context->createBuffer( RT_BUFFER_OUTPUT, format, width, height );
    }

    return buffer;
}
また、rtTraceという関数を呼ぶことで、レイトレを行っている。
    rtTrace(top_object, ray, prd);
第1引数は、用意したオブジェクトツリーの最上位オブジェクト、第2引数はレイ
第3引数は、ローカル変数prdへの参照で、各レイごとに、rtPayloadセマンティクスで定義された変数にアタッチされる。

レイがオブジェクトにヒットしたらclosest hit programが走り、resultに法線色がセットされ、
どのオブジェクトにもヒットしなかったらmiss()により背景色がセットされる。
全ての例がトレースされたら、制御はpinhole_camera()へ戻り、出力色をoutput_bufferへ格納する。

また、Optixはトラバーサルとシェーディングの両方で再帰をサポートしていて、ローカルスタックがステートを制御するのに使われる。スタックがオーバーフローしたときは、例外プログラムを実行して、abortする。
このチュートリアルでは、例外発生時の出力色をexception()で定義している。
rtDeclareVariable(float3, bad_color, , );

RT_PROGRAM void exception()
{
output_buffer[launch_index] = make_color( bad_color );
}



Programming Guide 


チュートリアルはこの先続くが、その前にProgramming Guideのほうが有用そうなのでそちらを見ていく。

Object Model


OptixはオブジェクトベースのC APIで、オブジェクト階層の単純な保持モードが実装されている。
このオブジェクト指向のホストインターフェースは、GPUでプログラムを実行するために拡張されている。
主なオブジェクトは以下の通り

 Context  Optixエンジンを走らせるインタフェース
 ProgramPTXアセンブリにコンパイルされた、CUDA C関数
 VariableCからOptixプログラムへデータを渡す名称
 Buffervariableへバインドすることのできる、多次元アレイ
 TextureSampler補間機構を備える、1つまたは複数のバッファ
 Geometry    三角形やほかのユーザー定義型などの、レイが交差可能な1つまたは複数のバッファ
 Material 最も近いプリミティブや、潜在的に最も近いプリミティブへ、レイが交差するとき、実行されるプログラムのセット
 GeometryInstance GeometryとMaterialオブジェクトの間をバインディングする
 Group 階層を構成するオブジェクトのセット 
 GeometryGroup GeometryInstaneオブジェクトのセット
 Transform ジオメトリオブジェクトをトランスフォームするための階層ノード
 Selector 子をトラバースで選択する、プログラム可能な階層ノード
 Acceleration 階層ノードへバインドできるオブジェクトの、空間構造
 RemoteDevice オプションのリモートレンダリングのためのネットワーク接続


Programs



 Ray Generation レイトレーシングパイプラインへのエントリーポイント。各ピクセルやサンプルやユーザー定義に対して並列に、システムにより呼び出される
 Exception 例外ハンドラ。スタックオーバーフローや他のエラーの状態により呼び出される
 Closest Hitレイをトレースして最近点に交差したときに呼び出され、マテリアルシェーディングなどを行う
 Any Hit トレースされたレイが新たな潜在的な最近点を見つけた時に呼ばれ、影の計算などを行う。
 Intersectionレイプリミティブの交差テストを行う。トラバーサル中に呼び出される
 Bounding Box プリミティブのワールド空間でのBBoxを計算する。システムが新たな空間構造を作るときに呼ばれる
 Miss 全てのシーンジオメトリにレイが当たらなかったときに呼ばれる
 Visit Selectorノードのトラバーサル中に、レイがトラバースする子を決定するために、呼び出される

これらのPrograms への入力言語は PTX である。Optix SDKは、また、NVCCを使ったラッパークラスやヘッダーを用意している。
これにより、CUDA Cを使って、最適なPTXを生成することができる。

太字部分は、tutorial 0 で出てきたもの。例えば、pinhole_cameraは、RayGenerationProgramとして使われている。
    // Ray generation program
    std::string ptx_path( ptxPath( "pinhole_camera.cu" ) );
    Program ray_gen_program = context->createProgramFromPTXFile( ptx_path, "pinhole_camera" );
    context->setRayGenerationProgram( 0, ray_gen_program );
BBoxについては、記事には書かれていないが、ジオメトリを作るときに指定されている。
    Geometry box = context->createGeometry();
    box->setPrimitiveCount( 1u );
    box->setBoundingBoxProgram( box_bounds );
    box->setIntersectionProgram( box_intersect );





Comments