前へ | 上へ

四章

グローバルイルミネーション(大域照明)



概要

Now we should see the most astonishing feature of the Lightflow Rendering Tools, the one that will make your images real, and their colors alive: global illumination. In the last five years many renderers integrated special techniques to handle the light transport phenomena, but they almost always provided slow algorithms, that would also fail in many situations, producing both uncorrect results and anaesthetical artifacts.
Lightflow's Global Illumination Engine produces almost perfect images, which are so physically accurate as good-looking to the human eye.

Before entering the next tutorial, I would like to spend some words on the internals of Lightflow's Global Illumination Engine, giving the right space to some important credits.
At this point, you should already have understood that Lightflow is not based on a monolithic structure: on the contrary it adopts a divide and conquer philosophy. In particular the proprietary Multiway Light Channels split the complex problem of light transport in many simpler parts, each treated with ad hoc algorithms.
The result is a mix inspired by the most powerful rendering techniques available to date, such as Arvo's backward ray-tracing [1] [2], Kajiya's path tracing [3], Jensen's photon maps [4], Shirley's photon density estimation [5], Ward's irradiance gradients [7], and probably many others that have at least influenced the wonderful research field of Computer Graphics...

ようやく私達は Lightflow Rendering Tools の最も驚くべき特徴を理解するでしょう。これはあなたのイメージを実現化し、それらの色をグローバルイルミネーションで活かします。これまでの五年間、多くのレンダラーが光転送現象を扱うための特別なテクニックを統合してきましたが、それは遅いアルゴリズムと、ほとんどの場合では不正確な結果と美しくない汚点の両方を作り出す失敗も、大体いつものように提供しました。
Lightflowグローバルイルミネーションエンジンは人間の目で見たままのように、物理的に正確な、ほとんど完璧なイメージを作り出します。

次のチュートリアルに入る前に、重要なクレジットに当然のスペースを与え、Lightflowグローバルイルミネーションの内部に言葉を入れておきたいです。
この時点で、Lightflow がモノリシック構造をベースにしていないことをあなたはもう既に理解しているはずです: それどころかこれは分割と獲得の考え方を採用しています。特にマルチウェイライトチャンネルは特別なアルゴリズムでそれぞれ扱われる多くの単純なパーツに、光転送の複雑な問題を分割しました。
以下の人々と、恐らくコンピュータグラフィックスの分野の素晴らしい研究に少しでも影響を与えた多くの人々の、現在までのところ利用できる最もパワフルなレンダリングテクニックによって引き起こされた混合の結果です: Arvo's backward ray-tracing [1] [2], Kajiya's path tracing [3] Jensen's photon maps [4], Shirley's photon density estimation [5], Ward's irradiance gradients [7]


コーネルボックス

To try Lightflow's Global Illumination Engine we will use the famous Cornell Box, an experimental scene that was invented at the Cornell University to test different algorithms.

LightflowGlobal Illumination Engine を試してみましょう。アルゴリズムの違いをテストするためにコーネル大学 (Cornell University) で考案された有名な実験シーンである、Cornell Box を使います。


from lightflowPM import *

s = scene()

s.newInterface( "default",
		[ "trace-depth", 6,
		  "radiosity-depth", 6,
		  "radiosity-samples", 400,
		  "radiosity-threshold", 0.1,
		  "radiosity-reuse-distance", 0.25, 0.4, 0.01,
                  "photon-count", 300000,
                  "photon-clustering-count", 2000, 100 ] )

# the "trace-depth" attribute controls the maximal number of ray-traced 
# light bounces.
# the "radiosity-depth" attribute controls the maximal number of
# radiosity iterations, that is to say the number of bounces of the indirect
# illumination.
# the "radiosity-samples" attribute sets the amount of rays that are
# used to sample the light space at every surface location. Normally
# values between 200 and 500 produce good results. Note that this parameter
# is very influent on the rendering time, since light sampling is one
# of the most time consuming tasks.
# "radiosity-threshold" sets the maximal error bound in the radiosity
# estimation. A value of 0.1 means that the error is allowed to be 10% 
# of the real value.
# "radiosity-reuse-distance" sets the screen, maximum and minimum distance
# from different sampling locations. This parameter is the only one that
# must be set accordingly to the scene size. The smaller these values are, 
# the better the result will be, but usually a good value for the
# screen distance is from 0.2 to 0.5, while a good value for the
# maximum distance is everything greater than one fifth of the length
# of the visible surfaces.
# In this case we are modeling a room with sides 2 unities long, hence
# a value of 0.4 will prove to be good enough. The minimum distance
# should be an order of magnitude less.
# The "photon-count" parameter controls the amount of photons that are 
# spread into the scene to compute the global illumination. Obviously
# more photons means better approximations and longer times.
# "trace-depth" アトリビュートはレイトレースライトの跳ね返りの最大数
# を制御する。
# "radiosity-depth" アトリビュートは間接照明の跳ね返り回数という、放射
# 反復回数の最大数を制御する。
# "radiosity-samples" アトリビュートは全てのサーフェイス面のライト空間
# をサンプル取得するために使用する放射量を設定する。通常の値の 200 と
# 500 の間はよい結果を生む。このパラメータは、ライトサンプリングが最も
# 時間を浪費する作業の一つなのでレンダリング時間に非常に影響すること
# に注意する。
# "radiosity-threshold" は放射評価の最大跳ね返り誤差を設定する。値 0.1
# は実際の値の 10% のエラーを許すという意味である。
# "radiosity-reuse-distance" はサンプリング位置の距離からの最大と最小
# スクリーンを設定する。
# このパラメータはシーンの大きさによって設定されていなければならない。
# この値を小さくするほどよい結果となるが、可視サーフェイスの長さの 5
# 分の 1 よりも大きい距離が最大値としてよい値であるのに対して、通常は
# 0.2 から 0.5 のスクリーン距離がよい値である。
# この場合、単位長さ 2 方面の部屋をモデリングしており、従って値 0.4
# が十分に良好なであることが証明される。最小距離は大小の順番となる。
# "photon-count" パラメータは全体的な照明を計算するためにシーンの中で
# 拡散する光粒子の量を制御する。明らかに大量の光粒子は良好な近似と長い
# 時間となる。


