挿絵画家になろう(その3)


「挿絵画家になろう(その3)」です。
Blenderのアドオン、MB-Labの啓蒙です。
前回までで、MB-Labを使って少女キャラクターを生成し、簡素な服を着せ、あらかじめ用意されているポーズを切り替えるところまで実現しました。
今回は髪の毛です。

MB-Labにもアニメ調少年用、少女用の髪が一種類ずつ付属します。
それなりにクオリティが高いものですが、髪の毛はキャラクターの描き分けに必須です。
顔は同じで許すとして、髪の毛は異なるものを用意しないと人物の区別ができません。

私は髪の毛を用意するためにそれはそれはそれは色々な手法を試しました。
カーブという機能を使う方法、パーティクルを使う方法、UV球を変形させる方法、スカルプトでガチに造形する方法、その他色々……。
試しているうちに、Blenderの操作が少しずつ分かってきたのです。
費やした時間は膨大です。
その間、小説なんて全く執筆していません。
本末転倒なのです。

でもご安心ください。
この記事は『小説家になろう 』 としていて挿絵に困っている 同胞の諸兄姉に簡単に3Dキャラクターを使った挿絵を描く方法を展開することが目的です。
ソリューションがあるのです。
それは『VRoidStudio』という神アプリです。

Pixive 様に怒られそうですね。
VRoidStudioは髪型を作成するためのツールではありません。
Pixive様が開発・提供する3Dキャラクターモデル作成のための統合環境です。
寧ろMB-Labの競合ソフトです。

なにはともあれ、ゲットして試してみることをお勧めします。
本記事執筆時の最新版はVRoid Studio ベータ版 v0.7.3です。

起動するとキャラクターモデルの選択画面になります。

「あなたのモデル」として新規作成しても良いのですが、「サンプルモデル」として幾つかのサンプルモデルが選べるようになっています。
今回はSendagaya_Shinoを選びましょう。

これだけの操作で3Dキャラクター生成完了です。

質問:顔の編集できるのですか?
回答:はいできます。
質問:髪型の編集、できるのですか?
回答:はいできます。
質問:服の編集、できるのですか?
回答:いくつかの基本形があり、それぞれ変形させることができます。
   テクスチャを差し替え、印象をがらりと変えることができます。
質問:ポーズの変更とかできるのですか?
回答:いくつかのプリセットから選べます。
   VRM形式で描きだすことができるので、『3tene(ミテネ)』等の別ソフトにエキスポートすることもでき、それでポーズをつけることもできます。

質問:あれ? 挿絵のキャラクター作るのって、VroidStudioで良くね?

そのとおりです。
VRoidStudio』はバーチャルユーチューバーなどにも使える優れたソフトです。
実際にVChatやバーチャルユーチューバーに使われていて、その実用性は折り紙付き。

ではなぜVRoidStudioを使わずにMB-Labを使うのか?
MB-Labの人体メッシュへの愛、所以です。
それにVRoidStudioがいかに良いソフトであるかなんて、今更私が力説してもね……。

細かくは、VRoidStudioは顔に関してそれほど多くのバリエーションを持ちません。
メッシュが破綻するようなダイナミックな調整はできないのです。
恐らくはVRoidStudioの範囲内で留まる限り、defaultの表情からそれほど離れることはできないでしょう。
カスタムキャスト で作ったモデルが、カスタムキャストで作ったと判るように、VRoidStudioで作ったモデルも同様のことが言えそうです。
とはいえ、BlenderもVRM形式のデータを読み込む方法が無いわけではなく、一旦読み込んでしまえばあとはどうとでも編集できてしまいます。
ただBlenderとの親和性とメッシュの弄りやすさには差がでるかと……。

もう一つ、服に画像テクスチャを張り付ける仕様です。
服の造形にはそれほど自由度がありません。
VRoidStudioのコミニュティでは服用のテクスチャの開発が盛んですが、テクスチャ作成は職人芸の世界だったりします。
3Dメッシュを弄るのとどちらがより楽かという天秤になります。
私はそれっぽい2Dテクスチャを作るスキルを今に至って培うことができていません。
正直この辺は得手不得手なのでしょうね。

とりあえず髪の話に戻します。
VRoidStudioでは髪を比較的簡単に編集できます。
なおかつ髪をOBJ形式でエクスポートすることができます。

Sendagaya_Shino のdefaultの髪の毛をOBJ形式でエクスポートしてみましょう。
上段二行目のタブから『撮影・エクスポート』を選択し、左側メニューの『エクスポート』を選択します。
すると右側に『髪をOBJ形式でエクスポート(四角形メッシュ)』が選択できるようになります。

「Sendagaya_Shino_Hair.obj」というファイル名で保存してください。
次に前回MB-Labで作成したBlenderのプロジェクトにインポートします。
先ずはdefaultの髪を外します。
キャラクターの両足の間ににある横倒しになった四角錐を右クリックします。
そしてオブジェクトモードに切り替えます。

ツールシェルフの[ManuelBastioneLab]タグ、[PROXY FITTING]から[キャラクター:]をgirl1_bodyに、[プロキシ:]をhair01_anime_femaleを選択し、[Remove fitting]ボタンを左クリックします。

次に Sendagaya_Shino_Hair.obj をインポートします。

Sendagaya_Shino_Hairがdefaultの髪と重なって出現します。
オレンジ色に縁どりされ、選択された状態になっていると思いますが、選択されていなければ右クリックで選択してください。
Sendagaya_Shino_Hair の位置合わせを行います。
defaultの髪とほぼ同じ位置に重なるようにすればOKです。

Sendagaya_Shino_Hair を上への拡大([s]キーに続き[z]キー、後は[↑]キーで調整し[enter]キーで確定)、左右への拡大 ([s]キーに続き[x]キー、後は[←]キーで調整し[enter]キーで確定)、前後への移動 (テンキーの[3]キーを押して横にして、[g]キーに続き[y]キー、後は[←]キーで調整し[enter]キーで確定)等で調整します。

できましたらテンキーの[1]キーを押して正面を向かせ、PROXYシステムで装着します。
ツールシェルフの[ManuelBastioneLab]タグ、[PROXY FITTING]から[キャラクター:]をgirl1_bodyに、[プロキシ:]をSendagaya_Shino_Hairを選択し、[Fit Proxy]ボタンを左クリックします。

おお! 髪よ! フィットしました。
しかも輝くばかりの銀髪。
VRoidStudioとMB-Labとのキャラクター造形の違いにより前髪が眉上になってしまっていますが、これはこれでいいではないでしょうか?
気になる方は、髪を上下に拡大してから位置合わせをすると好みに合わせることができます。

髪の毛が銀色なのは袖なしTシャツ同様、マテリアルを設定していないからです。
Sendagaya_Shino_Hairオブジェクトにdefaultの髪のマテリアルを設定してみましょう。
オブジェクトモードになっていることを確認してから、Sendagaya_Shino_Hairを右クリックして選択します。
プロパティウインドウのマテリアル(赤と白の十字の円のマーク)を選択します

Sendagaya_Shino_Hairの項目の下、空の長方形の右の[+]ボタンを押して新しいマテリアルスロットを追加します。

新しいマテリアルスロットが現れるので[新規]となっているマテリアルのセレクターボタン([△]ボタンもしくは[▽]ボタン)を押して既存のマテリアルリストを表示させます。

MBLab_anime_hair_001を選択してください。

艶のある青髪になりました。
defaultの髪は不要になりますので削除します。
オブジェクトモードになっていることを確かめて、hair01_anime_femaleオブジェクトを右クリックして選択し、[delete]キーを押下します。

OK? と聞いてくるので[削除 X]ボタンを左クリックすると削除できます。
ついでに袖なしTシャツのマテリアルを設定します。
オブジェクトモードになっていることを確認し、上にあるViewのセレクタ([Default]となっている横の[△]ボタンか[▽]ボタン)を右クリックして現れる選択肢から[Composing]を選択します。

下にある3Dビューエディッタウインドウでgirl1_bodyオブジェクト(キャラクターの手とか足)を右クリックして選択します。
その後上にあるノードエディッタウインドウの下でマテリアルを選択します。
ノードエディッタにgirl1_MBLab_anime_skinが表示されます。
表示されない場合はズーム等で調整してください。

