2017年12月2日土曜日

CyclesのDenoise

この記事は Blender Advent Calendar 2017 2日目の記事です。
(注意)この記事は急いで調べながら書いているので、間違いを含んでいる可能性があります。違ったらすみません



Blenderも気づけばバージョン2.79!
CyclesにDenoiseも載りました
今回はCyclesのDenoiseがどうなっているか、掘り下げていこうかと思います。

以下最初のシーンに適当なスザンヌとエリアライト追加してレンダーした結果です。
左がdenoise無し、右がdenoise有りです。

1spp


2spp


4spp


8spp


16spp


32spp



フルHDで、denoiseに16~17秒掛かっていますが、低サンプル数でもかなり綺麗になっています。
速度については、OpenCLを使用してレンダーするようにすると、denoiseについても半分くらいの速度でレンダーできました。

設定方法は簡単で、ここにチェックを入れるだけです。


実際の使用例や説明については公式ヘルプをご参照ください
Blender 2.79: Cycles: Denoising(デノイズ)
また、インタラクティブに確認できるサンプルがこちらにあります





それでは、Blenderのdenoiseがどうなっているのか、実装面見ていきます。

まずリポジトリをcloneしてきて、denois とかで検索すると
それらしきファイルが出てきました。


これでしょうか?
中をみると
bool DenoisingTask::run_denoising()
{
 /* Allocate denoising buffer. */
        なんか前処理っぽいコード

 /* Prefilter shadow feature. */
 {
        なんかコード
 }

 /* Prefilter general features. */
 {
        なんかコード
 }

 /* Copy color passes. */
 {
        なんかコード
 }

        なんか後処理っぽいコード
}

となっており、それっぽいですね。
影と、それ以外の部分について、別々にフィルターをかけて、
最後にカラーパスにコピーしているようなコメントが見えます。

また変数としてnlm_temporary_1 などといったものが定義されており、
Non-local Means Filter が使用されているであろうことがわかります。

Blenderは各種バッファが個別に(ノードとかで)とれると思いますが、
それらのために、いっぱいNLMの計算をしているように見えます。
最後に、denoising_reconstructとかいう関数で、レンダリング結果を再構成(上書き)しているようです。

それらしい文字で検索してみると、
https://developer.blender.org/D2592
こちらのコミットでdenoise用のフィルター関数が追加されたようです。

      REGISTER_KERNEL(filter_divide_shadow),
      REGISTER_KERNEL(filter_get_feature),
      REGISTER_KERNEL(filter_combine_halves),
      REGISTER_KERNEL(filter_nlm_calc_difference),
      REGISTER_KERNEL(filter_nlm_blur),
      REGISTER_KERNEL(filter_nlm_calc_weight),
      REGISTER_KERNEL(filter_nlm_update_output),
      REGISTER_KERNEL(filter_nlm_normalize),
      REGISTER_KERNEL(filter_construct_transform),
      REGISTER_KERNEL(filter_nlm_construct_gramian),
      REGISTER_KERNEL(filter_finalize),

とか追加されていて、やはり主にNLMを使用しているようですが、
CPU用とCUDA用とOpenCL用が同時に追加されていますね。
さらにAVX用のファイルも中は空っぽいですが追加されていて、気合いを感じます。
いやー大変ですね。

CPU版の関数らしきものは
blender/intern/cycles/device/device_cpu.cpp にあり、
bool denoising_non_local_means(device_ptr image_ptr, device_ptr guide_ptr, device_ptr variance_ptr, device_ptr out_ptr, DenoisingTask *task)
こんな感じの関数が実際のフィルタ処理しているところのようです。

https://developer.blender.org/D2592

さて、実際に入ってるコードは上記チケットのコードですが、

以前のチケットを見たところ、最初に入れたいって言ってた人は別の人のようで、
上記チケットのレビューもしているようです。
最初は、この論文を実装したいと言っていたようです。
Adaptive Rendering based on Weighted Local Regression
http://sglab.kaist.ac.kr/WLR/

現在のdenoiseはGoogle SoCのプロジェクト
https://wiki.blender.org/index.php/User:Lukasstockner97/GSoC_2016/Proposal
によって、無事、上記論文の手法が実装されたっぽいです。
この手法は pbrt2 にも実装されているそうです。
実装した人、2016年当時、18歳です。すごいですね。
Blenderは優秀な若人によって日々成長しています。