s.lightOn( s.newLight( "soft-conic",
                       [ "position", vector3( 0, 0, 0.98 ),
                         "direction", vector3( 0, 0, -1 ),
                         "angle", 0.0, 3.141592 / 2.0,
                         "radius", 0.05,
                         "samples", 7,
                         "color", vector3( 8, 8, 8 ) ] ) )

# We simulate an area light with a conic light that produces soft
# shadows. The spreading angle of the light is set to 90 degrees
# (PI / 2 in radians) to obtain the same light distribution of a patch 
# light source. We could also put a real "patch" light, with a well
# defined surface, but the computation times would have been longer.
# Check the class documentation to see how area lights work, and how
# fake soft shadows may be obtained with the "soft" and "soft-conic" types.
# 軟らかな影を作り出す円錐状ライトで領域の照らしをシミュレートする。
# ライトの拡散角度はパッチライトソースの距離と同じライトを得るために
# (PI / 2 の角度で) 90 度に設定されている。十分に定義したサーフェイス
# に実際の  "patch" ライトを置いているが、計算回数は長くなるだろう。
# エリアライトがどのように働き、"soft" と"soft-conic" タイプで得られる
# ソフトなシャドウをどのように真似するのかを知るためにクラスドキュメン
# トをチェックすること。


neon = s.newMaterial( "matte", [ "kc", vector3( 3, 3, 3 ), "shadowing", 0.0 ] )

whitewash = s.newMaterial( "generic",
                           [ "kdr", vector3( 0.9, 0.9, 0.9 ),
                             "ksr", vector3( 0.5, 0.5, 0.5 ),
                             "km", 0.07,
                             "shinyness", 1.0,
                             "radiosity", 1,
                             "caustics", 0, 0 ] )

redwash = s.newMaterial( "generic",
                           [ "kdr", vector3( 0.8, 0.1, 0.1 ),
                             "ksr", vector3( 0.5, 0.5, 0.5 ),
                             "km", 0.07,
                             "shinyness", 1.0,
                             "radiosity", 1,
                             "caustics", 0, 0 ] )

bluewash = s.newMaterial( "generic",
                           [ "kdr", vector3( 0.2, 0.3, 0.8 ),
                             "ksr", vector3( 0.5, 0.5, 0.5 ),
                             "km", 0.07,
                             "shinyness", 1.0,
                             "radiosity", 1,
                             "caustics", 0, 0 ] )

trnswash = s.newMaterial( "generic",
                           [ "kdr", vector3( 0.9, 0.9, 0.9 ),
                             "ksr", vector3( 0.5, 0.5, 0.5 ),
                             "km", 0.07,
                             "shinyness", 1.0,
                             "radiosity", 1,
                             "caustics", 0, 0,
                             "visibility", 1 ] )

metal = s.newMaterial( "physical",
                       [ "fresnel", 1,
                         "IOR", 9.0,
                         "kr", vector3( 0.95, 0.95, 1 ),
                         "kd", 0.0,
                         "km", 0.02,
                         "shinyness", 1.0,
                         "radiosity", 0,
                         "caustics", 2, 2 ] )

glass = s.newMaterial( "generic",
                       [ "fresnel", 1,
                         "IOR", 1.57,
                         "kdr", vector3( 0, 0, 0 ),
                         "kdt", vector3( 0, 0, 0 ),
                         "ksr", vector3( 1, 1, 1 ), vector3( 0.5, 0.8, 1 ),
                         "kst", vector3( 1, 1, 1 ), vector3( 1, 0.6, 0.2 ),
                         "kr", vector3( 1, 1, 1 ),
                         "kt", vector3( 1, 1, 1 ),
                         "km", 0.02,
                         "shinyness", 1.0,
                         "transmission", 0,
                         "radiosity", 0,
                         "caustics", 2, 2 ] )