ノードエディッタウインドウの左上にマウスカーソルをあてて右ボタンを押したまま左下に動かすと矩形選択となりノード全てを選択することができます。

[ctrl]キーを押しながら[c]キーを押してコピーします。
次に3DビューエディッタウインドウTank_Top_Planeオブジェクトを右クリックして選択します。
ノードエディッタウインドウのマテリアルには何も設定されていない状態です。

マテリアル選択の[新規]のボタンを左クリックすると新しいマテリアルが生成されます。
二つのノードが現れるのですが、これを[delete]キーで消します。
そしてノードエディッタウインドウにマウスカーソルを合わせて[ctrl]キーを押しながら[v]キーを押して、ノードをペーストします。
これで girl1_MBLab_anime_skinのノードがコピーされました。
ここで[Anime_mblab_skin_diffuse]を[RGB]に差し替えます。
ノードエディッタウインドウ上で[Anime_mblab_skin_diffuse] 付近をズームアップします。
[shift]キーを押しながら[a]キーを押すと[追加]メニューで出てくるので[入力] → [RGB]を辿り、選択します。

[RGB]ノードが現れるので出力[カラー]を繋ぎ変えます。
カラーサークルから適当な色を選択すると袖なしTシャツの色が自由に変えられます。

同様にオブジェクトウインドウでSendagaya_Shino_Hairオブジェクトを選択し、ノードエディットウインドウ上の[RGB]のカラーサークルを変えると、髪の色を任意に変えることができます。

どうでしょうか?
メチャクチャ簡単だと思いませんか?
ここまで3D的な編集は殆どやっていません。
Blender、MB-Lab、VRoidStudioといった優れたソフトウェアを組み合わせることにより比較的自由に3Dキャラクターを生成できてしまいました。
これは凄いことなのです。

MB-LABの人体メッシュは秀逸です。
こんなにもクオリティの高いメッシュを使えるとは感謝しかありません。
VRoidStudioの髪型編集・作成機能を加えると強力この上ないソリューションとなります。
説明していませんが、設定されているマテリアルも良く考えられた応用の効くものです。
興味がおありでしたらノード構成を覗いて見られることをお勧めします。

今回はこれで終わりです。
絵は完成していませんが、時間があれば[その4]を書きたいと思います。


挿絵画家になろう(その2)


「挿絵画家になろう(その2)」です。
MB-Labの啓蒙です。
前回の扉絵は拙かったですかね?
学校や会社で開いて、後ろに誰か居たら慌てるレベル。
少し反省しています。
服が無いのが問題なんですよね。
正直3Dキャラクターモデルの世界では服と髪を用意することがそれなりに困難です。

というか趣味の世界になりますよね。
ドレスとかの3Dモデルを精密に作っている方もいらっしゃって、凄いなと。
髪はね、今はなんとかなるんですよ。
凄いツールがあります。
問題は服です。
私も服に関しては毎回四苦八苦して作っています。
何か良いソリューションがあれば教えて頂きたいものです。
とは言え、前回作ったキャラクター、裸のままでは可哀想です。
せめてシャツを着せることにしましょう。

以下のYoutube動画を参考にさせていただきます。

【blender】平面メッシュから簡単に服を作る方法

この動画に従い、袖なしTシャツを作成します。
説明用に素材を作成しました。
ここに置いておきます。

tank_top_material.zip

このZIPファイルを解凍するとtank_top_material.objという3D waveファイルになります。
これを前回作成したgirl11_after_finalize.blendのプロジェクトにインポートします。

以下のように二つの板がキャラクターを挟み込むよう現れます。

図のような位置になるように位置合わせしてください。
位置合わせはオブジェクトモードであることを確認し、オブジェクトを右クリックで選択してから以下の操作を行います。
いずれも 確定は[enter]キー、キャンセルは[esc]キー です。

上下に移動させる場合:
・テンキーの[1]キーを押して正面を向かせる
・[g]キーを押してから[z]キーを押し、上下キーで調節する。

左右に移動させる場合:
・テンキーの[1]キーを押して正面を向かせる
・[g]キーを押してから[x]キーを押し、左右キーで調節する。

前後に移動させる場合:
・テンキーの[3]キーを押して横を向かせる
・[g]キーを押してから[y]キーを押し、左右キーで調節する。

上下に拡大させる場合:
・テンキーの[1]キーを押して正面を向かせる
・カーソルをオブジェクトに重ねる
・[s]キーを押してから[z]キーを押し、上下キーで調節する。
 ・拡大後は位置がずれるので上下に位置合わせする

左右に拡大させる場合:
・テンキーの[1]キーを押して正面を向かせる
・カーソルをオブジェクトの左右どちらかに振る
・[s]キーを押してから[x]キーを押し、左右キーで調節する

調節はだいたいで問題ありません。
というか、正解はありません。

できましたら服に頂点グループを割り当てます。
3D waveデータには頂点グループが含まれていないので設定が必要です。
オブジェクトモードになっているのを確認してから服オブジェクトを右クリックで選択します。
モードを切り替えて編集モードにします。
服の上にカーソルを当て、何度か[a]キーを押下して全ての頂点が黄色く選択されている状態にします。
頂点グループの設定は プロパティウインドウのオブジェクトデータ(逆三角形のマーク)から行います。
頂点グループに何も無い状態だと思われますので、右側にある[+]ボタンを左クリックし、[Group]が現れるのを確認してから[割り当て]ボタンを左クリックし確定させます。

次にキャラクター側にコリジョンモデファイアーを付与します。
オブジェクトモードであることを確認し、キャラクターの顔あたりを右クリックするとキャラクターがオレンジに縁どられます。
プロパティウインドウのオブジェクトモデファイアー(スパナのマーク)を選択、追加からコリジョンを選択します。

コリジョンを追加できたらコリジョンの設定を行います。
コリジョンの設定はプロパティウインドウの物理演算の項目(ボールの跳ねているマーク)で行います。
[ソフトボディとクロス]の[外側:]と[内側:]をそれぞれ最小値の0.0001にします。

次に服にクロスモデファイアーを付与します。
オブジェクトモードであることを確認し、Tank_Top_Planeオブジェクトを右クリックし選択します。
プロパティウインドウのオブジェクトモデファイアーを選択、追加からクロスを選択します。

クロスの設定は プロパティウインドウの物理演算の項目で行います。
設定項目が比較的多いので注意してください。
以下は一例です。
意図に合わせて色々調整できます。

[クロス]
・[プリセット]をCottonにするとパラメータが色々変わります。
・[ステップ数:]を11くらいにします。
 大きな値にすると品質があがりますが時間がかかります。
・[マテリアル:重さ:]を5以上にします。
 軽いとめくれ上がり、重いとだらんと垂れ下がります。
・[減衰:速度:]0.1にします。
 遅い方が品質が上がりますが時間がかかります。
 ある程度遅くないと変形に失敗します。
[クロスコリジョン]
・[品質:]2にします。
・[距離:]0.010くらい?
 服と体の距離ですが、広いと角度によっては体が見えてしまいます。
 逆に狭いとポーズ次第で地肌が見えてしまいます。
[クロス縫合スプリング]
・[縫合の力:]50.0000くらい。
 適当に増減させてください。
 今回のように二枚のplaneを縫合するだけならば強さだけに着目すれば問題ありません。
 複数のパーツを縫合する場合は縫合される順番等に気を付ける必要があるため、強さと距離を検討する必要が出てきます。
・[収縮:]頂点グループを選択します。
 これが設定されていないと縫合されません。

設定は以下を参考にしてください。

ここまで設定し、[アニメーションの再生]ボタン(一番下にある右向きの三角形のアイコン)を左クリックすると物理演算シミュレーションが実行されます。
一度実行すれば、タイムラインのバーで任意の変化点を選択できます。

好みの変化点を選択し、クロスモディファイア―を適用します。
プロパティウインドウのオブジェクトモデファイアーを選択、[Cloth]にある[適用]ボタンを押します。

