LoginSignup
2
1

More than 3 years have passed since last update.

OptiX7 環境構築と大まかな流れ

Last updated at Posted at 2021-03-04

はじめに

RTXに対応しリアルタイムレンダリングで勢いを見せるOptiX 7の環境構築を行い、大まかな流れを学習がてらにまとめました。

NVIDIA公式のチュートリアルとSIGGRAPH2019でのプレゼンテーションを参考にしてます。

この記事では

  1. OptiX 環境構築 (Windows) の解説
  2. 簡単なOptiXでのレイトレーシングプログラムの大まかな流れを解説

目次

  1. OptiX 環境構築
  2. OptiXレイトレーシングの大まかな流れ
  3. 感想
  4. 参考文献

OptiX 環境構築

インストール

サンプルプロジェクトのダウンロード

以下のGitHubリポジトリから今回のサンプルプロジェクトをダウンロード

プロジェクトのビルド

CMakeのGUIを開き、source directory にダウンロードしたサンプルプロジェクトを指定、build directorybuildフォルダーを指定して新規フォルダーを作成する。

OptiX_INCLUDEOptiX_INCLUDE_DIRにはインストールしたOptiXのルートディレクトリとincludeフォルダを指定する。

CMake 3.18.0 - D__Documents_OptiX_optix7course_build 3_3_2021 4_22_51 PM.png

すべて正しくインストールされていればConfigureをした後にGenerateでビルドが成功する。

サンプルプロジェクトの実行

build 内のVisual Studioプロジェクトを開き、Visual Studioでビルドすれば.exe実行ファイルができ、実行できる。

OptiXレイトレーシングの大まかな流れ

OptiX 7 では Optix 6 に比べ、自由度が高まった反面、画像を描画するだけの簡単なプログラムでもメモリの確保、シェーダーバインディングテーブルの設定などやることが多くなってしまったので下のような画像を描画するのに必要な最小限の作業をまとめてみる。

osc_example2.png

ところどころ省略しているため実際のコードは examples02_pipelineAndRayGen を参照してください。

画像を描画するためには必要な工程は4つに分けられます。

  1. デバイスサイドでのレイ生成関数の定義
  2. OptiX パイプラインの作成
  3. シェーダーバインディングテーブルの作成
  4. レイトレーシングの開始

1. デバイスサイドでのレイ生成関数の定義

まずはデバイス側(GPU)に送りたい情報を格納する構造体を定義する。この例ではレンダリング結果の色を格納するバッファーのみだが、カメラ位置などレイトレーシングの計算に関わる情報はここに格納することになる。

// LaunchParams.h
struct LaunchParams
{
    int       frameID { 0 };
    uint32_t *colorBuffer;
    vec2i     fbSize;
};

デバイス側で先ほどの構造体をグローバル関数として定義する。
それぞれのピクセルからのレイ(光源)の動作を担当する__raygen__renderFrame()
関数を定義し、任意の色をバッファーに入れる。

今回はレイ生成の関数しか使わないが、レイトレーシングの一連の流れに必要な関数 (__miss__radiance(), __anyhit__radiance(), __closesthit__radiance()など)は定義しておかなければならない。

// devicePrograms.cu
extern "C" __constant__ LaunchParams optixLaunchParams;

// レイ生成の挙動を定義
extern "C" __global__ void __raygen__renderFrame()
{
    ...
    optixLaunchParams.colorBuffer[fbIndex] = rgba;
}

// レイがどのオブジェクトにも当たらなかった時の挙動を定義
extern "C" __global__ void __miss__radiance()
{ /*! for this simple example, this will remain empty */ }

2. OptiX パイプラインの作成

レイトレーシングに必要な、レイの定義、物体に当たった時、当たらなかった時などを関数として定義していきます。

2a. OptiXの初期化、コンテントの作成

まずはOptiXの初期化、GPUの選択、OptiXコンテントの作成を行う。

// SampleRenderer.cpp
void SampleRenderer::initOptix()
{
    optixInit();    // OptiXを初期化する

    ...

    SetDevice(...);    // 使用するGPUの番号を指定
    StreamCreate(...);    // ストリームの作成
    cuCtxGetCurrent(...);    // 使用するGPUデバイスの情報を入手
    optixDeviceContextCreate(...);    // OptiX コンテントを作成

    ...
}

2b. OptiX モジュールの作成

次に、作成するプログラムの設定(モーションブラーの有無など)を設定した上でOptiXモジュールを作成することでパイプラインを最大限に最適化してくれる。

// SampleRenderer.cpp
void SampleRenderer::createModule()
{
    moduleCompileOptions = ...
    pipelineCompileOptions = ...\
    optixModuleCreateFromPT(...);
}

2c. プログラムグループの設定

レイトレーシングに関わる関数それぞれの紐づけを行う。

// SampleRenderer.cpp
void SampleRenderer::createRaygenPrograms()
{
    OptixProgramGroupOptions pgOptions = {};
    OptixProgramGroupDesc pgDesc    = {};
    pgDesc.kind                     = OPTIX_PROGRAM_GROUP_KIND_RAYGEN;
    pgDesc.raygen.module            = module;           
    pgDesc.raygen.entryFunctionName = "__raygen__renderFrame";    // レイ生成関数を開始点とする

    optixProgramGroupCreate(...);
}

void SampleRenderer::createMissPrograms()
{
    ... // 同様
}

2d. パイプラインの作成

上記で定義したレイトレーシングに関わる関数をひとまとめにし、パイプラインを作成する。

// SampleRenderer.cpp
void SampleRenderer::createPipeline()
{
    std::vector<OptixProgramGroup> programGroups;
    for (auto pg : raygenPGs)
      programGroups.push_back(pg);
    for (auto pg : missPGs)
      programGroups.push_back(pg);
    for (auto pg : hitgroupPGs)
      programGroups.push_back(pg);

    optixPipelineCreate(...);
}

3. シェーダーバインディングテーブルの作成

パイプライン中の各関数にユーザー定義の変数などを格納した上でデバイスにアップロードする。

// SampleRenderer.cpp
void SampleRenderer::buildSBT()
{
    std::vector<RaygenRecord> raygenRecords;
    for (int i=0;i<raygenPGs.size();i++) {
      RaygenRecord rec;
      OPTIX_CHECK(optixSbtRecordPackHeader(raygenPGs[i],&rec));
      rec.data = nullptr; /* for now ... */
      raygenRecords.push_back(rec);
    }
    raygenRecordsBuffer.alloc_and_upload(raygenRecords);
    sbt.raygenRecord = raygenRecordsBuffer.d_pointer();

    std::vector<MissRecord> missRecords;
    ... // 同様
}

4. レイトレーシングの開始

フレームバッファーを作成し、レイトレーシングを開始する。

// SampleRenderer.cpp
void SampleRenderer::render()
{
    launchParamsBuffer.upload(&launchParams,1);
    optixLaunch(...);
}

以上をビルド、実行すると同フォルダに画像が書き出されるはずです。

感想

OpenGLなど他のAPIと比べると確かに導入部分が大変な気がする。ただここでの自由なメモリー確保などが最適化やカスタマイズの自由度に貢献していそう。

シャドーレイなど複数のレイの定義、Acceleration Structure、リアルタイムノイズ除去などできることがたくさんあり、一度基盤ができてしまえば機能の付け足しは比較的簡単な模様。

Example 12ではソフトシャドウとノイズ除去を実装しており、リアルタイムで以下のようなクオリティーのレンダリングを行うことができる。RTX 2070 で実行したが60FPS以上出てるっぽい。

Optix 7 Course Example 3_4_2021 1_33_56 AM.png

参考文献

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1