ボレロ村上 - ENiyGmaA Code

中3女子です。

Sprout.Darkroom で外部ファイルからのテクスチャマッピング

https://github.com/bolero-MURAKAMI/Sprout
Sprout.Darkroom に、コンパイル時に外部ファイルからテクスチャを読み込む機能を追加しました。


テクスチャマッピングの定番ということで、球体に世界地図を Spherical Mapping で貼り付けてみます。


アフリカ大陸あたりを中心に、地球っぽい球体が描画されています。


下記がソースコードです。
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 は↓これです。


レンダリング作業は、前回と同じく 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 を使いましょう。