これで一応袖なしTシャツ( Tank_Top_Plane )の完成です。
girl1_bodyにTank_Top_Planeを着せます。
オブジェクトモードであることを確認し、girl1_bodyを右クリックで選択します。
ツールシェルフに[ManuelBastioniLab]のタグが現れるので選択します。
[PROXY FITTING]で[キャラクター:]にgirl1_bodyを[プロキシ:]にTank_Top_Planeを設定します。
後は[Fit Proxy]ボタンを押せばフィッティングされるはずなのですが、このままではうまくいきません。
Tシャツが小さく股間に埋まってしまいます。

理由はこのキャラクターがdefaultのキャラクターを縮小したものであるからだと思われます。
Proxyシステムはdefaultの体形に合わせた服や髪を、ターゲットのキャラクターに合わせて変形させるものです。
もとから変形後のキャラクターに合った服や髪は、更に変形が強調されてしまいます。
つまり小さい服は更に小さくなってしまいます。
であれば、defaultのキャラクターで服を作成すれば良いのか?
これはある意味正しいのですが、元のキャラクターは八頭身小顔で手足が長く、しかもかなりグラマーなのです。
それに合わせて服を作ると胸が余る等の弊害が出てきます。

対策方針としては以下。
①目的のキャラクター向けに服や髪を作成
②defaultのキャラクターに合わせて拡大
③Proxyシステムを適用

先ず[Remove Fitting]でProxyの適用を解除します。
そしてTank_Top_Planeを上方向、左右方向、前後方向にそれぞれ拡大します。
方向ごとに微調整をかける方がよいでしょう。
もともとこのキャラクターにフィットするように作った服なので、[影響:]は0にして構いません。
下に例示するくらい拡大するとフィットします。

以下のように綺麗にフィットしました。

ポーズを変えます。
先ずオブジェクトモードであることを確認し、girl1_bodeyを右クリックで選択するとモードをポーズモードに変えることができるようになります。
ポーズモードに切り替えた後、ツールシェルフの[ManuelBastioniLAB]の[POSE AND ANIMATION]でポーズを選択します。
[Female]のポーズを任意に選んでください。

袖なしTシャツが綺麗にキャラクターに追随しています。
どのポーズでも袖なしTシャツ自体には破綻は見られません。
Proxyシステム、素晴らしいですよね。

今回は簡単な形状のPlane二枚から袖なしTシャツを作っていますが、袖を付ければ普通のTシャツに、裾を伸ばせばワンピースになります。
襟や前袷を別パーツで付けることもそんなに難しい話ではないので、最低限のメッシュ操作ができれば色々な服を作ることが可能です。

この服の作り方の良い点は、一度作ると変形前の素材は別のキャラクター用に流用できることです。

ポーズと表情を付けてみました。

どうでしょう?
MB-Labの可能性みたいなものを感じていただけたでしょうか?
思ったより簡単にアニメ調のキャラクターを生成、編集できるのです。
これは十分趣味にできると思いませんか?

今回はこれで終わりです。
絵は完成していませんが、時間があれば[その3]を書きたいと思います。


鎮神頭


黒灰色(こっかいしょく)の魔女と時の魔女』、『第五章第三話(四)鎮神頭』更新します。
チェスに関してあまりにも強くなりすぎてしまったアウラ。
『教師エリフ』は次の手としてアウラに囲碁を教えます。
初めての囲碁、予想通りアウラは只者ではなかった、というお話です。
読んで下さいね。
絵は『乳母サリー』の中の人であるサリーのイメージです。

前回に引き続き、今回も対局もの。
囲碁対局です。
使わせていただいた棋譜は古典囲碁の名局、『鎮神頭』です。
この譜面は幼き頃、父に教わりました。
双方の強さがはっきり出ていて、白優勢で進み、黒の大逆転の棋譜として当初から採用を考えていたものです。
小説のマテリアルとしては短手順なのが良いですね。
凄い戦いなのですが、以下のサイトで実譜を並べることができます。

平安時代 顧師言 対 伴小勝雄

白番は日本国王子、黒番は顧師言(こしげん)と言われています。
日本国王子が誰なのかに関しては伴少勝雄(とものおかつお) 説と高岳親王説があるそうです。

とはいえお二方とも時代が合いません。
対局が行われたのは旧唐書宣宗本記では 大中二年(西暦八百四十八年)、杜陽雑編巻下では大中(西暦八百四十七年~ 八百六十年)の中頃とされています。
高岳親王は弘法大師空海の十大弟子の一人で偉いお坊さん。
入京したのは貞観六年(西暦八百六十四年)、六十代中盤になってからのこと。
長安に渡ったのは求法の為で、碁を打ちに行ったわけではなく、この方を件の日本国王子と考えるには色々無理があります。
ここでは伴少勝雄説を採用します。

伴少勝雄(小勝雄)は伴雄堅魚ともいい、平安時代の貴族です。
延暦二十三年(西暦八百四年)の第十八次遣唐使船に碁師として随伴しました。
当時十九歳という若さ。
一回でも負けると次の対局機会が失われるという条件の下で唐の碁師相手に勝ち進んだそうです。
少勝雄は遣唐使の碁師に選出されるくらいですから、当時日本の第一人者であったのだろうと思われます。
唐側から見ると朝貢国である日本から来た碁打ちが予想外に強く、様子見に出した打ち手のみならず、高位者までもが次々に負かされていくという嫌な展開。
まさに道場破りにあった名門道場という感じですかね。
唐側としてもこれ以上負けられないというところまで追い詰められました。
そこで出てきたのが顧師言というわけです。

小説内では四隅の星は手順で指されたことにしていますが、実譜では予め置かれたものです。
古代の囲碁ではこのように四隅の星にタスキ掛けで置石してから指すルールであったようです。
黒(先手)を持っているのが 顧師言 であることにご注意ください。
古代においてはコミも無く、 本局のように上位者が黒を持つこともあったようです。

対局は序盤から互いに活殺相まみえる激しい戦いになります。
白優勢で進んでいくのですが、ある手筋に流れてゆきます。
戦いの流れはこのサイトで確かめてください。
45手めが鎮神頭。
一手で両シチョウを防ぐ伝説の鬼手です。

少勝雄は顧師言に敗れました。
少勝雄は鴻臚卿(使節応対に当たる官署の長官)に顧師言の唐での序列を尋ねます。
鴻臚卿は第三位である旨を告げます。
少勝雄は「小国の一位でも大国の三位に敵わないのか」と嘆いたといいます。
そして第二位者、第一位者との対局を望みました。
願いはやんわりと拒絶されます。
それどころかその後、少勝雄に対局の機会は与えらませんでした。
それもそのはず、顧師言は棋待詔(待詔のうち囲碁をもって仕える役職)、唐の第一位であり更なる上位者など居なかったのです。
最終兵器を投入して辛うじて面目を保ったというのに、これ以上少勝雄に居座られてはたまったものではありません。
当然、次の対局などあろうはずもなく。
本局を最後に少勝雄は日本に帰ることとなります。
物語として面白いと思いませんか?

さて、前回に続いて二話連続で対局物の展開。
興味が無い方にはつまらないですよね?
読者様が離れていきそうで怖いですが、できればお付き合いいただけますと嬉しいです。
実情をバラすと、この二話にかけた時間は他の話の比ではありません。
一手一手、どのような想いをのせて打たれたのか、理解しようと努力したのです。
しかし悲しいかな、私はずぶの素人、名人たちの手を理解できるはずもなく。

でもね、今はツールがあるんですよ。

Lizzie – Leela Zero Interface

このソフトは囲碁の局面を分析し、指すべき手を提案してくれます。
左側に折れ線グラフで優劣が示されます。
これを使えば棋譜の検討ができるわけです。
『鎮神頭』の棋譜ではどうも白38手め(小説中では42手目)G12が敗着手であるようです。
推奨手はF9、展開は以下。

ただしこの展開では歴史に残る名局にはならないのでしょうね。
勝ちを確信した瞬間、返しの大技炸裂、 相手に絶望を与え、精神ダメージ百倍。
あくまでも両シチョウを同時に解決して白石を叩き潰す鬼手だから伝説なのです。

……ええ、分かっていますとも。
何から何まで他人のふんどし。
でもね、こんな凄いツールが無料で使えるなんて、感謝しかありません。
凄い時代になったものですね。
『小説になろう』に囲碁モノが増えるといいな、なんて期待しています。

