PlantFactoryから[for Blender]で出力したFBXファイルをCyclesでImportしたときに、非透過テクスチャを透過させるより簡単な方法を紹介する。
まず、読み込んだ直後はこのようになっている。
まず、一番左上、アルファ用のモノクロテクスチャのColorから、MixColor/AlphaのColorへ繋げる。これだけで透過するようになる。
次に、Displacementがよくわからないことになっているので、カラー画像からバンプマップを作り、それをDisplacementへ接続する。Invertは色を反転し、RGB to BWは色を0~1.0の値に変換する。
最後。正直これはいるのかいらないのかわからないが、赤くなっているので、[Refl BSDF]に対応しそうな[Refraction BSDF]に置き換える。
作業結果:
前回「クラスはC++にデータ型を追加するための機能である」ということを説明した。
今回は、継承とは何か、を説明する。
くどいようだが、クラスは現実世界にあるモノの種類をデータ型として定義する機能である。継承はそれをさらに発展させ、
「○○は■■の一種である」という定義を、C++語で行うための機能なのである。
具体的な例を挙げると、日本語で「リンゴは果物の一種である」という定義を、C++言語で記述するための機能である。
「リンゴは果物の一種である」をC++語に翻訳すると、以下のようになる
class リンゴ : public 果物{...};
断っておきたいが、確かに継承した子クラスは親クラスのメンバ変数等をすべて持つことになる。だが、
継承は、似たようなクラスを量産するための機能ではない。
例えば
class Apple { unsigned char Color[3]; double weight; int price; };
が既に定義済みの時に、
class Orange { unsigned char Color[3]; double weight; int price; };
を作りたいとする。
もし、
class Orange : public Apple{ //継承したので、メンバ変数は全てAppleを引き継ぐ };
というコードを書いたら、それは
「このプログラムにおいて、オレンジはリンゴの一種であるとする」
と宣言したのと同じことなのだ。
例え既に定義されているクラスAppleと、メンバ変数も、メンバ関数も、全く同じクラスOrangeが必要になったとしても、「OrangeはAppleの一種である」という関係が成り立たないなら、継承を使ってはならない。
この例なら、普通は両方のクラスをFruitクラスからの継承とするだろう。以下のようになる。
※ただし、「Appleとほとんど同じ内容のクラスをもう一つ作るのは無駄に思えるので、Fruitを作って、AppleとOrangeはFruitを継承しましょう」ということを書いている解説書があるが、誤解を招く表現だと思っている。楽をするために継承を使ってはいけない。
class Fruit{ unsigned char Color[3]; double weight; int price; };
//リンゴというデータ型を定義。これは果物の一種である。 class Apple:public Fruit{ //果物の一種なので、パラメータの種類は果物から引き継ぐ }; //オレンジというデータ型を定義。これは果物の一種である。 class Orange:public Fruit{ //果物の一種なので、パラメータの種類は果物から引き継ぐ };
だが、それすらできない場合もある。
class すっぽん : public 月{ ... }
日本語には「月とすっぽん」ということわざがある。意味は、
似たところもあるが、比較にならないほどの違いがある
である。月はすっぽんの一種ではないし、その逆でもない。そしてこの両者は方や生き物、方や星である。種類としての共通点がない。
そしてこれは「ほかのプログラマから、オレンジはリンゴの一種という意図をもって書いたと解釈され、馬鹿にされる」という次元の話をしているのではない。
class Orange : public Apple は、OrangeはAppleの一種である、という定義のC++語訳であるから、C++のコンパイラがそのように解釈してしまうのが困る点なのである。C++が意味を勘違いすると、後でとんでもないバグを引き起こす可能性がある。正確には、プログラマのミスを検出してくれないのである。
続く...
e-onのPlantFactoryはBlender向けにExportできる(ただしPLE版では削減されるらしい)。
のだが、なぜかちゃんと読み込めない(いつもの事)。
現在試行錯誤中。
とりあえず、アルファチャンネルをもたないjpgなどの画像にモノクロのマスクをつけて透過する方法をここにメモしておく。
ちなみに、e-onのライセンスに触れたくないので加工画像だが、カラー画像、アルファ画像はそれぞれ以下のようなもの。
出力結果。言いたいことはいろいろあるが、とりあえずそれっぽくできた。
Daz Studio から Blender Cyclesへの変換。
mcjTeleBlender3を使えばいい。
参考にさせていただきました:
http://3d-memo.blog.jp/archives/1049736434.html
はずなのだが、なぜか動かない。動いてもテクスチャが反映されない。
私のところでは以下二つが問題だった。
出力されたscene.batファイルをテキストエディタで開く。
Blenderのパスが自分の環境にあっていなかったら、自分の環境にあったものに変更する。
ちなみにこのパスはDAZ Studio側の、mcjTeleBlender3のAuxiliary Optionsタブから変更できる。
テクスチャファイル(.mtlファイル)にはテクスチャファイルへのフルパスが入っているが、そのパスの半角スペースが、全て「 _ 」(アンダーバー)に置き換えられていた。
エクスプローラなどのファイラで確認し、テクスチャへの正しいパスに置換する。量が多いのでテキストエディタの置換機能などを使ったほうがいい。
これでやっとテクスチャがつく。あとは別に作ったシーンをBlenderで開き、フィギュアをAppendすればいい。
学校の授業では構文は教えてくれるがその機能が何のためにあるかを教えてくれない。なぜだ。なぜなのだ。
そんなわけで、ここでは「クラスとは何で、使うと何がいいのか」について私なりの解釈を載せようと思う。
まず、かなり極端な例だが、以下のようなプログラムを考える。
//身長を入力する関数 double scanHeight(){ double h; printf("身長を入力してください:"); scanf("%lf",&h); return h; } //体重を入力する関数 double scanWeight(){ double w; printf("体重を入力してください:"); scanf("%lf",&w); return w; } //BMIを計算する int main(){ double HeightA = scanHeight(); double WeightA = scanWeight(); double HeightB = scanHeight(); double WeightB = scanWeight(); double BMI_A = WeightA*HeightA*HeightA; double BMI_B = WeightB*HeightB*HeightB; }
このプログラムは正常に動作する(多分)。だが、コーディングという観点からみるとバグを生みやすい書き方をしているといえる。
以下のようなバグを作る可能性がある。
int main(){ double HeightA = scanHeight(); double WeightA = scanHeight();//コピペミスかなにか。コンパイルは通る double HeightB = scanHeight(); double WeightB = scanWeight(); double BMI_A = WeightA*HeightA*HeightA; double BMI_B = WeightB*HeightB*HeightB; }
このようなバグを作った場合、実行時には三回連続で身長を聞かれてしまう。
ここで問題である。なぜこのソースコードがコンパイルを通るのか?
WeightAという変数を体重用の変数と考えているのも、scanHeightという関数を身長用の関数だととらえているのも、いずれも人間が勝手にそうだと言い張っているだけなのだ。コンピュータ(コンパイラ及びCPU)には、このプログラムは以下のように見える。
int main(){
double型の変数1 = doubleを返す関数1();
double型の変数2 = doubleを返す関数1();
double型の変数3 = doubleを返す関数1
double型の変数4 = doubleを返す関数2;
double型の変数5 = 変数2*変数1*変数1;
double型の変数6 = 変数4*変数3*変数3
}
変数名WeightAや関数名scanHeightはあくまで人間がわかりやすいように人間が勝手につけたものだ。コンピュータにとってはWeightAもHeightAも、double型の二つの変数、程度の違いしかない。
ここでもし、以下のように書けたら、きっとエラーが出るに違いない。
//身長を入力する関数 heightValue_t scanHeight(){...} //体重を入力する関数 weightValue_t scanWeight(){...} int main(){ heightValue_t HeightA = scanHeight(); weightValue_t WeightA = scanHeight();//エラー WeightAは体重型だがscanHeightが返すのは身長の値 heightValue_t HeightB = scanHeight(); weightValue_t WeightB = scanWeight(); bmiValue_t BMI_A = WeightA*HeightA*HeightA; bmiValue_t BMI_B = WeightB*HeightB*HeightB; }
C++が扱えるデータの種類は文字コード(char)や小さな整数(short)や大きな整数(int)や小数(double)といった、「数値」だけである。
それだけでもプログラムを組むことはできるのだが、「体重」や「身長」や「人間」や「車」といった、現実に存在する様々な種類のデータを扱いたい。
データの種類を追加することで、現実世界に存在するモノに対する処理を、ソースコード上に、C++言語の文法で記述できるようにするための機能。
それがクラスなのである。
例えば、
class Apple{ unsigned char Color[4]; double weight; };
というプログラムを書いたら、それは
このプログラムには「リンゴ」という種類(データ型)が定義されており、リンゴというモノ(インスタンス)には、色と重さがある
という日本語を、C++語に翻訳したことになるのだ。
続く...
C++11のunique_ptr<>。これ基本的な考え方は大したことないんだが物凄く便利だ。
関数内でメモリを確保するときは、スタック領域に確保するかヒープ領域に確保するかの二通りの考え方がある。
スタック領域に確保する場合:
メリット:スコープを抜けたときに自動で解放してくれる
デメリット:あまり大きなメモリは確保できない , サイズが固定である
ヒープ領域に確保(new)する場合:
メリット:大きなメモリを確保できる , 実行時にサイズを指定できる
デメリット:明確なdeleteを忘れるとメモリリークを起こす
問題は、狭いスコープ内、例えば関数内、if文の{}内などでメモリを確保したくなり、サイズを実行時に指定したくしかも大きなサイズが必要というケースは頻繁にあるということだ。
少し余分に確保しておく、という方法が取れればそれでもいいが、そうでない限りnewを使うしかなくなる。すると同時にdeleteの義務が発生してしまうのである。
std::unique_ptrはこのような場合に用いることができる、
「スコープを抜けたら自動的に解放してくれるスタック変数のメリット」と
「大きな領域を動的に確保できるヒープのメリット」
を足したような素晴らしいポインタである。
#include <memory> #pragma warning(disable:4996) struct RGB { unsigned char r; unsigned char g; unsigned char b; }; int main() { int width; int height; scanf("%d %d", &width, &height); bool makeimage = true; //フラグが経っているときだけ画像を作成し、何かする if (makeimage == true) { std::unique_ptr<RGB[]> image = std::unique_ptr<RGB[]>(new RGB[width*height]); for (int i = 0; i < width*height; i++) { image[i].r = 0; image[i].g = 0; image[i].b = 0; } } return 0; }
このプログラムでは sizeof(char)×3×任意の値×任意の値 という、場合によっては巨大なメモリを動的に確保するが、delete [] の必要がない。書かなくても、ifブロックを抜けた時点で速やかに解放される。
GCではないので、コンピュータの気まぐれでいつ解放されるかわからない、という不安もついてこない。
----------------------
書きかけかも?