LoginSignup
26
19

More than 5 years have passed since last update.

冬到来! NanoRT ではじめよう C++ レイトレーシング

Last updated at Posted at 2016-11-13

冬到来!

寒い冬はこたつで新しいジブンにチャレンジしたいですね!

NanoRT でレイトレーシングと C++ プログラミングを学ぼう!

NanoRT ならラズベリーパイ(ARM)でも動くよ!
NanoRT の C++ コードは cpplint と clang-format でキレイに保たれているからはじめての C++ 若人にも安心だよ!
(examples の一部のコードは除く)

とりあえずビルドしてうごかしてみる.

まずは, C++ コンパイラである, g++ or clang++ いれようね.
git もいれようね.
Windows のひとは Visual Studio もいいけど, Bash on Ubuntu on Windows もおすすめだよ!

ソースコードをクローンするよ.

$ git clone https://github.com/lighttransport/nanort

いくつかサンプルがある中で, まずは objrender をビルドしてうごかしてみよう!

$ cd examples/objrender
$ g++ -O2 -g -o testrender -I../../ -I../common/ -I. main.cc tiny_obj_loader.cc

コンパイルオプションを説明すると...

  • -O2 : プログラムの最適化を有効にするよ
  • -g : デバッグ情報をプログラムに埋め込むよ
  • -o testrender : プログラムの名前を指定するよ
  • -I : ヘッダファイルを探すパスを指定するよ
  • main.cc tiny_obj_loader.cc : 入力の C++ ソースファイルを指定するよ

コンパイルに成功したらプログラムを動かしてみよう

$ ./testrender

render.exr という, OpenEXR 形式の画像が生成されます.

