スポンサーサイト

--年--月--日 --:--

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

動的にシェーダー定数をプログラム

2006年10月20日 16:12

ってことで、上記タイトルの機能を あるけ が趣味で作ってるシェーダー編集エディターに追加しました。

既存のシェーダーの開発ツールとしては nVidia の FX Composer や ATI の RenderMonkey があります。
どっちのツールも定数レジスタを設定することは出来るけどプログラムすることは出来ませんでした。
射影行列を作ったり、ライトの位置を簡単な円運動させたり、そういうことは出来なかったわけです。

もちろん、定数を設定するだけなら出来ます。
たとえば、HDRの閾値を入力したり。

しかし、実際にゲームで使う場合、定数レジスタに設定する値は複雑な演算を行ったり、
計算で求めたベクトル同士の内積の値を突っ込んだりします。
そういう細かな定数レジスタの設定は、出来ませんでした。

あるけ はプログラマなので、別にスライダーなどで定数レジスタが弄れなくても、簡易的なプログラムが書け、それで定数レジスタを制御できる方がうれしいです。


・・・ってことで、Closure の機能をバリバリ使ってタイトルの機能を追加しました。
アプリケーションの中でプログラムを入力すると、構文解析を行い、エラーが無ければプログラムを構築します。

どんなことが出来るか?というと、、、


char pProgram[] = "\
 VEC vTest  = init(1,0,0); \
 MAT mtTest  = rotY( rad(cnt()) ); \
  \
 return vTest * mtTest;\
"
;



こんな感じのプログラムがあるとします。
cnt() っていうのは1フレームに1ずつ増えるカウンタを返す関数、rad() は与えられたパラメーターを Degree から Radian にする関数、init() はベクトルの初期化関数、rotY() はY軸回転の行列を返す関数です。

このテキストをほいさーって感じに Closure のジェネレーターに突っ込んでやると、これを評価する Closure が返ってきます。
後は、値が欲しい時に Closure を評価してやれば、この Closure が計算し、「Y軸周りに回転するベクトル」を取得することが出来るわけです。


Closure を使ってるので、他のプログラムの中でこのプログラムを呼び出すことも可能です。
スクリプトに見えますが、インタプリタで動いてるわけでなく、式を保存しているので Closure を評価するだけで式の実行を行います。

関数型プログラムの悪い欠点で、あまり長いプログラムはスタック食いつぶすだろうから書けないけどねw
シェーダー開発ツールはこの機能を入れてもうちょっと機能を追加したら、公開しようかな?って思ってます。


ためしに作ったようなものだけど、他のゲーム会社とかでも使われたら最高だね~w


blog_rank
スポンサーサイト

C++言語で関数型プログラミング 第1章

2006年08月25日 18:07

前回、どこまで書いたっけかなw

・・・・・前回の解説じゃちょっと飛んでるところも多いね、こりゃ。

まず、用語説明から。
 ・TCClosure  クロージャー(Closure)を実現するために あるけ が作ったテンプレートクラス
 ・Lambda   無名関数を扱えるようにした BOOST に入ってるトリッキーなテンプレートクラス
こんなもんかな。

他にはカリー化とかも知っておいた方がいいんだろうけど、あるけ 自体実は良くわかってないので省きますw



さて、前回は式のオブジェクト化を行えば Closure が出来そうなこととか、Lambda がどうやって動いてるのか?を簡単に説明しました。

Lambda 自体はすごく綺麗に動的に関数の生成を行えるように作られています。
しかし、実際問題として、Lambda を有機的に使うことはすごく難しいです。
なぜなら、この前の説明で作った ((_1 * 100 + _2 + 10)(1,2)) の左辺値を変数に格納しようとしたら、

boost::lambda::lambda_functor_base::sig::type boost::lambda::lambda_functor::operator ()(A &,B &) const'
 with
  Act=boost::lambda::arithmetic_action,
  Args=boost::tuples::tuple    arithmetic_action,boost::tuples::tuple    lambda_functor,
    boost::tuples::tuple>,
    const boost::lambda::detail::parameter_traits_    IF::RET>::type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>>>,boost::lambda::lambda_functor>,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>>,const boost::lambda::detail::
    parameter_traits_::RET>::type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>,
  SigArgs=boost::tuples::tuple    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>,
  T=boost::lambda::lambda_functor_base,boost::
    tuples::tuple    lambda_functor,
    boost::tuples::tuple    arithmetic_action,boost::tuples::tuple    lambda::placeholder<1>>,const boost::lambda::detail::parameter_traits_    IF::RET>::type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,
    boost::tuples::null_type>>>,boost::lambda::lambda_functor>,boost::tuples::null_type,
    boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>>,const boost::lambda::detail::
    parameter_traits_::RET>::type,boost::
    tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::
    null_type,boost::tuples::null_type,boost::tuples::null_type,boost::tuples::null_type>>,
  A=int,
  B=int