余談ですが前述の第十八次遣唐使船には最澄や空海、橘逸勢、霊仙といったそうそうたるメンツが乗っているんですね。
で、四隻の遣唐使船で出発していきなり遭難。
うち実際に唐に到着できたのは一番船と二番船の二隻のみ、三番船は難破、四番船は行方不明。
最澄たちの乗る二番船は比較的順調に目的地である明州に着くのですが、一番船は 福州に漂着し海賊の疑義をかけられ抑留されます。
大使、藤原葛野麻呂が嘆願書を書いたものの悪筆悪文でますます嫌疑は深まるばかり。
そこで同船していた無名の留学僧、空海がサラサラサラッと嘆願書を代筆。
あまりにも立派すぎる嘆願書、これは海賊であるはずがないと無事放免されたそうな。
そりゃそうですよね、三筆に数えられる空海の書ですもん。
国宝にするレベルですやん。
この時、空海は個人での長安入京留学の嘆願書もちゃっかり提出し認められることに。
期間は二十年。
まことに図々しい。
実際には唐の滞在は二年であったわけですけれど、その二年の濃ゆいこと濃ゆいこと。
なんでこうなる?という奇天烈さ。

ああいけない、好きな歴史の話だと脱線してしまいます。
空海はやっぱり色んな意味で只者でないですね。


挿絵画家になろう(その1)


このブログを始めた動機の一つはMB-LABの素晴らしさを世に啓蒙することです。
MB-LABは3Dアニメ調の絵作りにおいて、非常に良いものです。
MB-LABはそれ自体を一つの趣味として良いほどの素晴らしいツールなのですがあまり流行っていません。
そんなのはおかしいと思うのです。

私は自作小説を飾る萌え絵の挿絵が喉から手が出るほど欲しいのです。
MB-LABはそんな私に光明を与えてくれました。
私自身の習熟度、センス、掛けられる時間等、様々な制約により然程良い作品は生み出せていません。
しかし私ではなくもっとセンスや熱意があるかたたちが使えば、凄い作品が生み出されると思うのです。
これは啓蒙する必要があるのです。
MB-LABは開発資金の問題を抱えていらっしゃるとのことです。
もっともっとネット上のクリエーターの皆さんが利用すれば、彼らの必要不可欠なツールとなれば、資金や開発者も集まり、開発が加速するでしょう。
もしくは応用方法の情報、髪や衣服のデーターが増えれば、私としてはぜひ使わせて欲しいのに……、そういうさもしい思いも実はあります。

MB-LABですが、そもそもアニメ調の絵作りに使っている例があまり見受けられません。
何故なのでしょう?
理由はMB-LABについて説明する記事を書こうとすれば思い知ることになります。
とにかく歴史、動作環境から始まって日本語ドキュメントが少ないこと等、すべて説明するのが大変なのです。

MB-LABは3Dデータ編集ツールであるBlenderのアドオンとして機能します。
しかしBlender自体がちょっと試してみようというには敷居が高いでしょう。
折りしもBlenderは現在、2.79系から2.8x系に移行したタイミングです。
Blenderの2.79系と2.8x系の操作系は全く別物で、Blenderのユーザーでも2.79系は使えるけれど2.8x系はチョット、という人は多いでしょう。
私などもそのクチです。
今からBlenderを学ぶのならば2.80を選択するのが良いのかもしれません。
しかしながら2.80系の記事がネット上でもあまり潤沢には無いことがネックになるでしょう。

MB-LABは2.79系でも2.80系でも動作しますが、これまたややこしいことに対応バージョンが異なります。
Blender 2.79系に対応するのはManuelBastioniLAB 1.6.1aというバージョンです。
2.79系への対応は開発が停止して久しいです。
Blender 2.8x系に対応するのはMB-Lab 1.7.5というバージョンです。
Blender 2.8x系への対応は開発は続けられています。

私が説明できるのはBlender 2.79とManuelBastioniLAB 1.6.1aの組み合わせだけです。
以下のリンクの下のほうにあるmanuelbastionilab_161a.zipというファイルがアドオンファイルです。

animate1978/MB-Lab

※いつまであるか分からないので、興味がある方は早めにダウンロードしておくことをお勧めします。

この記事はBlenderやMB-LABについて熟知している必要はありませんが、Blender 2.79b をインストールしてManuelBastioniLAB 1.6.1a をプラグインとして動作させることができるとより楽しめるかと。

では実際にアニメ調の少女キャラを生成していきましょう。
ターゲットは私の自作小説『黒灰色(こっかいしょく)の魔女と時の魔女』の登場人物の一人であるマリアの九歳時とします。
マリアは信望者たちを束ね、空賊、マリアカンパニーの首領となる人物です。
幼きころに両親を失いますが、弟ヨシュアと共に赤ん坊のリリィを育て上げるという豪傑です。

Blender 2.79bと ManuelBastioniLAB 1.6.1a のインストールはできましたでしょうか?
Blenderを再起動すると以下のような画面になると思います。

[esc]キーを押すと次のような画面になります。

Cubeが選択されているので[delete]キー押下。
「削除 x」とでるので左クリックしてCubeを削除する。
CameraとLampだけが残ります。

左側のツールシェルフのタグに[ManuelBastitioniLAB]タグがあるのでそれを選択します。
※ツールシェルフが無い場合は編集画面にカーソルを置き、[t]キーを押下すると現れます。
[選択]を押すと幾つかの項目が現れます。

ここで選択するのはベースとなるキャラクターです。
写実的にするのかアニメ調にするのか、男か女か、人種等を選択できます。
今回の趣旨はアニメ調の女の子なので[Anime female(F_AN02)]を選択します。
[Init character]ボタンを押すとキャラクターといくつかのLampが生成されます。

全体的に小さく、しかも左を向いて生成されるので以下の手順で表示を調整します。

・テンキーの[1]キーを押して正面を向かせる。
・編集画面上で単にホイールを回転させると拡大縮小となるので適当な大きさにする。
・[shift]キーを押しながらホイールを回転させると垂直への移動となるので適当な位置にする。
・[ctrl]キーを押しながらホイールを回転させると水平への移動となるので適当な位置にする。

これらの操作を組み合わせて位置合わせをします。
これが[Anime female(F_AN02)]のdefaultのキャラクターです。

これをベースに好みに合わせて改造してゆくことになります。
先ずRest poseの変更です。
ツールシェルフの[ManuelBastitioniLAB]タグにある [Rest pose]メニューで変更できます。
defaultではa-poseです。
UNITYやCLIP STUDIO PAINT 3D等、他のツールへのエクスポート、服の作りやすさから私はt-poseに変更しています。

今回はマリアの少女時代がターゲットです。
defaultのキャラクターは八頭身で手足が非常に長い造形です。
アニメ調では作風にもよりますが、小さな体に大きな頭が載っている感じにしたいです。

キャラクターの大きさを管理することは重要です。
複数のキャラクターを作ったとき、調整が楽になります。
blenderでキャラクターの大きさを知るには以下の操作を行います。

・対象オブジェクト(今回は生成したキャラクター)を右クリックで選択する。
・編集画面の上にカーソルを置き、[n]キーを押してプロパティシェルフを表示させる。
・[寸法]を見る

身長はZ、手の先から反対の先までがX、前後の幅がYで表示されます。
defaultで生成されたキャラクターの身長は1.7325mであることが判りました。

体形や表情の変更はツールシェルフの[ManuelBastitioniLAB]タグにある[Body,face and measure parameters]で変更します。
開いたメニューにある[PARAMETERS]の[Mophing categories:]から変更したい部位を選択します。

[本文]は[Body]に、[ヘッド]は[Head]に読み替えてください。
十代後半の女性キャラクターの基本体形として、提案パラメータは以下です。

・[Arms:Arms_ForearmLength]:0に
・[Arms:Arms_UpperarmLength]:0に
・[Legs:Legs_LowerlegsLength]:0に
・[Legs:Legs_UpperlegsLength]:0に
・[Pelvis:Pelvis_Length]:0に
・[Torso:Torso_Length]:0に
・[Hands:Hands_FingersLength]:0に
・[Hands:PalmLength]:0に

