プラグインの作成
ここまでTutorial[入門編]では基本的なシーンの作成方法やアセットの設定方法などについて話を進めてきました。 発展編では新たに実装したいレンダリングシステムなどの拡張をLightmetricaではいかに簡単に作成できるかについて説明していきたいと思います。
プラグインの役割
Lightmetricaではほとんどの構成要素(例:新規アセットやレンダリング手法)を簡単に拡張することができます。 Lightmetricaにすでに実装されているシステムの正確性は保障されているので、例えば研究用に新規に実装したレンダリング手法を比較したい時や新たに作成したシステムを試したい時,一般化したいシステムを作成したい時などにLightmetricaでは簡単に拡張することができます。
プラグインの実装方法
実装環境
- Windowsお使いの方:Visual Studio2015のインストールをしてください。
- Mac:特に何の準備も必要ないです。
Lightmetricaで実装する際に用いる言語はc++
です。
#include <lightmetrica/lightmetrica.h>
を最初に宣言するだけで必要なライブラリを参照でき、手軽に新規プラグインを作成できます。
新たに作成したプラグインファイル名(hogehoge.cpp
)は任意でいいですが、動的ライブラリを作成時には以下のようにしなくてはなりません。
動的ライブラリの名前
- Windows:
(インターフェース名)
_(実装名)
.dll - Mac:
(インターフェース名)
_(実装名)
.dylib - Linux:
(インターフェース名)
_(実装名)
.so
というように記述します。
例えば、新しいレンダラの手法としてmymethod
という実装名の、自分の手法を用いたレンダラを作成したいとしたら、
renderer
_mymethod
.dll(or .dylib or .so)のように動的ライブラリの名前を設定します。
百聞は一見に如かず。ということで、2つのプラグインを例に拡張方法を見て理解を深めていきましょう。
例1: 新規テクスチャの作成
まずはLightmetricaの中のplugin
フォルダ内にあるtexture_checker
フォルダにtexture_checker.cpp
というソースファイルが入っているのを確認してください.
これは既に作成されたtexture
の拡張プラグインになります。
コマンドラインを開いて
lightmetrica
->bin
->plugin
フォルダまで下図のように移動したあとに,
cd lightmetrica/
cd bin/
cd plugin/
コンパイルして動的ライブラリを作ってやりましょう。わずかなコマンドだけでできます。
- Macの場合:
clang++ -dynamiclib -std=c++1y -march=native -I”../../include/” “../../plugin/texture_checker.cpp” -o texture_checker.dylib
- Windowsの場合:
“C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall” x86_amd64
cl /LD /EHsc texture_test.cpp /I”” /link /out:texture_checker.dll
このコマンドでMacの場合dylibファイルが、Windowsの場合dllファイルがbin
フォルダのplugin
フォルダに追加されました。
先ほどの注意を思い出してください。
今texture_checker
という名前で動的ライブラリを作りました。
なのでinterface
はtexture
に,type
は実装名のchecker
にしてやる必要があります。
それに注意した状態であとはscene
ファイルを書き換えてやるだけです。
bsdf_checker:
interface: bsdf
type: diffuse
params:
TexR: checker
checker:
interface: texture
type: checker
params:
scale: 100
color1: 1 0 0
color2: 1 1 1
textureをtexture_checker
で拡張したのでinterface
をtexture
に、type
をchecker
と記述してtextureを作成します。
あとは今までと同じように実行すると、
下図のように床がチェック模様の画像が出来上がりました。
実はこのtexture_checker.cpp
は画像を用いてtextureを作成するのではなく、meshの位置を計算してチェック模様のtextureを作成するように作られています。scale
で縞模様の大きさをcolor1
とcolor2
でそれぞれの格子の色を決定できるようにしています。
試しに色と大きさを変えてみましょう。
bsdf_checker:
interface: bsdf
type: diffuse
params:
TexR: checker
checker:
interface: texture
type: checker
params:
scale: 50
color1: 1 1 0
color2: 1 0 1
ちゃんと色と格子の大きさが変わりましたね。 このように自分の好きなようにプラグインを用いて拡張することができます。
それではtexture_checker.cpp
のコードを見てどのように作成しているかを見てみましょう。
#include <lightmetrica/lightmetrica.h>
LM_NAMESPACE_BEGIN
class Texture_Checker final : public Texture
{
public:
LM_IMPL_CLASS(Texture_Checker, Texture);
public:
LM_IMPL_F(Load) = [this](const PropertyNode* prop, Assets* assets, const Primitive* primitive) -> bool
{
scale_ = prop->ChildAs<Float>("scale", 100_f);
color1_ = prop->ChildAs<Vec3>("color1", Vec3(1_f, 0_f, 0_f));
color2_ = prop->ChildAs<Vec3>("color2", Vec3(1_f));
return true;
};
LM_IMPL_F(Evaluate) = [this](const Vec2& uv) -> Vec3
{
const int u = (int)(uv.x * scale_);
const int v = (int)(uv.y * scale_);
return (u + v) % 2 == 0 ? color1_ : color2_;
};
private:
Float scale_;
Vec3 color1_;
Vec3 color2_;
};
LM_COMPONENT_REGISTER_IMPL(Texture_Checker, "texture::checker")
細かい作成方法は次のAPIの読み方で説明しますので、ここでは簡単にだけ説明します。
プラグインに与えてやりたいパラメータはLM_IMPL_F(Load)
メソッド内に記述します。つまりsceneファイルのparams
以下に入るパラメータをここで記述してやります。
今、texture_checker
はscale
とcolor1
とcolor2
の3つのパラメータがありましたよね。
sceneファイルの中のparams
以下の木構造がprop
に入るので、その値をchildAs
で取得してやることでscene
ファイルに記述した値が読み込まれます。
後はLM_IMPL_F(Evaluate)
で、戻り値がVec3
のEvaluate
という関数を作って、床の場所の位置によってcolor1_
かcolor2_
のどちらを返すかを決定するように作成して、格子模様を作っています。
例2:新規レンダラの作成
例1とやることは変わりません。 (フォルダ名)にrender_nomal.cppというファイルが入っています。 先ほどと同じようにコンパイルしてやるだけです。
- Macの場合:
clang++ -dynamiclib -std=c++1y -march=native -I”../../include/” “../../plugin/texture_checker.cpp” -o texture_checker.dylib
- Windowsの場合:
“C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall” x86_amd64同じようにsceneファイルを少しだけ書き換えてやりましょう。
cl /LD /EHsc texture_test.cpp /I”” /link /out:texture_checker.dll renderer: type: normal params: num_threads: -1 render_time: 5 #num_samples: 100000000 max_num_vertices: 5
今回はrenderer_normalという名前にしたのでtype
はnormal
と記述します。
それでは実行結果を見てみましょう。
今回は法線マップを作成できるようにrenderer
を拡張しました。
同じようにソースコードを見てみましょう。
#include <lightmetrica/lightmetrica.h>
LM_NAMESPACE_BEGIN
class Renderer_Normal final : public Renderer
{
public:
LM_IMPL_CLASS(Renderer_Normal, Renderer);
public:
LM_IMPL_F(Initialize) = [this](const PropertyNode* prop) -> bool
{
return true;
};
LM_IMPL_F(Render) = [this](const Scene* scene, Film* film) -> void
{
const int w = film->Width();
const int h = film->Height();
for (int y = 0; y < h; y++)
{
for (int x = 0; x < w; x++)
{
// Raster position
Vec2 rasterPos((Float(x) + 0.5_f) / Float(w), (Float(y) + 0.5_f) / Float(h));
// Position and direction of a ray
const auto* E = scene->Sensor()->emitter;
SurfaceGeometry geomE;
E->SamplePosition(Vec2(), geomE);
Vec3 wo;
E->SampleDirection(rasterPos, 0_f, 0, geomE, Vec3(), wo);
// Setup a ray
Ray ray = { geomE.p, wo };
// Intersection query
Intersection isect;
if (!scene->Intersect(ray, isect))
{
// No intersection -> black
film->SetPixel(x, y, SPD());
continue;
}
// Set color to the pixel
film->SetPixel(x, y, SPD::FromRGB(Vec3(
Math::Abs(isect.geom.sn.x),
Math::Abs(isect.geom.sn.y),
Math::Abs(isect.geom.sn.z))));
}
}
};
};
LM_COMPONENT_REGISTER_IMPL(Renderer_Normal, "renderer::normal");
LM_NAMESPACE_END
これも細かい説明はここでは省きますが、LM_IMPL_F(Initialize)
メソッドでは、パラメータを読み込んだり(今回の例ではなし),そのレンダリング手法に必要な前処理などを記述します。
あとはLM_IMPL_F(Render)
で実装したいレンダリング手法をfilmに書き出すように記述していきます。
基本的な作り方はこのようになります。 次章のAPIの読み方では、どのようなAPIがあるか、又より詳しくプラグインの作成方法を説明します。