s.materialBegin( whitewash )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -0.25, -0.25, 0.995 ), vector3( 0.25, -0.25, 0.995 ),
                            vector3( -0.25,  0.25, 0.995 ), vector3( 0.25,  0.25, 0.995 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -0.25,  0.25, 0.99 ), vector3( 0.25, 0.25, 0.99 ),
                            vector3( -0.25,  0.25, 1.00 ), vector3( 0.25, 0.25, 1.00 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -0.25, -0.25, 0.99 ), vector3( -0.25, -0.25, 1.00 ),
                            vector3(  0.25, -0.25, 0.99 ), vector3(  0.25, -0.25, 1.00 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3(  0.25, -0.25, 0.99 ), vector3(  0.25, -0.25, 1.00 ),
                            vector3(  0.25,  0.25, 0.99 ), vector3(  0.25,  0.25, 1.00 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -0.25, -0.25, 0.99 ), vector3( -0.25, 0.25, 0.99 ),
                            vector3( -0.25, -0.25, 1.00 ), vector3( -0.25, 0.25, 1.00 ) ] ) )

s.materialEnd()

s.materialBegin( neon )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -0.25, -0.25, 0.99 ), vector3( 0.25, -0.25, 0.99 ),
                            vector3( -0.25,  0.25, 0.99 ), vector3( 0.25,  0.25, 0.99 ) ] ) )

s.materialEnd()

s.materialBegin( trnswash )

s.addObject( s.newObject( "patch",
			  [ "points",
			    vector3( -1, -1, -1 ), vector3( 1, -1, -1 ),
			    vector3( -1, -1,  1 ), vector3( 1, -1,  1 ) ] ) )

s.materialEnd()

s.materialBegin( whitewash )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -1, -1, -1 ), vector3( -1, 1, -1 ),
                            vector3(  1, -1, -1 ), vector3(  1, 1, -1 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -1, -1, 1 ), vector3( 1, -1, 1 ),
                            vector3( -1,  1, 1 ), vector3( 1,  1, 1 ) ] ) )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -1, 1, -1 ), vector3( -1, 1, 1 ),
                            vector3(  1, 1, -1 ), vector3(  1, 1, 1 ) ] ) )

s.materialEnd()

s.materialBegin( redwash )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( -1, -1, -1 ), vector3( -1, -1, 1 ),
                            vector3( -1,  1, -1 ), vector3( -1,  1, 1 ) ] ) )

s.materialEnd()

s.materialBegin( bluewash )

s.addObject( s.newObject( "patch",
                          [ "points",
                            vector3( 1, -1, -1 ), vector3( 1, 1, -1 ),
                            vector3( 1, -1,  1 ), vector3( 1, 1,  1 ) ] ) )

s.materialEnd()


s.materialBegin( glass )

s.transformBegin( transform().translation( vector3( -0.45, 0, -0.1 ) ) )

s.addObject( s.newObject( "sphere", [ "radius", 0.35 ] ) )

s.transformEnd()

s.materialEnd()


s.materialBegin( metal )

s.transformBegin( transform().translation( vector3( 0.45, 0.4, -0.65 ) ) )

s.addObject( s.newObject( "sphere", [ "radius", 0.35 ] ) )

s.transformEnd()

s.materialEnd()


saver = s.newImager( "tga-saver", [ "file", "cornell.tga" ] )

s.imagerBegin( saver )

camera = s.newCamera( "pinhole", [ "eye", vector3( 0, -2.99, 0 ), "aim", vector3( 0, 0, 0 ) ] )

s.imagerEnd()


s.radiosity()

s.render( camera, 300, 300 )
(画像の表示)
最後に

Here this manual ends. Now you should have at least understood the main mechanims upon which Lightflow is based, otherwise you'll better reread the first chapters. If you cannot comprehend some of the examples because the effect of some classes is not very clear, try checking the relative class documetation.
If then something still remains obscure feel free to contact me via email, using the subject Lightflow Explanations. I will also appreciate any suggestion (in this case use the subject Lightflow Suggestions, please).
For now, enjoy Lightflow and have a good time !

ここでマニュアルは終了です。もうあなたは Lightflow がベースにしている主要メカニズムを少しは理解したことでしょう。そうでなければ、もう一度最初の章からこのマニュアルを読んだほうがよいでしょう。クラスの効果がいまいち分からなくてサンプルのいくつかを理解できなければ、関連するクラスのドキュメントをチェックしてみて下さい。
それでもまだ分からないままであれば、Lightflow Explanations という件名で電子メールによって私までお気軽にコンタクトをとって下さい。あらゆる提案をお待ちしております (この場合は Lightflow Suggestions という件名にしてね)。
それでは、Lightflow を楽しんで、素敵な時間をお過ごし下さい ! 4-June-1998