年齢が上がれば胴体と腕、足を長くして、頭を小さくしていきます。
反対に年齢を下げるには頭を大きくしていく方針です。

今回は九歳の少女であるので以下。

・[Torso:Torso_BreastMass]:0に
・[Torso:Torso_BreastNipple]:0に
・[Torso:Torso_BreastPosZ]:0に
・[Torso:Torso_Mass]:0.3に
・[Torso:Torso_SizeX]:0.3に
・[Torso:Torso_SizeY]:0.3に
・[Pelvis:Pelvis_Shapely]:0に
・[Pelvis:Pelvis_Size]:0.2に
・[Pelvis:Pelvis_SizeX]:0.2に
・[Pelvis:Pelvis_SizeY]:0.2に
・[Pelvis:Pelvis_StomachVolume]:0.2に
・[Legs:Legs_LowerlegSize]:0.4に
・[Legs:Legs_LowerlegMass]:0.4に
・[Legs:Legs_UpperlegSize]:0.3に
・[Legs:Legs_UpperlegMass]:0.3に
・[Arms:Arms_ForearmMass]:0.3に
・[Arms:Arms_ForearmSize]:0.4に
・[Arms:Arms_UpperarmMass]:0.3に
・[Arms:Arms_UpperarmSize]:0.4に
・[Head:Head_Size]:0.9に
・[Neck:Neck_Length]:0.3に

少女らしく未発達、しかし健康的に見えるように肉付きを残す感じで調整します。
足はもう少し細くしたいのですが、各部のレングスを最小にしてしまっているので調整の余地があまりありません。
やってみれば分かりますがバランスが崩れるのす。
最終的には服を着せることになるので体に関しては然程こだわる必要はありません。

ここまで設定して身長を1.40m程度に合わせます。

・[Body:Body_Size]を0.31に

身長が1.4031mになりました。
ちなみに子供キャラ、小柄キャラである場合に[Body:Body_Size]を削って調節するのはありなのですが逆の場合は注意が必要です。
[Body:Body_Size]は全体の拡大・縮小なので身長を高くするためにこのパラメータを増やすと頭の大きなキャラになってしまいます。
あくまでも身長を伸ばすには、胴体や手足の長さを長くして調節するほうが良いです。

次は表情の設定です。

・[Head:Head_Round]を0.7に

これは目を大きくするためには顔の横幅を広げる必要があるからです。
キャラの描き分けにも用いますからあまり極端な設定はしない方が吉。

・[Cheaks:Cheeks_SideCrease]を0.2に

ほうれい線付近の顔表面をなだらかにします。
[ Cheeks_SideCrease] は加齢表現にも使えそうです。

・[Cheaks:Cheeks_Mass]を0.6に
・[Cheaks:Cheeks_Tone]を0.3に

この二つのパラメータは必ずセットで設定します。
[Cheaks:Cheeks_Mass]は頬の膨らみ具合、削げ具合を調節します。
九歳少女なので1側にふって膨らみをもたせます。
[Cheaks:Cheeks_Tone]は強調具合です。

・[Chin:Chin_Prominence]を0.7に

意図は横顔の鼻の頂点、唇先、顎先の位置を整え、正面下から見た場合に破綻しないようにすること。
ただ、表情の印象が大きく変わるので好みが分かれます。
唇先がへこんでいるほうが美人系。
逆に振ると可愛い系になりますが顔を下側から見た場合に顎の無いキャラになります。

・[Chin:Chin_SizeX]を0.7に

顎の丸みの設定です。
[Cheaks]の設定とのバランスになります。
頬を丸めたのならば顎もある程度丸くしたほうが良いでしょう。

・[Eyes:Eyes_PosZ]を0に

目の高さの調整です。
子供らしさを出すには低くします。
逆に大人感を出すには高くします。

・[Eyes:Eyes_InnerPosX]を1に

目の内側の位置ですが目を大きくする一環です。

・[Eyes:Eyes_OuterPosX]を0.7に

目の外側の位置です。
目を大きくする一環ですがあまり大きくするとメッシュが乱れます。
前述の[Head:Head_Round]のパラメータと関連します。

・[Eyes:Eyes_InnerPosZ]を0.9に
・[Eyes:Eyes_OuterPosZ]を0.2に
・[Eyelids:Eyelids_Angle]を0.4に

いわゆるタレ目、ツリ目の調整です。
ここではややタレ目に調整します。

・[Eyes:Eyes_SizeZ]を0.3に
・[Eyelids:MiddlePosZ]を0に

目のパッチリ具合です。
子供を表現するには大きくする方が良いのですが、マリアの性格からやや細目に設定します。

・[Eyelids:InnerPosZ]を0に
・[Eyelids:LowerCurve]を1に

このへんは好みで。

・[Eyelashes:Eyelashes_Length]を0に

睫毛の長さです。
睫毛をどれくらいの長さにするかですが、子供の場合はそれほど睫毛は重要ではないので短くします。
なお最終的には下の睫毛は表示させない予定です。

・[Eyebrows:Eyebrows_Angle]を0.6に

眉毛の角度です。
意思の強さを示すためにやや吊り上げます。

・[Eyebrows:Eyebrows_PosZ]を0.4に

眉毛を目に近付けるか離すか。

・[Eyebrows:Eyebrows_Size01]を0.2に

眉毛を細くする。

・[Mouth:MouthPosZ]を0.7に

口角の上げ下げ。
基本的に余裕のありそうな笑顔とします。

これでキャラクター設定を完了します。
3Dビューのシェーディングをレンダーにすると簡易的にレンダリングされます。

オブジェクトを選択した状態でテンキーの4か6を押すとオブジェクトを中心に回転させることができます。

いかがでしょうか?
丸坊主で尚可愛らしいとは異常です。
defaultの造形が優れているので、水平方向ならばどの方向から見ても破綻はないように見えます。

この後、Finalizeを行うことによりこのキャラクターを編集できるようになります。
その前に保存しましょう。

ツールシェルフの[ManuelBastitioniLAB]の[File tools]メニューを開きます。
[Include propotions]をチェックして[Export charcter]とします。
設定は.jsonファイルとしてセーブします。

尚、ここでgirl1_before_finalize.blendとして保存することをお勧めします。

ツールシェルフには[Skin editor]が有るのですが私は重要視していません。
Finalize後でも設定変更が可能であるからです。

Finalizeはツールシェルフの[ManuelBastitioniLAB]の[Finalize tools]メニューを開きます。

接頭語に[girl1]等のユニークな名前を付けます。
[Finalize with textures and backup]ボタンを押します。
ここで保存しているのはSkin画像だけです。

Finalize後に改めてgirl1_after_finalize.blendとしてセーブします。
Fnalize後はツールシェルフの[ManuelBastitioniLAB]のメニューが変わります。
後は生成したキャラクターを自由に編集できます。

ここで暫定で付属の髪を付けておきましょう。
ツールシェルフの[ManuelBastitioniLAB]の[ASSETS LIBRARY]のメニューを開きます。
[Assets model:]で[hair01_anime_female]を選択すると髪の毛が出てきます。

ツールシェルフの[ManuelBastitioniLAB]の[PROXY FITTING]のメニューを開きます。
キャラクターで作成したキャラクター[girl1_body]を選択、プロキシに[hair01_anime_female]を選択し、[Fit Proxy]ボタンを押します。

髪の毛が自動装着されました。

ただしここまで便利なのは付属の髪だけです。
PROXY FITTINGは凄い機能なのですが、自作の服や髪をセットする場合は試行錯誤が必要です。

次にポーズを変えます。
キャラクターの足元にある三角錐を右クリックで選択します。
モードが[ポーズモード]に変わります。
もし変わらず、[オブジェクトモード]のままならば、[ポーズモード]に変えてください。

[ポーズモード]の時、ツールシェルフの[ManuelBastitioniLAB]の[POSE AND ANIMATION]メニューで[Female pose]を選択できるようになります。

選択するとポーズを変更することができます。

色々なポーズが有るので試してください。
体形を大きく変えてしまっているので至る所で破綻がおきます。
ポーズは手動で修正することができます。

