Sprout.Darkroom で外部ファイルからのテクスチャマッピング
https://github.com/bolero-MURAKAMI/Sprout
Sprout.Darkroom に、コンパイル時に外部ファイルからテクスチャを読み込む機能を追加しました。
テクスチャマッピングの定番ということで、球体に世界地図を Spherical Mapping で貼り付けてみます。
-
- out.png
アフリカ大陸あたりを中心に、地球っぽい球体が描画されています。
下記がソースコードです。
gcc 4.7.0 20111126 (experimental) でコンパイルしています。
-
- darkroom.cpp
// // DARKROOM_TOTAL_WIDTH // DARKROOM_TOTAL_HEIGHT // #ifndef DARKROOM_TOTAL_WIDTH #define DARKROOM_TOTAL_WIDTH 8 #endif #ifndef DARKROOM_TOTAL_HEIGHT #define DARKROOM_TOTAL_HEIGHT DARKROOM_TOTAL_WIDTH #endif // // DARKROOM_TILE_WIDTH // DARKROOM_TILE_HEIGHT // #ifndef DARKROOM_TILE_WIDTH #define DARKROOM_TILE_WIDTH DARKROOM_TOTAL_WIDTH #endif #ifndef DARKROOM_TILE_HEIGHT #define DARKROOM_TILE_HEIGHT DARKROOM_TOTAL_HEIGHT #endif // // DARKROOM_OFFSET_X // DARKROOM_OFFSET_Y // #ifndef DARKROOM_OFFSET_X #define DARKROOM_OFFSET_X 0 #endif #ifndef DARKROOM_OFFSET_Y #define DARKROOM_OFFSET_Y 0 #endif #define SPROUT_CONFIG_SUPPORT_TEMPORARY_CONTAINER_ITERATION #include <cstddef> #include <cmath> #include <iostream> #include <sprout/config.hpp> #include <sprout/darkroom.hpp> SPROUT_STATIC_CONSTEXPR std::size_t total_width = DARKROOM_TOTAL_WIDTH; SPROUT_STATIC_CONSTEXPR std::size_t total_height = DARKROOM_TOTAL_HEIGHT; SPROUT_STATIC_CONSTEXPR std::size_t tile_width = DARKROOM_TILE_WIDTH; SPROUT_STATIC_CONSTEXPR std::size_t tile_height = DARKROOM_TILE_HEIGHT; SPROUT_STATIC_CONSTEXPR std::size_t offset_x = DARKROOM_OFFSET_X; SPROUT_STATIC_CONSTEXPR std::size_t offset_y = DARKROOM_OFFSET_Y; int main() { using namespace sprout::darkroom; # define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex # define DARKROOM_DEF_LOAD_TEXTURE_FILE "/home/boleros/pub/earth.tex.hpp" # include DARKROOM_LOAD_TEXTURE SPROUT_STATIC_CONSTEXPR auto object = sprout::make_tuple( objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::x, -10.0, materials::make_uniform_material_image( colors::rgb_f(1.0, 0.0, 0.0), 0.0 ) ), objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::x, 10.0, materials::make_uniform_material_image( colors::rgb_f(0.0, 1.0, 0.0), 0.0 ) ), objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::y, -3.0, materials::make_plaid_material_image( colors::rgb_f(1.0, 1.0, 1.0), colors::rgb_f(0.25, 0.25, 0.5), 0.0, 0.5 ) ), objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::y, 7.0, materials::make_uniform_material_image( colors::rgb_f(1.0, 1.0, 1.0), 0.0 ) ), objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::z, 12.0, materials::make_uniform_material_image( colors::rgb_f(1.0, 1.0, 1.0), 0.75 ) ), objects::make_aa_plane( sprout::darkroom::objects::aa_plane_direction::z, -2.0, materials::make_uniform_material_image( colors::rgb_f(1.0, 1.0, 1.0), 0.75 ) ), objects::make_sphere( coords::vector3d(-1.0, 0.5, 7.0), 3.0, materials::make_material_image( materials::make_texture_map( tex, // texture 1.0, // scaling -0.5, // offset_u 0.0, // offset_v colors::rgb_f(), // base_color materials::texture_map_interpolation::bilinear ), materials::make_uniform(0.25) ) ) ); SPROUT_STATIC_CONSTEXPR auto light = lights::make_point_light( coords::vector3d(7.0, 6.0, 0.0), colors::rgb_f(15.0, 15.0, 15.0) ); SPROUT_STATIC_CONSTEXPR auto camera = cameras::make_simple_camera( std::sqrt(3.0) / 2 ); SPROUT_STATIC_CONSTEXPR auto renderer = renderers::whitted_style(); SPROUT_STATIC_CONSTEXPR auto raytracer = tracers::raytracer<>(); typedef pixels::color_pixels<tile_width, tile_height>::type image_type; SPROUT_STATIC_CONSTEXPR auto image = pixels::generate<image_type>( raytracer, renderer, camera, object, light, offset_x, offset_y, total_width, total_height ); std::cout << "P3" << std::endl << image[0].size() << ' ' << image.size() << std::endl << 255 << std::endl ; for (auto i = image.begin(), last = image.end(); i != last; ++i) { auto const& line = *i; for (auto j = line.begin(), last = line.end(); j != last; ++j) { auto const& pixel = *j; std::cout << unsigned(colors::r(pixel)) << ' ' << unsigned(colors::g(pixel)) << ' ' << unsigned(colors::b(pixel)) << std::endl ; } } }
下記がテクスチャを読み込んでいる部分です。
# define DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER tex # define DARKROOM_DEF_LOAD_TEXTURE_FILE "/home/boleros/pub/earth.tex.hpp" # include DARKROOM_LOAD_TEXTURE
DARKROOM_DEF_LOAD_TEXTURE_IDENTIFIER で定義した識別子が、テクスチャの識別子になります。
この場合は tex です。
DARKROOM_DEF_LOAD_TEXTURE_FILE で定義した文字列が、テクスチャのパスです。
ここでは earth.tex.hpp を読み込んでいます。
上記二つを定義したうえで #include DARKROOM_LOAD_TEXTURE すれば、テクスチャが読み込まれます。
この仕組みの実装は、本質的には include ディレクティブで CVS を取りこむトリックと同じです。
constexpr auto tex{ # include "/home/boleros/pub/earth.tex.hpp" };
earth.tex.hpp の中身は以下のようになっています。
-
- earth.tex.hpp
#if defined(DARKROOM_LOADING_TEXTURE_VERSION) DARKROOM_TEX_VERSION(0) #elif defined(DARKROOM_LOADING_TEXTURE_INFO) DARKROOM_TEX_IMAGE_DEFAULT, DARKROOM_TEX_PIXEL_INT_R8G8B8, 128, 64 #elif defined(DARKROOM_LOADING_TEXTURE_PIXEL) 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe1e2e2, 0xe2e3e2, 0xe2e3e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe1e2e2, 0xe3e3e2, 0xe1e2e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe1e2e2, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe1e2e2, 0xe2e2e1, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe1e2e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe1e2e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe2e3e2, 0xe2e3e2, 0xe2e3e2, 0xe2e2e1, 0xe1e2e2, 0xe2e2e2, 0xe1e2e2, 0xe1e2e2, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e6, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e6, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e6, 0xe7e7e6, 0xe7e7e6, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, 0xe7e7e7, /* 略 */ #endif
基本的にピクセルが列挙されているだけなので、画像ファイルからこの形式への変換は簡単です。
例えば earth.tex.hpp は、以下のようなコードで変換しました。
(要 OpenCV 2.x)
-
- texconv.cpp
#include <cstddef> #include <iostream> #include <iomanip> #include <opencv/cv.h> #include <opencv/highgui.h> #include <opencv/cv.hpp> #include <opencv/highgui.hpp> int main(int argc, char* argv[]) { if (argc < 2) { std::cerr << "#error missing parameter.\n" << std::flush ; return 0; } cv::Mat image(cv::imread(argv[1])); std::cout << "#if defined(DARKROOM_LOADING_TEXTURE_VERSION)\n" "\n" "DARKROOM_TEX_VERSION(0)\n" "\n" "#elif defined(DARKROOM_LOADING_TEXTURE_INFO)\n" "\n" "DARKROOM_TEX_IMAGE_DEFAULT,\n" "DARKROOM_TEX_PIXEL_INT_R8G8B8,\n" << image.cols << ",\n" << image.rows << "\n" << "\n" "#elif defined(DARKROOM_LOADING_TEXTURE_PIXEL)\n" "\n" ; std::cout << std::hex; for (std::size_t y = 0, h = image.rows; y != h; ++y) { for (std::size_t x = 0, w = image.cols; x != w; ++x) { auto pixel = image.ptr(y) + x * 3; std::cout << "0x" << std::setfill('0') << std::setw(2) << unsigned(pixel[2]) << std::setfill('0') << std::setw(2) << unsigned(pixel[1]) << std::setfill('0') << std::setw(2) << unsigned(pixel[0]) ; if (!(y == h - 1 && x == w - 1)) { std::cout << ','; if (x != w - 1) { std::cout << ' '; } } } std::cout << '\n'; } std::cout << std::dec; std::cout << "\n" "#endif\n" << std::flush ; }
$ g++ -Wall -std=gnu++0x -o texconv texconv.cpp -lcxcore -lcv -lhighgui $ ./texconv earth.png > earth.tex.hpp
元となった earth.png は↓これです。
-
- earth.png
レンダリング作業は、前回と同じく darkroom.sh を使いました。
-
- darkroom.sh
#!/bin/bash TARGET_DIR=darkroom mkdir -p $TARGET_DIR TOTAL_WIDTH=512 TOTAL_HEIGHT=512 TILE_WIDTH=8 TILE_HEIGHT=8 for ((y=0; y<TOTAL_HEIGHT; y+=TILE_HEIGHT)); do for ((x=0; x<TOTAL_WIDTH; x+=TILE_WIDTH)); do mkdir -p $TARGET_DIR/$y/ binname=$x.$y.out g++ -o $binname -std=gnu++0x \ -I/home/boleros/git/sprout \ -DDARKROOM_TOTAL_WIDTH=$TOTAL_WIDTH \ -DDARKROOM_TOTAL_HEIGHT=$TOTAL_HEIGHT \ -DDARKROOM_TILE_WIDTH=$TILE_WIDTH \ -DDARKROOM_TILE_HEIGHT=$TILE_HEIGHT \ -DDARKROOM_OFFSET_X=$x \ -DDARKROOM_OFFSET_Y=$y \ darkroom.cpp && ./$binname > $TARGET_DIR/$y/$x.ppm rm $binname done; pushd $TARGET_DIR/$y/ convert +append $(ls *.ppm | sort -n) ../$y.ppm popd done; pushd $TARGET_DIR convert -append $(ls *.ppm | sort -n) out.ppm popd
このように、constexpr でとても解りやすく簡単に 3D を扱えることは確定的に明らかです。
皆さんもっと constexpr を使いましょう。