すみません、で結局なんなの?というところの詳細まで終えてませんが、

・kernelはNLMを使用している
・Weighted Local Regression(局所重み付き回帰)を使用している
 たぶん http://sglab.kaist.ac.kr/WLR/ の論文相当が実装されている。

ということでした。

いつも通りBlender Artistsのスレッド読めば楽勝だぜ!と思っていましたが、
長すぎて挫折しました…(現在89ページも伸びている)。すみません



Blender Advent Calendar 2017 3日目は
 yuinore さんによる レンダーファームとか です。





2016年12月11日日曜日

Custom Renderer(1)

本記事は、レイトレ Advent Calender 2016 10日目の記事です。


自作レンダラーをBlenderで使うためのあれこれです。
レイトレ自体とはあまり関係ないので恐縮です。

やりたいこと



前提として、この記事ではGPLライセンスは無視します。
成果物を公開する必要がない、Blenderを実験用DCCツールとして使いたい
といったプログラマを想定しています。

------------

事前知識

BlenderにはCyclesというパストレのレンダラーが載っています。
このレンダラーが、モンテカルロ的な感じで、良い感じにジワジワとリアルな絵を
レンダリングしてくれるのはご存知かと思います。

Cylcesはapacheライセンスで、オープンソースで公開されています。
レンダラーを作ってる人は、無料のBlenderに乗ってるCyclesみたいな感じで
本体に自分のレンダラーを組み込んでテストしたい!と思うはずです。

それを実際に商用ソフトで実践した例としてOctaneRender(for Blender)があります。
こちらは、まさにCyclesのようにノードを組み、シーンによりますがCyclesより速い速度で
Blenderのレンダービューにレンダリング結果を表示します。



OctaneRenderでは、Blender本体を改造して、TCP通信によりローカルにインストールされたOctaneRenderServerと高速に通信を行っています。
一見リアルタイムに見えますが、裏でTCPでジオメトリやカメラ情報を送り、
送り返されたレンダリング結果画像をBlender上に表示しているというわけです。
通信を介することで、GPLライセンスの汚染を回避しています。

このOctaneRender(forBlender)による仕組みを利用できないか、と考えたこともありました
しかし、このアプローチは本体改造のため、延々に本体の更新に合わせて
サポートし続ける必要があり、趣味で週末にやるには厳しいものがあります。

そこで、Blender本体で、改造なしにできる、レンダラー組み込み方法を見ていきます。

レンダラ用API

まず、Blender PythonでRender Engineというのがあります。
RenderEngine(bpy_struct)
https://www.blender.org/api/blender_python_api_2_78a_release/bpy.types.RenderEngine.html?highlight=renderengine

こちらにあるサンプルをBlenderのTextEditorに張り付けて実行します。
すると、Flat Color Rendererというレンダラーが追加されます。





続いて、F12を押してレンダリングしてみます。



このように青くなります。
先ほど張り付けたスクリプトでは、次のあたりで画像作ってるようです。

    # In this example, we fill the full renders with a flat blue color.
    def render_scene(self, scene):
        pixel_count = self.size_x * self.size_y
        # The framebuffer is defined as a list of pixels, each pixel
        # itself being a list of R,G,B,A values
        blue_rect = [[0.0, 0.0, 1.0, 1.0]] * pixel_count 
        # Here we write the pixel values to the RenderResult
        result = self.begin_result(0, 0, self.size_x, self.size_y)
        layer = result.layers[0].passes["Combined"]
        layer.rect = blue_rect
        self.end_result(result)

ピクセルを書き込んでるようですね。無事画像は出せそうです!

とは言っても、Pythonでレンダラー作ってる人なんて少数派なので、
これを別プロセスで走るC++などのレンダラーから行うことを考えます。

----------------------

データの引き渡し

レンダリングするには、Blender上のジオメトリやテクスチャ、カメラやノードなどのデータを、
自作レンダラーに引き渡さないといけません。

これを行うには複数の方法が考えられます
  • (1) 自作レンダラーのPythonインタフェースをboost.pythonやpybind11等で作り、ダイナミックリンクさせ、Blenderと同一プロセスで自作レンダラーを動かす。
  • (2) TCPやwebsocketやnamedpipeでローカル通信を行う。
  • (3) メモリマップファイルを使用して一部のメモリを共有させる。