如何でしょうか?
ここまで駆け足で説明してきましたが、凄いツールであることをお分かり頂けたでしょうか?
髪と服をどのように用意するのかが課題となりますが、このままでもデッサン人形の代わりになります。
また、髪や服を自分で描けるのならばポーズを付けてレンダリングすれば、素材として即戦力です。

今回はこれで終わりです。
絵は完成していませんが、時間があれば[その2]を書きたいと思います。


King’s Gambit


黒灰色(こっかいしょく)の魔女と時の魔女 』、『第五章第三話(三)キングズ・ギャンビット』更新します。
恒星船の中、一人で居るアウラを憐れんで、『教師エリフ』はアウラにチェスを教えます。
初めてのチェス、白番(先手)でアウラは教わりもしないキングズ・ギャンビット・オープニングを指す、というお話です。
読んで下さいね。

絵は『乳母サリー』の中の人であるサリーのイメージです。
この人も『教師エリフ』の中の人、エリフ同様に年齢不詳です。

まあ、あんまり小説展開上の話はできないのですが、使わせていただいている棋譜は実際の対局のものです。

Paul Morphy vs Eugene Rousseau (1849) Reap What You Rousseau

本局にはキングズ・ギャンビット・オープニングで白番の快勝のものを探していたら巡り合いました。
白番は1800年代後半の有名なチェスプレイヤー、ポール・モーフィーです。
黒番はユージン・ルソーという人ですが、私は良く知りません。
年代が違うので、有名なサクソフォン奏者とは別人です。

本局はポール・モーフィーが十二歳の頃の対局らしいです。
キングス・ギャンビット、つまりポーンのサクリファス(タダ捨て)から始まって、ナイト、ポーンと次々にサクリファスしてゆく派手な展開です。
上のリンクで譜面が再現されていますので是非追ってください。
小説の中では黒番が途中でリザイン(降参)しますが、実譜ではキッチリと詰んでいます。

小説を書くものとしては対局系ゲームの描写に興味があります。
『ヒカルの碁』や『ハチワンダイバー』など、ストーリーもモチロン面白いのですが作中の対局がリアルで、譜面を検証することが楽しみの一つです。
作者自身の棋力も相当高いのでしょうね。
更にはプロの棋士の監修が付かれていてリアリティを倍加させているようです。
凄いな、と思う反面、クオリティを保つことはアマチュア小説家では難しいのかな? という思いもあります。

とはいえ、昨今のAIの発達は恐ろしいものがあります。
将棋などの解説を見ていても、AIの推奨する指し手に至れるか否かが勝敗を分けるとか。
ツールを駆使すればかなりリアリティのある対局を記述することができるかもしれません。

今回チェスの対局チェックにSCIDというツールを使わせて頂きました。

http://scid.sourceforge.net/

このツールは一手一手指し進めるごとに検討が行われ、どこで手を悪くしているのか等の検証ができます。
SCIDの高評価の手を指し続ければ、アマチュア相手ならそうそう負けないのでしょうか?

キングズ・ギャンビットとは序盤でポーンを犠牲にして戦略的主導権を得る戦法です。
チェスで言うサクリファス、将棋での捨て駒、これらは自陣の王を詰まされる前に相手王を詰ますという明確な目的のために、ときとして必要な手段となります。
ただしゲームではなく実際の戦いにおいて、勝利のためにどこまでサクリファスできるか。
そもそも勝利とはなんぞや?
そういったことを考えつつ。


Diamond


ちょっとくだらない理由で元町のジュエリー店に行ってきました。
ふと見るとカラット級の大粒ダイヤがあります。
見つめていると魂を吸い込まれていきそうになります。
値段を見ます。
ふむ、買えなくもない。
未だボーナスは手付かずだったな……。
これ、いただきます……。
はっ! 私は何を考えているのだ?
ダイヤに誘惑されている自分を必死に抑えます。

これほどまでにダイヤモンドは私の心を魅了します。
ラウンド・ブリリアントカット の大粒のダイヤ、ずっとずっと眺めていたい。
車のディーラーもそうなのですが、ジュエリー店には行ってはいけませんね。
欲しくなってしまうのです。
気が付いたら買う前提で店員と話をしていたりします。
危険極まりありません。

我に返ってジュエリー店を後にしたのですがダイヤモンドが脳裏から離れません。
欲しい、欲しい、手元に置いて眺めていたい。
しかし買うわけにはいかないのです。
子供の塾代もあるし、家のローンだってあるのです。
ということで3Dモデルで我慢することにしました。
どうせならば作りましょう。

ラウンドブリリアントカットは寸法比や角度が精密に定義されているものだとばかり思っていました。
調べてみると、比較的ゆるいルールしかないんですね。
まあ想像するに相手は自然から発掘される石ころなのだから、取れた原石に合わせてカットする自由度を残す必要があるのでしょうね。
とは言え、幾何学図形なのでだいたいの形はスクリプト生成できます。
以下ラウンドブリリアントカットの3Dオブジェクトデータを生成するpythonスクリプトです。

# -*- coding: utf-8 -*-
from mpl_toolkits.mplot3d import axes3d
import math
import matplotlib.pyplot as plt
import numpy as np

def rot_z(r, P):
    # z軸で回転行列を計算する
    Rz = np.array([[ np.cos(r),  np.sin(r),    0.0       ],
                   [-np.sin(r),  np.cos(r),    0.0       ],
                   [ 0.0,        0.0,          1.0       ]])
    R = Rz.dot(P.T)
    return R.T

def rot_y(r, P):
    # y軸で回転行列を計算する

    Ry = np.array([[ np.cos(py),  0.0,        -np.sin(py)],
                   [ 0.0,         1.0,         0.0       ],
                   [ np.sin(py),  0.0,         np.cos(py)]])
    R = Rz.dot(P.T)
    return R.T

def rot_x(r, P):
    # x軸で回転行列を計算する
    Rx = np.array([[ 1.0,         0.0,         0.0       ],
                   [ 0.0,         np.cos(px), -np.sin(px)],
                   [ 0.0,         np.sin(px),  np.cos(px)]])
    R = Rz.dot(P.T)
    return R.T

def mid_point(P , Q, par):
    pp = par / 100.0
    pq = (100.0 - par) / 100.0
    PP = np.array([pp, pp, pp])
    PQ = np.array([pq, pq, pq])
    P = P * PP
    Q = Q * PQ
    R = P + Q
    return R

def push_index(R):
    global x
    global y
    global z
    x.append(R[0])
    y.append(R[1])
    z.append(R[2])

def print_v(s, P):
    global v_num
    v_num += 1
    sr = s + ' '
    sr += ' {:.6f}'.format(P[0])
    sr += ' {:.6f}'.format(P[1])
    sr += ' {:.6f}'.format(P[2])
    sr += '\n'
    return(sr)

#定数
table_r = 0.55 / 2.0
girdle_r = 1.0 / 2.0
crown_height = 0.162
pavilion_height = 0.431
girdle_height = 0.025
star_ratio = 60.0
pavilion_ratio = 40.0
culet_ratio = 2.5
center = 50.0
t = math.tan(math.pi/8.0)

x = []
y = []
z = []
v_num = 0

# table(auxiliary point)
P_TA = np.array([])
P_TA = np.empty((0,3), float)
S = np.array([table_r, t * table_r, crown_height])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
    P_TA = np.append(P_TA, np.array([R]), axis=0)

# table
P_TB = np.array([])
P_TB = np.empty((0,3), float)
S = mid_point(P_TA[0] , P_TA[1], 50.0)
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
    push_index(R)
    P_TB = np.append(P_TB, np.array([R]), axis=0)

# upper girdle(auxiliary point)
P_UGA = np.array([])
P_UGA = np.empty((0,3), float)
S = np.array([girdle_r, t * girdle_r, 0])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
#    push_index(R)
    P_UGA = np.append(P_UGA, np.array([R]), axis=0)

# upper girdle
P_UG = np.array([])
P_UG = np.empty((0,3), float)
S = mid_point(P_UGA[0] , P_UGA[1], center)
for num in range(0, 16):
    R = rot_z(math.pi / 8 * num, S)
    push_index(R)
    P_UG = np.append(P_UG, np.array([R]), axis=0)