macOS だと Preview で見れます. Linux だと ImageMagick で見れます.
(Windows のひとは Blender の画像ビュー機能を使うといいかも https://www.blender.org/ )

raytrace.png

ジャーーン! 絵がでました!

楽しいね!

キモになるのは main() 関数内の 100 行くらいの部分. 写経して学ぼう!
write your shader here のところで, 色を二倍とかしてみたりするといいかも.

int main(int argc, char** argv)
{
  int width = 512;
  int height = 512;

  float scale = 1.0f;

  std::string objFilename = "cornellbox_suzanne.obj";

  if (argc > 1) {
    objFilename = std::string(argv[1]);
  }

  if (argc > 2) {
    scale = atof(argv[2]);
  }

  bool ret = false;

  Mesh mesh;
  ret = LoadObj(mesh, objFilename.c_str(), scale);
  if (!ret) {
    fprintf(stderr, "Failed to load [ %s ]\n", objFilename.c_str());
    return -1;
  }

  nanort::BVHBuildOptions<float> build_options; // Use default option
  build_options.cache_bbox = false;

  printf("  BVH build option:\n");
  printf("    # of leaf primitives: %d\n", build_options.min_leaf_primitives);
  printf("    SAH binsize         : %d\n", build_options.bin_size);

  timerutil t;
  t.start();

  nanort::TriangleMesh<float> triangle_mesh(mesh.vertices, mesh.faces, sizeof(float) * 3);
  nanort::TriangleSAHPred<float> triangle_pred(mesh.vertices, mesh.faces, sizeof(float) * 3);

  printf("num_triangles = %lu\n", mesh.num_faces);
  printf("faces = %p\n", mesh.faces);

  nanort::BVHAccel<float, nanort::TriangleMesh<float>, nanort::TriangleSAHPred<float>, nanort::TriangleIntersector<> > accel;
  ret = accel.Build(mesh.num_faces, build_options, triangle_mesh, triangle_pred);
  assert(ret);

  t.end();
  printf("  BVH build time: %f secs\n", t.msec() / 1000.0);


  nanort::BVHBuildStatistics stats = accel.GetStatistics();

  printf("  BVH statistics:\n");
  printf("    # of leaf   nodes: %d\n", stats.num_leaf_nodes);
  printf("    # of branch nodes: %d\n", stats.num_branch_nodes);
  printf("  Max tree depth     : %d\n", stats.max_tree_depth);
  float bmin[3], bmax[3];
  accel.BoundingBox(bmin, bmax);
  printf("  Bmin               : %f, %f, %f\n", bmin[0], bmin[1], bmin[2]);
  printf("  Bmax               : %f, %f, %f\n", bmax[0], bmax[1], bmax[2]);

  std::vector<float> rgb(width * height * 3, 0.0f);

  t.start();

  // Shoot rays.
  #ifdef _OPENMP
  #pragma omp parallel for
  #endif
  for (int y = 0; y < height; y++) {
    for (int x = 0; x < width; x++) {

      // Simple camera. change eye pos and direction fit to .obj model. 

      nanort::Ray<float> ray;
      ray.org[0] = 0.0f;
      ray.org[1] = 5.0f;
      ray.org[2] = 20.0f;

      float3 dir;
      dir[0] = (x / (float)width) - 0.5f;
      dir[1] = (y / (float)height) - 0.5f;
      dir[2] = -1.0f;
      dir.normalize();
      ray.dir[0] = dir[0];
      ray.dir[1] = dir[1];
      ray.dir[2] = dir[2];

      float kFar = 1.0e+30f;
      ray.min_t = 0.0f;
      ray.max_t = kFar;

      nanort::TriangleIntersector<> triangle_intersector(mesh.vertices, mesh.faces, sizeof(float) * 3);
      nanort::BVHTraceOptions trace_options;
      bool hit = accel.Traverse(ray, trace_options, triangle_intersector);
      if (hit) {
        // Write your shader here.
        float3 normal(0.0f, 0.0f, 0.0f);
        unsigned int fid = triangle_intersector.intersection.prim_id;
        if (mesh.facevarying_normals) {
          normal[0] = mesh.facevarying_normals[9*fid+0];
          normal[1] = mesh.facevarying_normals[9*fid+1];
          normal[2] = mesh.facevarying_normals[9*fid+2];
        }
        // Flip Y
        rgb[3 * ((height - y - 1) * width + x) + 0] = fabsf(normal[0]);
        rgb[3 * ((height - y - 1) * width + x) + 1] = fabsf(normal[1]);
        rgb[3 * ((height - y - 1) * width + x) + 2] = fabsf(normal[2]);
      }
    }
  }

  t.end();
  printf("Render %f secs\n", t.msec() / 1000.0);

  // Save image.
  SaveImage("render.exr", &rgb.at(0), width, height);
  // Save Raw Image that can be opened by tools like GIMP
  SaveImageRaw("render.data", &rgb.at(0), width, height);

  return 0;
}

もっとレイトレーシングやりたい!

examples/path_tracer を動かすともっときれいな絵がでるよ!

夏休み到来! 最高のパストレーシングの夏にしよう!

C++ をもっと知りたい!

The Definitive C++ Book Guide and List

レイトレーシングをもっと知りたい!

Disney が作ったレイトレーシング(パストレーシング)の解説が参考になるよ!
(レイトレーシング人材育成のためにディズニーとかすごい頑張っているよ!)

PBRT-v3 もおすすめ!

1,000 ページを超える本は圧巻だね! Kindle 版で iPad とかタブレットで見るのもおすすめ.

そして, 2018 年 10 月 15 日に, PBRT book v3 が web で無償公開されたよ.

HTML もいいけど, 本や PDF でみたいときもあるよね. 製本版や Kindle 版はクリスマスプレゼントとしておねだりしよう!

ちなみにレイトレーシングをするには, 最低限中学数学の知識が必須! できれば高校数学の知識が望ましい.

「はじめようレイトレーシング」は, レイトラ :tiger: と一緒にレイトレーシングを学ぶことができるよ.

優秀な小学生さまにおすすめ!

「はじめようレイトレーシング」でレイトレーシングを学ぶことで, 中学, 高校数学を楽しく学ぶことができるね!

TODO

  • 優秀な若人さまが, 日々切磋琢磨し, レイトレーシングと C++ を人類史上最速でお極めなされるスキームを確立したい.
26
19
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
26
19