(2)を行うことで、OctaneのようにGPLライセンスの汚染を回避できたりしますが、
今回は最近やってみたかった(3)のメモリマップファイルというのを使ってみることにします。


----------------------

メモリマップファイルで画像共有

マルチプロセスの環境では、直接メモリを参照できる(と思われる)メモリマップドファイルは、かなり有効な手段ではないかと思い、試してみます。

stbimageで1枚画像を読み込んで、メモリマップとして登録するC++コードはこんな感じです。

#define STB_IMAGE_IMPLEMENTATION

#include <windows.h>
#include <string>
#include "stb_image.h"

int main(int argc, char *argv[])
{
 if (argc <= 1) return -1;

 std::string file = argv[1];
 int w, h, channels;
 unsigned char* data = stbi_load(argv[1], &w, &h, &channels, 4);

 HANDLE hmap = CreateFileMapping((HANDLE)-1, NULL, PAGE_READWRITE, 0, w * h * 4, "testmmap");
 if (hmap == NULL) {
  stbi_image_free(data);
  return -1;
 }
 if (GetLastError() == ERROR_ALREADY_EXISTS) {
  printf("ERROR_ALREADY_EXISTS\n");
 }
 LPSTR mapview = (LPSTR)MapViewOfFile(hmap, FILE_MAP_ALL_ACCESS, 0, 0, 0);
 if (mapview == NULL) {
  stbi_image_free(data);
  return -1;
 }

 // メモリマップに画像を転送.
 memcpy(mapview, data, w * h * 4);

 // TODO: ここでメモリマップドファイルをBlenderで読み込み表示させる

 // ここでメモリマップ削除される.
 UnmapViewOfFile(mapview);

 CloseHandle(hmap);
 stbi_image_free(data);
 return 0;
}

続いて、python側でこれを表示させます。
とりあえずVCのデバッグで、UnmapViewOfFileのところでbreakで止めて
render_sceneのところに以下のコードを埋めこんでレンダーしてみます。
画像サイズはとりあえず決め打ちです。


        blue_rect = [[0 for i in range(4)] for j in range(pixel_count)]
        
        mm = mmap.mmap(-1, 240*240*4, tagname="testmmap")
        mm.seek(0, os.SEEK_END)
        mmsize = mm.tell()
        mm.seek(0)
        image = mm.read(mmsize)

        for i in range(int(mmsize / 4)):
            rgba = struct.unpack_from("4B", image, i*4)
            blue_rect[i][0] = rgba[0] / 255
            blue_rect[i][1] = rgba[1] / 255
            blue_rect[i][2] = rgba[2] / 255
            blue_rect[i][3] = rgba[3] / 255





ふむ、逆になってますが、なんか出ました。結果画像はいけそうです。


----------------------

データ共有

これが厄介です。

objで出力するコード部分にメモリマップで出すように仕込んだりしてみましたが、
objで出力する(Blender標準の)エクスポーターが遅い!!!
100万ポリゴン一瞬で出してくれないと困ります。

BlenderはAlembicに対応しましたが、Alembicの出力コードはpythonからは全くアクセスできず、
こちらも使えません。

また、各種自作レンダラーは、独自のシーンファイルを定義しないと動かなかったりして、なかなか厄介です。


すみませんここ考えてる途中で時間切れです!(続く)

明日は Ushio さんによる、”確率密度関数周りの何かを” です!










2016年8月3日水曜日

blender - MMDボーン名をLRにする

絶対誰か作ってるだろうと思うけど、それ探して入れるより書いたほうが圧倒的に速い気がしたので、
ちゃちゃっと作成。

to_blender
右~→~.R
左~→~.L

to_mmd
~.R→右~
~.L→左~

となります

使い方:
テキストエディタに張り付けてメッシュとボーンを全部選択して実行。
元に戻すときは、最後のあたりを
#to_blender()
to_mmd()
にして実行。

免責事項:
数分で書いたかき捨てスクリプトなので必ず保存してから実行してください
どうなっても責任とれません

# -*- coding: utf-8 -*-
import bpy

def to_blender():
 for obj in bpy.context.selected_objects:
  if (obj.type == "ARMATURE"):
   for bone in obj.data.bones:
    if ("右" in bone.name):
     bone.name = bone.name[1:] + ".R"
    if ("左" in bone.name):
     bone.name = bone.name[1:] + ".L"
  if (obj.type == "MESH"):
   for group in obj.vertex_groups:
    if ("右" in group.name):
     group.name = group.name[1:] + ".R"
    if ("左" in group.name):
     group.name = group.name[1:] + ".L"