# star facet
P_SF = np.array([])
P_SF = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_TA[num] , P_UGA[num], star_ratio)
    push_index(R)
    P_SF = np.append(P_SF, np.array([R]), axis=0)

# culet
P_CU = np.array([0, 0, -pavilion_height - girdle_height])

# lower girdle(auxiliary point)
P_LGA = np.array([])
P_LGA = np.empty((0,3), float)
S = np.array([girdle_r, t * girdle_r, -girdle_height])
for num in range(0, 8):
    R = rot_z(math.pi / 4 * num, S)
#    push_index(R)
    P_LGA = np.append(P_LGA, np.array([R]), axis=0)

# lower girdle
P_LG = np.array([])
P_LG = np.empty((0,3), float)
S = mid_point(P_LGA[0] , P_LGA[1], center)
for num in range(0, 16):
    R = rot_z(math.pi / 8 * num, S)
    push_index(R)
    P_LG = np.append(P_LG, np.array([R]), axis=0)

# pavilion facet
P_PF = np.array([])
P_PF = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_LGA[num] , P_CU, pavilion_ratio)
    push_index(R)
    P_PF = np.append(P_PF, np.array([R]), axis=0)

# culet table
P_QT = np.array([])
P_QT = np.empty((0,3), float)
for num in range(0, 8):
    R = mid_point(P_LGA[num] , P_CU, culet_ratio)
    push_index(R)
    P_QT = np.append(P_QT, np.array([R]), axis=0)

#make obj file
path_w = './test_diamond.obj'
s = 'New file'
with open(path_w, mode='w') as f:
    f.write('g diamond\n')
    for num in range(0, 8):
        f.write(print_v('v', P_TB[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_SF[num]))
    for num in range(0, 16):
        f.write(print_v('v', P_UG[num]))
    for num in range(0, 16):
        f.write(print_v('v', P_LG[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_PF[num]))
    for num in range(0, 8):
        f.write(print_v('v', P_QT[num]))

    t_base = 0
    s_base = 8
    ug_base = 16
    lg_base = 32
    p_base = 48
    c_base = 56

    #Table Facet: 1
    f.write('#Table Facet: num = 1\n')
    f.write('f 1 2 3 4 5 6 7 8\n')

    #Bezel Facet: 8
    f.write('#Bezel Facet: num = 8\n')
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 1, ug_base +  1, s_base + 2))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 2, ug_base +  3, s_base + 3))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 3, ug_base +  5, s_base + 4))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 4, ug_base +  7, s_base + 5))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 5, ug_base +  9, s_base + 6))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 6, ug_base + 11, s_base + 7))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 7, ug_base + 13, s_base + 8))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 8, ug_base + 15, s_base + 1))

    #Star Facet: 8
    f.write('#Star Facet: num = 8\n')
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 2, t_base + 2))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 3, t_base + 3))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 4, t_base + 4))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 5, t_base + 5))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 6, t_base + 6))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 7, t_base + 7))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 8, t_base + 8))
    f.write('f {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 1, t_base + 1))

    #Upper-Girdle Facet: 16
    f.write('#Upper-Girdle Facet: num = 16\n')
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base +  1, ug_base +  2))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base +  2, ug_base +  3))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base +  3, ug_base +  4))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base +  4, ug_base +  5))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base +  5, ug_base +  6))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base +  6, ug_base +  7))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base +  7, ug_base +  8))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base +  8, ug_base +  9))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base +  9, ug_base + 10))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base + 10, ug_base + 11))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 11, ug_base + 12))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 12, ug_base + 13))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 13, ug_base + 14))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 14, ug_base + 15))

    f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 15, ug_base + 16))
    f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 16, ug_base +  1))

    #Girdle: 16
    f.write('#Girdle: num = 16\n')
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  1, lg_base +  1 , lg_base +  2 , ug_base +  2))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  2, lg_base +  2 , lg_base +  3 , ug_base +  3))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  3, lg_base +  3 , lg_base +  4 , ug_base +  4))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  4, lg_base +  4 , lg_base +  5 , ug_base +  5))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  5, lg_base +  5 , lg_base +  6 , ug_base +  6))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  6, lg_base +  6 , lg_base +  7 , ug_base +  7))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  7, lg_base +  7 , lg_base +  8 , ug_base +  8))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  8, lg_base +  8 , lg_base +  9 , ug_base +  9))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base +  9, lg_base +  9 , lg_base + 10 , ug_base + 10))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 10, lg_base + 10 , lg_base + 11 , ug_base + 11))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 11, lg_base + 11 , lg_base + 12 , ug_base + 12))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 12, lg_base + 12 , lg_base + 13 , ug_base + 13))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 13, lg_base + 13 , lg_base + 14 , ug_base + 14))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 14, lg_base + 14 , lg_base + 15 , ug_base + 15))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 15, lg_base + 15 , lg_base + 16 , ug_base + 16))
    f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 16, lg_base + 16 , lg_base +  1 , ug_base +  1))

    #Lower-Girdle Facet: 16
    f.write('#Lower-Girdle Facet: num = 16\n')
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base +  1, lg_base +  2))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base +  2, lg_base +  3))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base +  3, lg_base +  4))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base +  4, lg_base +  5))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base +  5, lg_base +  6))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base +  6, lg_base +  7))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base +  7, lg_base +  8))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base +  8, lg_base +  9))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base +  9, lg_base + 10))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base + 10, lg_base + 11))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 11, lg_base + 12))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 12, lg_base + 13))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 13, lg_base + 14))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 14, lg_base + 15))

    f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 15, lg_base + 16))
    f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 16, lg_base +  1))

    #Pavilion Facet: 8
    f.write('#Pavilion Facet: num = 8\n')
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  1 , p_base + 1, c_base + 1, c_base + 2, p_base + 2))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  3 , p_base + 2, c_base + 2, c_base + 3, p_base + 3))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  5 , p_base + 3, c_base + 3, c_base + 4, p_base + 4))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  7 , p_base + 4, c_base + 4, c_base + 5, p_base + 5))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base +  9 , p_base + 5, c_base + 5, c_base + 6, p_base + 6))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 11 , p_base + 6, c_base + 6, c_base + 7, p_base + 7))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 13 , p_base + 7, c_base + 7, c_base + 8, p_base + 8))
    f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 15 , p_base + 8, c_base + 8, c_base + 1, p_base + 1))

    #Culet: 1
    f.write('#Culet: num = 1\n')
    f.write('f 57 58 59 60 61 62 63 64\n')

# plot
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")

ax.scatter(x, y, z)
#ax.plot(x, y, z)
plt.show()


このpythonコードはカレントディレクトリにtest_diamond.objという3Dオブジェクトファイルを作成します。
幾つかの追加モジュールが必要です。
一応生成したファイルも以下に置いておきます。