まぁ、こんな冗談みたいな長さのテンプレートを宣言しないと格納できないわけです(;´Д`)'`ァ'`ァ

これが Lambda を使用する場合のもっともとっつきにくい部分でしょう。
使える場面が少ないのと、使う場合は必ずテンプレート関数にしないといけないからです。
暗黙のテンプレート引数として使えば、別にどんな型だろうが使う側は知らなくてもOKですからね。


次は、Lambda がどうやって動いてるのかを説明しようと思います。
今日は忙しいのでさわりの部分だけw


blog_rank

C++言語で関数型プログラミング 序章

2006年08月07日 17:33

以前書いたブログに Closure のこと書いてて、その記事の技術、全然公開してなかったから説明しながら公開していこうかと思います。


っていっても、Boost の Lambda みたいに綺麗になってないんだけどね。
けど機能はこっちの方が上だ!って自負がありますよこれ( ・`ω・´)

無名関数だって使えるし、遅延評価だってできます。
Haskell みたいに完全遅延評価(グラフリダクションっていうらしい。並列で遅延評価するって意味なのかな?)は出来ないけどね。
(出来たら恐ろしいけどw)



んで、本題。

やりたかったことはっていうと、

 1:動的に関数を生成する
 2:1で生成された関数も同じように動的に生成する関数の部品として再利用できちゃう
 3:無名関数みたいに、値が決まって無くてもあとで値を入れれば動いちゃう。
 
っていうことですね。
まぁ、要するに「式」をオブジェクト化したかったワケです。
「式」同士を演算子で結合できたりね。


Lambda の実装に触発されて出来たわけですけど機能は大分違いますね。

また、冒頭で Lambda みたいに綺麗になってないといいましたが、全て Template が生成する「実態」だけで済ませている Lambda と違い、このクラス(TCClosure と呼んでます)は new や malloc などで動的に確保したメモリを使用することでC言語特有の「汚い」実装になってるからです。
その代わり汎用的に出来るわけです。

実際、Lambda を有機的に使う場合、必ず Template 関数なりにしなければいけなかったところ、
Lambda の場合、型が自動的に作られるのと生成される型が複雑なので、
 とてもじゃないが指定して使うなんてことができない)
TCClosuer は戻り値だけ特定すれば、どんな関数やクラスでも利用可能です。
要するに、テンプレートの引数として必要なのは戻り値の型だけなんですね。


また、違う戻り値の TCClosure も型キャストさえ出来れば組み合わせて使う(組み合わせることで新しい TCClosure を生成する)ことが出来ます。



んで、どうやって実装しているか?
いきなり核心の部分を説明してもかなりわかりにくいです。


なんで、まずは簡単なことを考えましょう。

どうやって式をオブジェクト化するか?

これだけでもかなり抽象的ですねw

まず単純に考えて、
 a + b = f
において、
 「f の値を動的(必要な時)に求める」
が出来ればよさそうな気がします。

それじゃこれをクラス化してみましょう。


a を項クラス term とします。
そうすると、b は term クラスの保持する値が違うだけなので、同じ term クラスとして扱えます。
また、f は結果なので result クラスとします。

そうすると、a + b = f の式は
 term + term = result
と書けますね。


さて、ここで式の再利用を考えると result クラスの部分を term と置き換えれば上手く行きそうな気がします。
result = term とすれば term と term を演算子でつなぐと term を返す
これなら出来上がった term を使ってまた新しい「式」を定義できそうです。

そうです、これが Lambda で使ってる技術なんです。
Lambda の場合、全て「一つの部分」でやっちゃう
(たとえば
  ((_1 * 100 + _2 + 10)(1,2))
 など、式を変数として保存するのではなく、関数の引数として書いたり一箇所にまとめて書く)
ので、ワザワザ「式」を保存するオブジェクトを定義する必要がありません。
ですから、(Lambda の場合は)戻り値を再帰的に使って実現してるわけです。

(上の例では
 _1 * 100 で一つ戻り値が出来て(これを term1 とする)
 term1 + _2 でもう一つ戻り値が出来る(これを term2 とする)
 さらに term2 + 10 でもう一つ戻り値が出来る(これを term3 とする)
 よって、結局 term3(1,2) が呼ばれるわけです)



とりあえず今日はここまで。

次回は(いつになるかわからんけど)これの実装方法とか必要になるクラスのこととか書きます。




blog_rank



最近の記事


上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。