def to_mmd():
 for obj in bpy.context.selected_objects:
  if (obj.type == "ARMATURE"):
   for bone in obj.data.bones:
    if (".R" in group.name):
     bone.name = "右" + bone.name[0:-2]
    if (".L" in group.name):
     bone.name = "左" + bone.name[0:-2]
  if (obj.type == "MESH"):
   for group in obj.vertex_groups:
    if (".R" in group.name):
     group.name = "右" + group.name[0:-2]
    if (".L" in group.name):
     group.name = "左" + group.name[0:-2]


to_blender()
#to_mmd()

2016年7月25日月曜日

blender - 軸を変えずにボーンの長さを変更する技

以下 twitterで教えてもらった手法(@tarava777 さんありがとうございます)

1. ボーン編集モードで、マニピュレータをノーマルにして、左のやつをアクティブ要素にします
2. 長くしたいボーンのヘッドと、その子ボーンすべてを選択します。
違う軸のマニピュレーターが出てしまった場合は、
ヘッドの丸ぽち目がけてshift押しながらマウス右クリックを何回か押すとうまく選択できたりします。
3. マニピュレーターでy軸方向に移動させます。
以上です。




以下古い手法 結構悩んでたどり着いた技。



(わかりやすいように、ちょっとオーバーにしてます)

これではMMDのモーションが同じように適用できない!
そこで全てのユーザトランスフォームをリセットした状態で、

ポーズモードで、スケールを継承のチェックを外し、

ポーズモードで、スケールさせます。
Ctrl + A で、デフォルトのポーズに適用します
これでようやく軸を維持したままボーンの長さを変えることができます。めんどい

2016年5月8日日曜日

blender - ディフュージョンフィルタ

アニメ表現を目指す上で欠かせない(?)、ディフュージョンフィルタを試してみました。
元ネタはググったらいっぱい出てきます。

元画像

ノード

結果画像


それっぽいですね!

ちなみにDiffusionって書いてあるフレームの中だけだとこんな感じ。
フレームの外の部分は、参考にしたブログの人がこんな感じにしてたのでやってます。
見比べてみると、フレームの中だけのほうが良いかな…。

2015年12月6日日曜日

Blender - Blenderバーガーオフ2

Blender Advent Calender 2015 12月6日の記事です。

みなさん、そろそろ忘年会シーズンですが、いかがお過ごしでしょうか??

さて、本日、2年ぶりに、Blenderバーガーオフ!を開催したので、その模様をお伝えしたいと思います。

Blenderバーガーオフ会とは


Blender使いが集まってハンバーガーを食べるオフ会です。

なぜハンバーガーなのか?


主催者がハンバーガー好きだからです。

Blenderと関係あるのか?


バーガーは、パーティクル(ゴマ)、ソフトボディ(バンズ)、弾性/流体計算(チーズ)、サブサーフェススキャタリング(パティ/レタス)
などの様々なCG要素で構成されています。
かの有名なビルゲイツですらハンバーガーを食べながら仕事をしていたと言われています。
また、Blenderを通じて、以下のような様々なバーガーに対するアプローチが行われています。

http://blenderartists.org/forum/showthread.php?351419-Burger
http://gman20999.deviantart.com/art/Blender-cycles-3D-model-burger-render-568035930
http://real3d.fr/3rd-day-with-blender-food/
https://inevitablecrafts.wordpress.com/2012/06/01/blender-camera-t-r-a-c-k-i-n-g/


みなさんもCGをやる傍ら、バーガーを食べて健康を保ちましょう!

Blenderバーガーオフ2の模様



こちらがバーガーオフの模様です!


とても美味しそうですね!

やはりこの魅力は静止画だけでは伝え切れません。
そこで動画を撮ってみました。こちらです!
(酔っ払っていて微妙な撮り具合ですみません)


どうでしょうか。おいしいハンバーガーを食べたくなってきましたでしょうか?
一旦Blenderの手を止めて美味しいバーガーを食べましょう!
Blenderで行き詰っていた問題が解決するかもしれません!!

さて、明日12/7のBlender Advent Calenderは、@psi_ni_phi さんです!