g diamond
v  0.275000 -0.000000 0.162000
v  0.194454 -0.194454 0.162000
v  0.000000 -0.275000 0.162000
v  -0.194454 -0.194454 0.162000
v  -0.275000 -0.000000 0.162000
v  -0.194454 0.194454 0.162000
v  -0.000000 0.275000 0.162000
v  0.194454 0.194454 0.162000
v  0.365000 0.151188 0.097200
v  0.365000 -0.151188 0.097200
v  0.151188 -0.365000 0.097200
v  -0.151188 -0.365000 0.097200
v  -0.365000 -0.151188 0.097200
v  -0.365000 0.151188 0.097200
v  -0.151188 0.365000 0.097200
v  0.151188 0.365000 0.097200
v  0.500000 -0.000000 0.000000
v  0.461940 -0.191342 0.000000
v  0.353553 -0.353553 0.000000
v  0.191342 -0.461940 0.000000
v  0.000000 -0.500000 0.000000
v  -0.191342 -0.461940 0.000000
v  -0.353553 -0.353553 0.000000
v  -0.461940 -0.191342 0.000000
v  -0.500000 -0.000000 0.000000
v  -0.461940 0.191342 0.000000
v  -0.353553 0.353553 0.000000
v  -0.191342 0.461940 0.000000
v  -0.000000 0.500000 0.000000
v  0.191342 0.461940 0.000000
v  0.353553 0.353553 0.000000
v  0.461940 0.191342 0.000000
v  0.500000 -0.000000 -0.025000
v  0.461940 -0.191342 -0.025000
v  0.353553 -0.353553 -0.025000
v  0.191342 -0.461940 -0.025000
v  0.000000 -0.500000 -0.025000
v  -0.191342 -0.461940 -0.025000
v  -0.353553 -0.353553 -0.025000
v  -0.461940 -0.191342 -0.025000
v  -0.500000 -0.000000 -0.025000
v  -0.461940 0.191342 -0.025000
v  -0.353553 0.353553 -0.025000
v  -0.191342 0.461940 -0.025000
v  -0.000000 0.500000 -0.025000
v  0.191342 0.461940 -0.025000
v  0.353553 0.353553 -0.025000
v  0.461940 0.191342 -0.025000
v  0.200000 0.082843 -0.283600
v  0.200000 -0.082843 -0.283600
v  0.082843 -0.200000 -0.283600
v  -0.082843 -0.200000 -0.283600
v  -0.200000 -0.082843 -0.283600
v  -0.200000 0.082843 -0.283600
v  -0.082843 0.200000 -0.283600
v  0.082843 0.200000 -0.283600
v  0.012500 0.005178 -0.445225
v  0.012500 -0.005178 -0.445225
v  0.005178 -0.012500 -0.445225
v  -0.005178 -0.012500 -0.445225
v  -0.012500 -0.005178 -0.445225
v  -0.012500 0.005178 -0.445225
v  -0.005178 0.012500 -0.445225
v  0.005178 0.012500 -0.445225
#Table Facet: num = 1
f 1 2 3 4 5 6 7 8
#Bezel Facet: num = 8
f 1 9 17 10
f 2 10 19 11
f 3 11 21 12
f 4 12 23 13
f 5 13 25 14
f 6 14 27 15
f 7 15 29 16
f 8 16 31 9
#Star Facet: num = 8
f 1 10 2
f 2 11 3
f 3 12 4
f 4 13 5
f 5 14 6
f 6 15 7
f 7 16 8
f 8 9 1
#Upper-Girdle Facet: num = 16
f 10 17 18
f 10 18 19
f 11 19 20
f 11 20 21
f 12 21 22
f 12 22 23
f 13 23 24
f 13 24 25
f 14 25 26
f 14 26 27
f 15 27 28
f 15 28 29
f 16 29 30
f 16 30 31
f 9 31 32
f 9 32 17
#Girdle: num = 16
f 17 33 34 18
f 18 34 35 19
f 19 35 36 20
f 20 36 37 21
f 21 37 38 22
f 22 38 39 23
f 23 39 40 24
f 24 40 41 25
f 25 41 42 26
f 26 42 43 27
f 27 43 44 28
f 28 44 45 29
f 29 45 46 30
f 30 46 47 31
f 31 47 48 32
f 32 48 33 17
#Lower-Girdle Facet: num = 16
f 50 33 34
f 50 34 35
f 51 35 36
f 51 36 37
f 52 37 38
f 52 38 39
f 53 39 40
f 53 40 41
f 54 41 42
f 54 42 43
f 55 43 44
f 55 44 45
f 56 45 46
f 56 46 47
f 49 47 48
f 49 48 33
#Pavilion Facet: num = 8
f 33 49 57 58 50
f 35 50 58 59 51
f 37 51 59 60 52
f 39 52 60 61 53
f 41 53 61 62 54
f 43 54 62 63 55
f 45 55 63 64 56
f 47 56 64 57 49
#Culet: num = 1
f 57 58 59 60 61 62 63 64

この内容を test_diamond.obj という名前で保存すればpythonのコードを実行する必要はありません。
あとはBlenderなどの3D編集ツールにインポートすれば良いわけです。
尚、Blenderでインポートする場合がZが上で、法線を外側に揃えて下さい。
マテリアルはグラスBSDFでIORは2.420あたりにセットしてください。
透過屈折するオブジェクトを綺麗にレンダリングするためには周辺環境とライティングを作りこむ必要があるようです。
クオリティティを追求するにはテクが必要ですね。

まだまだ魂が吸い込まれそうな画像には程遠いですが、楽しいものです。
この記事をご覧のあなたもぜひお試しあれ。


MB-LAB


私は『小説家になろう』さんで小説『黒灰色(こっかいしょく)の魔女と時の魔女』を連載させていただいていますが、正直あまり読まれていません。
凄く面白いんですけれどね。(本当ですよ)

読まれていない理由は萌え絵の挿絵が無いせいだと思います。(いや、本当ですよ)
ラノベの価値はほぼほぼ挿絵で決まりますからね。
それで色々萌え絵を生成する方法を検討しているわけです。

「描画」ではなく「生成」と言っているのは絵が描けないためです。
描けないというのは語弊がありました。
それなりのクオリティの萌え絵を現実的な時間で描くことが私にはできないということです。
そりゃ、一か月かかって良いのならば描けるのかも知れませんが、それでは小説を更新する時間が取れませんしね。

で、ツールに頼ろうというわけです。
特に3Dキャラは一度作ると使いまわしができます。
昨今では 『カスタムキャスト』や『VRoidStudio』 など、色々なツールが有って、可愛いキャラを作ることができます。
カスタムキャストはバーチャルライブアプリで、萌え絵を作成するためのツールというわけではないのでしょうが、十分可愛らしい絵が作れるようです。
VRoidStudioは3Dキャラメイカーと銘打っているだけあって私の目的にかなりマッチします。
どちらもネット上で人気があります。
情報もあって活気があります。
熱いです。
これらを小説の挿絵に使うことは良い選択だと思います。

しかし私はもう一つの道を選びました。
上の絵は『MB-Lab』ベースのものです。
MB-Labはかつて『ManuelbastioniLAB』という名前で開発・公開されていたBlenderベースの3Dキャラクター生成ツールです。
『ManuelbastioniLAB』 は Manuel Bastioni氏のプロジェクトでしたが資金上の問題で開発中止となり、今では『MB-Lab』と名称を変え、Git-HUB上にForkされて開発・公開が続けられているプロジェクトです。

3Dの人物生成を行うツールで 『MB-Lab』は 一番優れたものだと思います。
このような優れたツールが無償で公開されていることは僥倖なことです。
そして資金上の問題で開発が行き詰ってしまったことは残念でしかたがありません。
Manuel Bastioni氏の功績はもっと報われるべきであるし、 『MB-Lab』 はもっと正当な評価を受けるべきです。

私は上の絵を『ManuelBastioniLAB 1.6.1a』ベースのキャラクターで作成しました。
正直、私の3Dの技術も絵として仕上げる技術もプアです。
私は『MB-Lab』の真価を発揮させることができていません。
しかしながら、それでも私にとって十分過ぎるほどの成果を 『MB-Lab』 は私に与えてくれます。

上の絵は『黒灰色(こっかいしょく)の魔女と時の魔女』の第五章第一話で、イリアが瀕死で運び込まれてきた少年ジャックの心を窃視する場面のイメージです。
宜しければ小説をお読みいただけますと幸いです。

『MB-Lab』はBlenderベースという時点で、 かなりとっつきにくく敷居が髙いものです。
また、本来はphotorealisticな人物生成を得意としているツールで、アニメ調キャラクターの生成は余技なのかもしれません。
自由度の高いツールであるので、使い手によって全然違うものができあがるでしょう。
私もそれほど使い込んでいるわけではないのですが、いくつか分かってきたことがあるのでシェアしていければ良いですね。


ブログ始めました


こんにちは。
小説家になろう』というサイト様でSF系のファンタジー小説『黒灰色(こっかいしょく)の魔女と時の魔女』を掲載させて頂いています。
ここでは小説とtwitterに掲載している宣伝画像を紹介させていただきます。

お付き合いいただければ幸いです。

上の絵は 『黒灰色(こっかいしょく)の魔女と時の魔女』 第四章第三話での南海での決戦後の風景です。

それはそうと、今回、何も無い所からwordpressの立ち上げを行ったのですが、色々なところで躓いてしまいました。
今までは動くように設定されているものを使わせていただいていただけなんだなあ、と痛感。

小説や絵の話だけでなく、技術的なお話もできればいいなと考えております。