SICP

SICP を読んでみる #6 第一章 pp.22-23

本文

反復アルゴリズムに慣れるために両替が例に上がっている。ただ、ここで書かれている日本語がよくわからない(汗。
しかたないので原文もあわせて読みつつ何を言っているのか解釈(実際には不安に思ったところに曖昧さはなかった)。

註33の内容を試しにやってみる。両替を cc 、d を使用する硬貨とすると

(cc d 10)
(cc 1 10) + (cc 5 5)
(cc 1 9) + (cc 5 5) → (cc 1 n) は (cc 1 0) になり、条件より 1 に置きかわる
1 + (cc 5 5)
1 + (cc 1 5) + (cc 5 0) → (cc 1 5) は先の計算の途中にも出て、1 に置きかわる。また、(cc 5 0) は条件より 1
1 + 1+ 1 = 3

という感じ。硬貨の額面金額を取得するために配列が使えないと、それだけで問題が難しくなるような気がする。

問題解答

問題1.11
再帰は簡単。

(define (f n)
	(if (< n 3) n (+ (f (- n 1)) (f (- n 2)) (f (- n 3)))))

反復でやっぱりハマるものの、うっすらといつも使っているループとの対応が取れてきた気がする。
自分の解答は以下の通り。

(define (f n)
    (define (f-iter i v0 v1 v2)
        (cond ((< n 3) n)
                 ((= i (- n 3)) (+ v0 v1 v2))
                 (else (f-iter (+ i 1) v1 v2 ))))
    (f-iter 0 0 1 2))

。。。と思って回答を見たら全然違う(そもそも問題を間違えていた)

(define (fi n)
    (f-iter 2 1 0 n))

(define (f-iter a b c n)
    (cond ((= n 0) c)
            ((= n 1) b)
            ((= n 2) a)
            (else (f-iter (+ a b b c c c) a b (- n 1)))))

ループだとカウントアップしていくのに、反復だとカウントダウンしていく感覚が何だかなじめない。
ただ、確かに数学的にはこっちの方がそれっぽい気はする。
頭の中に値が変化するモデルが組み立てられれば、反復の方が簡潔かつ柔軟に記述できそうな気はする。

SICP

SICP を読んでみる #5 第一章 pp.20-22

問題解答

問題1.9

前者は (+ a b) が (inc (+ (a-1) b)) と展開できるので、a が 0 になるまで繰り返し展開された後で集約されていきます。そのため再帰的。後者は (+ a b) が (+ (a-1) (b+1)) と置き換えられ、a が 0 になるまで繰り返し置き換えられるので反復的。

問題1.10
(A 1 10) は手で何とか展開できたものの、 (A 2 4) は大変すぎるので実行して動作確認しました。
回答っぽいのには辿り着いたけど、2^..^n の計算の仕方が。。。。 f の時って 2n なので、その時でも一般化された (A m n) って成り立つのかがわからない。ざっくりした流れはつかめたので置いておくことにします。

本文

1.1.2 木構造再帰
再帰版の fib は一瞬で書けたものの、反復版でつまづく(というか、今もモヤッとしている)。反復の書き方がどうも慣れない。
明日、反復の復習をします。

SICP, 未分類

SICP を読んでみる #4 第一章 pp.14-20

問題解答

問1.8

これは手を動かすだけ。 improve を式のままに置き換えて終了です。

ちなみに確認用に pow も定義してみました。

(define (pow x y)
	(if (= 1 y)
			x
			(* x (pow x (- y 1)))))

本文

1.1.8 ブラックボックス抽象としての手続き
変数のスコープの話。内部定義とブロック構造で、問1.6の回答で出てきた記法の解説。このようなやり方を静的有効範囲(lexical scoping)という。

1.2 手続きとその生成するプロセス
線形再帰プロセスと線形反復プロセス。最初、二つの例がどう違うのかよくわからなかったものの、if は条件を評価した結果を評価してるだけだから、スタックに積まれるというわけではないという解釈でいいんでしょうかね?普通のプログラミング言語だとどちらも再帰になりそうな気がするんだけど。

P.20 まで本文を読み進めるとまさにそのあたりのことが記述されている。再帰的プロセスと再帰的手続きの違い。

SICP

SICP を読んでみる #3 第一章 p.14 (オマケで環境構築)

問題解答

問1.6

昨日わからなかった問1.6を考えるところから再開。いろいろ考えたもののわからないので、ギブアップして回答を参考にすることにしました。

述語(good-enough? guess x)が真になっても, new-ifは代替部
(sqrt-iter (impreve guess x) x)
を評価しようとするので, 停止しない.

これを読んで納得。

(new-if good-enough? guess sqrt-iter)

ということになるので、 new-if が評価される前に good-enough? と sqrt-iter 両方が評価されてしまうのか。蓋を開けてみれば何てことはなかったです。

問1.7

計算精度の問題。値が小さいときは許容値が相対的に大きくなりすぎて十分計算がされなくなってしまって、値が大きい時は大きな数値間の差を取ることで有効桁数が落ちて許容値以下にならなくなってしまうため、無限ループになってしまう。

プログラムの改良は一応自前でできたものの、解答と照らし合わせると全く知らない記法がされていて目が点。
これは追々やるだろうし、今のところはこんな感じなのねということで写経しながら流れを掴むだけにとどめておきます。

環境構築

そろそろコードをコンソールで打つ(そして無限ループに入って死ぬw)のも辛いので、今日は Gauche+Meadow で動作確認できるように環境構築をすることにしました。

環境構築は”Meadow + Gauche セットアップメモ“を参考。。。というか、そのまま実行。M-x run-scheme で gosh プロンプトが出るところまですんなり行きました。

もうちょっと詳しい使い方も知りたかったので、ひげぽんさんの”関数型言語の勉強にSICPを読もう – (4) 1章 – 小休止 Schemeの環境整備“も参考にいろいろお試し。

.scm は自動で Sceme モードと紐づいているようなので、ファイルを編集し始めた時点でモードが切り替わります。
操作として覚えないといけないのは

  • M-x run-scheme で gauch インタプリタ起動
  • ウィンドウを分割して、一方にソースコード、一方で gauche インタプリタを起動した状態で式の末尾で C-x C-eするとその式を実行

さしあたってこの二つくらい。最初勘違いしていたのが、C-x C-e するとファイル全体が評価されるのかと思っていた点。これは式だけなので気をつける必要があります。ファイル全体を評価する方法も知りたかったですがすぐに見つからず。Meadow を使ってるのに Lisp 周りは全然使ってないのがバレバレですね orz→問1.7の回答を見たら、そもそも一つの式にまとめてしまっているのでファイル全体を評価するという必要がないのかも?うーん。。。それだけじゃ足りないような気もするんですが。

SICP

SICP を読んでみる #2 第一章 pp.12-14

何だかんだ作業していたら夜中になってしまって SICP を読む時間がほとんど取れず。
作業の進捗も大事だけど、うまいこと切り替えないと時間の捻出ができなくなってしまうので、今後対策を考えていかないと。

今回は Newton 法の解説を読みつつコードを写経して動作確認と、問1.6に着手。

問1.6は実際に動かしてプログラムが返ってこなくなることは確認できたものの、なぜそうなるか説明ができないところでタイムアウト。明日また考える。

SICP

SICP を読んでみる #1 第一章 pp.1-12

今日は新幹線の中で読んだ内容のおさらいとして 1-12P を、メモをとりながら読み込み。内容はプログラムの基礎や Lisp の文法、Lisp インタプリタ内での式の評価順序について。

演習問題を確かめようと実行すると無限ループになって帰ってこなくなるような罠が潜んでたり、さっそく Lisp の面白い書き方例が出てきたり。

(define (a-plus-abs-b a b)
((if (> b 0) + -) a b))

なんてのは、実行して理解したら”おおっ!!”という感じがする。

SICP

SICPを読み始めてみた

ずっと昔から読もう読もうと思いつつほったらかしにしていた SICP を今年は読んでみることにしてみました。
#正月、帰省帰りの新幹線内でたまたま読み始めたってのがきっかけなんですが

1.1.6 まで読んだ感じだと、まあ一人で読むこともできそうだなーって感触はあるものの、多分一人で読んだら体力切れで途中で挫折するなーって感じもしているのでちょっと計画を立てつつ完走できるようにしたいなとおもっています。

計算機プログラムの構造と解釈[第2版]は書籍も出ていますが、Web でも公開されています。Web 版は書籍で載っていない問題の解答もあるのでとても助かります。

そんなこんなで動作確認をするために Scheme を実行するための環境構築をしなければいけないんですが、Scheme って言っても結構いろいろあるものなんですね。どれにするかなーとググっていたら正にこの正月から SICP を読み始めたという同志(こっちが勝手に言ってるだけw)を発見。

SICP を読むために Emacs で Scheme 環境を構築

環境構築の内容がものすごく詳細に書かれていて参考になりそうなのでこちらを参考にさせていただきつつ、処理系も Gauche を使うことに。 Gauche は SICP で使われているものとは細かいところで違いがあるようなので、 “SICPを読むためにやっておくと便利かもしれないこと” あたりに目を通しておくといいかも?

ひとまずコードを実行して動作確認をしたいだけなら Gauche を起動すればできるので今日のところはこれで確認するとして、ボチボチ Emacs の連携とかの環境を整えつつ本を読み進めていこうかとおもっています。

未分類

モデリング時の注意点について考察してみた

最近、公私共にモデルデータの綺麗さということについて考えさせられることが結構あったので、自分の勉強のためにモデリングを行う際に注意する点をまとめてみました。自分で検証と考察を繰り返している段階な上に、自分自身は業務としてモデリングを行ったことはないのでこれが正しいとは全く思っていません。むしろおかしいところがあったら教えてください。

ここでは主にムービー用の高精度のモデルを作成することを想定していて、 リアルタイムやローポリゴンのモデルを作成する場合は事情が変わってきます。

※そのため、ここ数日話題になっているシドニアモデルに関してはこの内容は当てはまらないと考えています。トゥーンレンダリングにはそれに合ったモデリング手法があり、それに沿った解釈を行う必要があると私は思います。

Transform ノードの数

Transform ノードの数が多いとシーンファイルのサイズが大きくなったりビューポート上の処理が重くなったりするので、可能な限りモデルはコンバイン(アタッチ)して一まとめにします。

Transform ノードの数による違い

Transform ノードの数が違うとどれくらい影響があるのかを検証するために、Maya で 10000 個の Cube を別々に作成したものと、一つのオブジェクトとしてまとめたものを比較しました。 どちらも Cube を作成後ヒストリーの削除をおこない、ただのメッシュとしたものを 10000 個用意し、上位のグループで回転アニメーションをつけています。ThinkPad T530(Corei7-3720QM/16GB/NVIDIA NVS 5400M) での計測結果です。

タイプ fps メモリ使用量 ファイルサイズ(ma) ファイルサイズ(mb) ファイル読み込み時間(mb) オブジェクトの選択にかかる時間
別々に作成 3.8fps 686.7MB 14MB 11.2MB 15秒 15秒
Combine 130fps 456.9MB 9.3MB 6.1MB 体感上ゼロ 体感上ゼロ

数値化すると激的な違いがあることがわかります。

メッシュ構成

基本

基礎は北田さんの Blog を見るのがいいです

四角形ポリゴン化

モデルは極力四角形ポリゴン化します。

四角形ポリゴンでモデリングする際の参考画像があります。

http://www.pixelandpoly.com/graphics/referenceimages/step-down-guide.png

試しに自分でも同様のモデルの作成をおこなってみました。

三角形ポリゴンがダメというわけではないですが、五角形以上のポリゴンは使用してはいけません。五角形以上のポリゴンは必ず三角形と四角形で構成できるので適切に分割します。

三角形ポリゴンを使用して一番影響があるのは、メッシュにスムースをかけた場合です。

四角形ポリゴンのみで構成されている場合、メッシュにスムースをかけても元のメッシュの形はそのままで間が綺麗に補間されるだけですが、三角形ポリゴンが含まれているとその周りのメッシュがゆがんで補間されてしまいます。

このゆがみが平面上にある場合は大きな影響を及ぼさないですが、曲面上にある場合はメッシュの皺やゆがみとしてレンダリングされてしまいます。

※これは本当?前述の参考画像でもメッシュのゆがみは発生しているので、これをもってNGな原因とするのは根拠が弱くないか?

ポリゴンは平面に

三角形ポリゴンの場合は必ず平面になりますが、四角形ポリゴンの場合は四つの頂点がずれると平面にならなくなってしまいます。誤差が小さい場合は気にならないですが、ずれが大きくなると問題が大きくなってきます。

ポリゴンのシェーディングは法線の情報を使用して行われます。法線はノーマルマップやディスプレイマップのようなものを使用しない限りポリゴン上のどの位置でも同じ値が使用されるため、ポリゴンが歪んでいるとその影響が大きくなります。

※これはちょっと根拠が弱い気がする。面の法線方向が正しくならないほうが問題としては大きい?

面の解釈が曖昧という方が根拠になりそう?

多分、これが問題になるかどうかはレンダラーに依存する。 例えば、Renderman の場合はレンダリング時にマイクロポリゴンにされるので問題にならない。それに対し、レイトレーサーの場合はポリゴンを三角形分割するので、面の状態によって割り方が変わってアーティファクトが発生するかもしれない。

まとめ

私の知識の範囲内でモデリングをする時に気をつける必要がある内容を考察・検証してみました。直観的にこれはマズそうだなーとか、一般的にダメと言われている内容でもキッチリと理論付けしようとすると意外とアレアレッ?と思うことがあります。特に面の解釈についてはレンダラ依存ですしね。

ぜひ、この記事を読んでおかしいところやご自分で検証した内容があれば教えてください。よろしくおねがいします。

雑記

弊社論文が NICOGRAPH2014 に採択されました

既にスケジュールにも載っていますが、弊社が投稿した論文がNICOGRAPH2014に採択され、11/4に発表をおこなうことになりました。まわりを見渡すと弊社以外は全て学校関係者の方々ということで、今から緊張しています。

今回投稿することになったのはひょんなきっかけからではありましたが、これを機会に CG や映像を研究している方々にも現場が抱えている悩みや問題点、テーマを知っていただき、将来にわたって産学相互に刺激を受けあうことができるきっかけとしたいと考えています。

Blender, Programming

第1回 Blenderソースコードリーディング 準備:Blender2.6aのコードを読んでみる(1)

引き続き、ドキュメントのある Blender2.6a をベースにコードリーディングを進めます。

Blender 内の動作を確認する

デバッガで動作を確認する

  1. Blender を Debug 版でビルドする
  2. 起動
  3. Visual Studio(VS) で デバッグ/プロセスへアタッチ

これで Blender と VS が接続された。

ブレークポイントを張ってみる

とりあえず適当にブレークポイントを張って、Blender の挙動を確認してみる。
コードをざっと見て bf_blenkernel に mesh.c というのがあったので、そこの add_mesh が Blender でジオメトリを作成するときに呼ばれる関数だろうという目星をつけてブレークポイントを張る。

Space→Add Cube で Cube を作成しようとすると、ブレークポイントで処理が止まって各種情報を確認できるようになる。

call stack を辿っていくと add_primitive_cube_exec() が呼ばれていて、更に呼び出し元を辿ると wm_* 関数群まで戻る。このあたりが GUI のメニュー処理を行っている。
wm_* 内で引き継がれている引数 ot がメニューと実際の処理を紐付けるためのもので、この中にどのメニューの表示内容やメニューが選ばれたときに呼ばれる関数がまとめられている。

このようにして、特定の機能やコードを起点にその周囲の処理の流れを把握することができる。

このときの情報を元に、次にどの方向に進むかを考える。今回の情報だけでも

  • ジオメトリの生成や管理
  • GUI でのイベント処理
  • ot を中心としたイベント、メニュー、処理のテーブル管理
  • テーブル管理のためのアーキテクチャ

といった方向性に進んでいくことができる。

Blender 内の構造を確認する

Blender code layout を見ながらコードのレイアウトを確認する。

横方向の点線でレイヤが分けられていて、矢印でレイヤ間/レイヤ内の依存関係が示されている。

Application startup レイヤ

例として Application startup レイヤの説明をおこなう。Application startup レイヤは blender/source ディレクトリにあり、creator と blenderplayer モジュールから構成されている。この二つのモジュールは依存関係がなく、それぞれは “Editor definitions, drawing, interaction” 以下のレイヤの機能を呼び出している。

モジュールの複雑さ

全てのレイヤは基本的に下のレイヤに依存しているため、上位に行けば行くほど複雑に、逆に下位に行けば行くほどそれぞれのモジュールの機能はシンプルになっていることが想像できる。
図をボヤッと見ていてもどこから手をつけていけばわからないが、タイトル下部に書いてある矢印の図を参考に、下の方から見ていくのがコツ。

Utility Libraries(in own development)レイヤ

Utility Libraries(from external development)レイヤ以下は外部で開発されている共通ライブラリなので今回のコードリーディングの対象にはならない。興味があればそれぞれのライブラリのプロジェクトを調べ、個別に調査をする。

つまり、Blender が抱えるコードで最もシンプルなのはUtility Libraries(in own development)レイヤに所属するものということになるので、まずはここの構造を把握することから始める。

モジュール概観

このレイヤに所属するモジュールをざっと眺めてみる

  • メモリ管理
    • guardedalloc
    • memutil
  • 文字列処理
    • string
  • 数値解析
    • opennnl
  • シーンデータ管理
    • bsp
  • データ処理・ソルバー
    • audaspace
    • boolop
    • decimation
    • elbeem
    • iksolver
    • itasc
    • smoke
    • mikktspace
    • moto
  • システム・GUI
    • container
    • ghost

分類としては色々雑なものの、以上のような感じ。
このように同じレイヤ内でも全く異なる目的だったり、アプリケーション上での位置付けが異なるのものが並列して記述されているので要注意。ただ、このレイヤはあくまでもユーティリティライブラリなので直接 Blender の特定の機能と結びつくものではなく、汎用的な部品の扱いとなる。

また、モジュール一つ一つが VS のプロジェクトとして分けられている。例えば、smoke は bf_intern_smoke プロジェクトになっているので、ここのコードを辿れば Blender で使用している smoke solver の情報を得ることができる。

今回のまとめ

Blender の内部を解析するにあたって、実際の Blender の挙動から処理の流れを追う方法と、コードレイアウトのドキュメントから Blender の構造を把握する方法の二つの方法をおこなった。この二つの方法を用いることで、アプリケーションの構造を把握するための糸口を掴むことができるようになる。

引き続き、Blender Architecture を参考にしながら Blender の論理的な構造を探っていく。