ピクセル単位のライティング

Philip Taylor
Microsoft Corporation

November 13, 2001

この記事のソースコードをダウンロードする。

**注   **このダウンロードを実行するためには DirectX 8.1 が必要です。

Driving DirectX へようこそ。今月のコラムでは、ピクセル シェーダの重要性を説明しながら、ピクセル単位のライティングについて解説します。ピクセル単位のライティングの基本的な事柄を説明し、さらに標準的なライティング モデルとカスタム ライティング モデルの両方を説明します。MicrosoftR DirectXR 8.0 (および 8.1) は、多様なライティング モデルを実現できる柔軟性を備えています。すべての人が標準モデルを理解し、各々のニーズに応じて独自のモデルを定義できなくてはなりません。

本コラムは、GDC の Dan Baker と Chas Boyd が作成した「ピクセル単位のライティング」の講演に含まれている資料をベースにしています。これをもとにした講演は、Meltdown Shader Workshop と SIGGRAPH のDirect3DR コースで行われました。

その代わりに、本コラムではピクセル ライティングの基本事項、標準モデル、および新しいモデルを定義するプロセスに焦点を当て、ピクセル シェーダを使って新しいライティング モデルを定義して実装する例を示します。ピクセル シェーダの真の価値が発揮されるのは、カスタムあるいは「do-it-yourself」のライティング モデルの時代になってからですが、とりあえず未来に目を向けることにしましょう。

ピクセル単位のライティングの基本事項

まず、読者は基本的なディフューズ ライティングとスペキュラ ライティングについての知識を持っていると仮定します。ここでは、図 1 に示すような物理モデルが前提となります。では、標準のライティング モデルを取り上げて、システムと用語の定義を示すことにしましょう。

次の図 1 は、Direct3D の固定機能ライティングの記述に使用される標準的なライティングの様子を示しています。位置 P によって定義される頂点、L ベクトルによって定義されるライト、V ベクトルによって定義される視点位置、および N ベクトルによって定義される法線があります。さらに、この図には Phong シェーディングの Blinn による単純化の一部である「ハーフ ベクトル」の H が示されています。ディフューズおよびスペキュラ反射ライティング システムは、これらの要素を使って記述することができます。

図 1. 標準のライティングの図

P = 頂点位置

N = 頂点 P の単位法線ベクトル

L = ライトから頂点位置への正規化されたベクトル

V = 頂点 P から視点位置 V への正規化されたベクトル

R = ライト反射率 R を表す正規化されたベクトル

H = Blinn による単純化に使用される、L と V の中間にある正規化されたハーフベクトル H

ディフューズ ライティングは、N と L の内積を使用して、カラーに対するディフューズの寄与を決定します。内積は 2 つのベクトル間の角度の余弦を表します。したがって、以下が成り立ちます。

  • 角度が小さい場合、余弦値は大きくなるので、この成分の最終的なカラーへの寄与は大きくなります。
  • 角度が大きい場合、余弦値は小さくなるので、この成分の最終的なカラーへの寄与は小さくなります。

スペキュラ ライティングの Phong 公式は、光の反射方向を表す反射ベクトル R とライト ベクトルLを使用し、R と L の内積の n 乗を計算します。累乗値 n により、各種のサーフェスをシミュレートすることができます。以下が成り立ちます。

  • 累乗値 n が大きければ、結果のハイライトは小さく明るくなり、光沢のあるサーフェスをシミュレートできます。
  • 累乗値 n が小さければ、結果のハイライトは大きく暗くなり、光沢のないサーフェスをシミュレートできます。

Blinn による単純化は R ベクトルを、R と L の中間のベクトルHに置き換え、累乗値 n を変更して、高品質な画像を得るための複雑な計算に十分に似た結果を計算します。ただし、H は R よりもはるかに簡単に計算できるので、計算コストを大幅に節約できます。

Direct3D のライティングは、ハードウェアとソフトウェアのどちらで行う場合でも、これらの公式を頂点レベルで使用します。残念なことに、頂点ライティングには 2 つの望ましくない性質があります。

  1. 頂点ライティングでは、きれいな外観を得るために、メッシュを細かくする必要があります。さもないと、下位のジオメトリの粗さが目に見えるようになります。
  2. 頂点ライティングでは、それを使用するすべてのアプリケーションが似たような外観を持つようになります。

三角形ラスタライザはローカル ジオメトリに関する深い知識なしに頂点を線形補間するので、頂点ライティングをきれいに見せるためには、メッシュを細かくすることが必要不可欠です。ジオメトリが粗すぎたり、ジオメトリが短い距離の中で多数のバリエーションを含んでいると、ハードウェアは優れたイメージ品質を得られるような値を生成できないことがあります。一方、メッシュを細かくすると、パフォーマンスが低下します。これに、頂点ライティングが特徴的な外観を持っているという事実を組み合わせれば、ほとんどの場合に適していないことがわかります。例外は、頂点ライティングがグローバルなアンビエントに使用される場合、またはピクセル単位のライティングと組み合わせて使用される場合です。

さて、ライティングに関してこれだけの知識を得たいまは、ピクセル単位のライティングがなぜ重要なのかが簡単にわかることでしょう。ピクセル単位のライティングは非常に優れているので、あらゆる人が使いたがります。

ピクセル単位のライティングには 2 つのアプローチがあります。

  1. ワールド空間内でのピクセル ライティング。
  2. 接空間内でのピクセル ライティング。

「接空間」とは何で、いったいどこから来たのだという疑問を持つ方がいるかもしれません。

ピクセル単位のライティングは、テクスチャ マッピングを使ってライティングを計算します。これは目新しいことではなく、テクスチャを使用するライト マッピングは、何年も前からゲームにおいてライティング エフェクトを生成するために使用されてきました。目新しいのは、ピクセル シェーダでは、テクスチャ ブレンド操作の代わりに、テクスチャ マップの中の値を使ってライティングの計算を行えるということです。

ライティングの計算が関係してくるので、これが 3D (座標空間、あるいは単に「空間」)の中で正しく行われるよう注意を払う必要があります。すべてのライティングは同じ座標空間の中で計算されなくてはなりません。法線がライトの方向と異なる空間の中にあったら、それらの間で行われる計算はすべて無意味になります。フィートにメートルを掛けるというような、まったく意味のない行為になるのです。

あらゆるライティング アプローチでは、この条件を念頭に置きながら、計算のすべての成分が同じ空間に含まれるようにソース値の計算を行う必要があります。今回の例では、2つの入力のセットを扱うことになります。

  1. テクスチャまたは「接線」空間に格納されている法線およびバンプ マップ。
  2. オブジェクトまたはワールド空間に格納されているライトの方向および環境マップ。

法線マップについては、テクスチャ マップの中の texel は、カラーではなくベクトルを表しています。下の図 2 は、法線が含まれている座標空間を示しています。テクスチャの幅と高さに沿った標準の u および v の方向は、テクスチャのサーフェスに対して垂直な "w" の方向と結合され、(u,v,w) の基底が得られます。つまり、1,0,0 というtexel は、実際には u ベクトルの 1、v ベクトルの 0、w ベクトルの 0 の成分に相当します。u、v、および w が互いに直交している場合、これは正規直交基と呼ばれます。これが生成するテクスチャ空間基底が、一般に「接空間基底」と呼ばれます。

u、v、および w に関して、これらのベクトルが現実の世界にどのように対応しているのかという疑問を抱く方もいるでしょう。一見すると、これはテクスチャの一部であるように思えます。しかし現実には、これらのベクトルはテクスチャ処理が行われる実世界のオブジェクトに貼り付けられます。つまり、個々の頂点で、u、v、および w はまったく異なる方向を持つことも考えられるのです! したがって、メッシュ上の、同じテクスチャを持つ 2 つの場所は、u、v、および w が異なるため、まったく異なるベクトルを持っている可能性があります。正規マップにおけるx、y、z は、実際には x*u + y*v + z*w を意味していることを思い出してください。u、v、および w が違っていれば、これらのベクトルは x、y、z が同じであっても異なるベクトルになります。難しいのは u、v、w を見つける作業で、それ以降の操作はかなり単純です。

図 3 は、オブジェクトとテクスチャ マップの両方の座標空間の相対的な関係を示しています。ピクセル ライティングのどちらのアプローチでも、すべてのソース データを、接空間からワールド空間へ、またはワールド空間から接空間へ移動する必要があります。

図 2. テクスチャ座標系

図 3. 基底計算の図

接空間 (u,v,w) が定義されたので(これらのベクトルを発見する方法については後述)、次はワールド空間ピクセル ライティングについて説明します。ワールド空間ピクセル ライティングでは、テクスチャ データのトランスフォームが必要です。たとえば、テクスチャ マップ texel をワールド空間にトランスフォームします。ここでは、ライト ベクトルは頂点シェーダの中で変換されず、ワールド空間の中に残されます。トランスフォームが必要なのはテクスチャ データなのです。単に、接線基トランスフォームを、3 つのテクスチャ座標としてピクセル シェーダにトランスフォームします。従法線を外積によって生成することで、計算量を節約できることに注意してください。イテレータはこの行列を個々のピクセルに対して補間します。その後、ピクセル シェーダと接空間の基底を使用して、ピクセル単位のライティングを実行します。

接空間ピクセル ライティングは、この逆です。この場合には、ライトおよび環境マップ データを接空間にトランスフォームすることが必要となります。頂点シェーダの中でライト ベクトルがトランスフォームされ、ピクセル シェーダに渡されます。環境マップ データはピクセル シェーダの中でトランスフォームされ、ピクセル シェーダはピクセル単位のライティングを実行します。

もう 1 つ、用語法上の規則について説明しておきます。法線、w、および z 軸のベクトルは、いずれも同じベクトルとして定義されます。接線は u 軸ベクトルで、従法線は v 軸ベクトルです。接線ベクトル u を、個々のポイントでテクスチャの u 軸に並行名ベクトルとして事前計算を行うことが可能であり、またこれが必要となります。

次に、接空間のトランスフォーム プロセスについて説明します。

まず、これが逆変換であることに注意してください。線形代数では、正規直交基を持つ行列の逆行列は転置行列となります。したがって、(u,v,w) がサーフェスの基底ベクトルで、Lがライトだとすると、トランスフォームは次のようになります。


             [U.x U.y U.z] * [-L.x]
   [V.x V.y V.z] * [-L.y]
   [w.x W.y W.z] * [-L.z]

        

これを展開すると、次が得られます。


             L.x' = DOT3(U,-L)
   L.y' = DOT3(V,-L)
   L.z' = DOT3(W,-L)

        

ただし、

U = テクスチャの x 軸に沿った接線

V = 法線

W = 従法線 U x W (外積)

内積はライトの方向の負の値を使って計算することに注意してください。

接空間ベクトルの計算は、法線の計算と似ています。w は法線として定義されているので、この情報はすでに手に入っています。後は、u と v を生成する必要があります。接空間基底ベクトル ( u と v ) を生成するには、次の式を使用します。


             Vec1 = Vertex3 ? Vertex2
   Vec2 = Vertex1 ? Vertex2
   DeltaU1 = Vertex3.u - Vertex2.u
   DeltaU2 = Vertex1.u - Vertex2.u
   DirectionV = |DeltaU2*Vec1-DeltaU1*Vec2|
   DirectionU = |DirectionV x Vertex.N|
   DirectionW = |DirectionU x DirectionV|

        

ただし、

X は外積を示します。

|| は正規化されたベクトルを取ることを示します。

Vertex1-3 は現在の三角形の頂点です。

通常、接線と法線はオーサリング プロセスの過程で計算し、従法線はシェーダの中で(外積として)計算します。したがって、頂点形式に追加するフィールドは u ベクトルのみです。さらに、基底が正規直交であると仮定すれば、v は u と w の外積なので、v を格納する必要もなくなります。

注意しなくてはならない点: 接線は平均値を取る必要があります。また、テクスチャ ラッピングに注意するようにしてください( u と v の値を変更するため)。DirectX 8.1 の D3DX には、接空間の計算に役立つ新しいメソッドがあります。ドキュメントをチェックしてください。

キャラクタ アニメーションでは、法線と接線のスキンを行う限り、このテクニックは有効です。スキンを行った後にシェーダ内で従法線を生成することで、スキンを行うベクトルを3つではなく 2 つに抑えることができます。これは、インデックス付きパレット スキニングと 2/4 行列スキニング、および頂点アニメーション(モーフィング)でも使えるテクニックです。パフォーマンスの点で見ると、トランスフォームを頂点単位のレベルで行えるという理由から、接空間ライティングは優れています。頂点ライティングよりもクロック数は少なくて済み、ピクセル単位の dp3 内積は他のピクセル演算と同等の速度を持つので、この点でパフォーマンスが失われることはありません。ディフューズとスペキュラを同じパスで実行するには、ライト ベクトルとハーフベクトルを計算し、この両方を接空間にトランスフォームします。その後、ピクセル パイプラインの中で、摂動の実行と内積の計算が、ピクセル シェーダの中かマルチテクスチャを使って行われます。

次の頂点シェーダのコードは、接空間ライト ベクトルの計算方法を示しています。


          // v3 は法線ベクトル
// v8 は接線ベクトル
// c0-c3 はワールド トランスフォーム
// c12 はライトの方向

// 接空間基底の生成
m3x3 r3,v8,c0      // 接線をワールド空間にトランスフォーム
m3x3 r5,v3,c0      // 法線をワールド空間にトランスフォーム

mul r0,r3.zxyw,r5.yzxw   // 外積によって従法線を生成
mad r4,r3.yzxw,r5.zxyw,-r0
            
dp3 r6.x,r3,-c12      // 結果として得られた行列を使って
dp3 r6.y,r4,-c12      // ライト ベクトルをトランスフォーム
dp3 r6.z,r5,-c12      // r6 は接空間におけるライトの方向

        

これを、接空間へのトランスフォームが必要なすべてのベクトルについて繰り返せばよいのです。

もう 1 つ注意しなくてはならないのは、内積ライティングに必要な値の範囲です。内積演算は、ライティング操作を実行するために [-1,1] の範囲で表現されたデータ、すなわち符号付きのデータを含んでいます。標準テクスチャ形式は、カラー値をマップするために [0,1] の範囲で表現されたデータを含んでおり、したがって符号なしのデータを含んでいます。ピクセル シェーダと固定機能パイプラインは、どちらもこのギャップを埋めるためにテクスチャ データの再マップを行う修飾子を定義しており、テクスチャ データを内積演算で使えるようにしています。ピクセル シェーダは、入力データを符号なしから符号付きに再マップする _bx2 引数修飾子を定義しています。このため、内積演算への入力引数には、通常はこの修飾子が適用されています。また、内積の結果を _sat 命令修飾子を使って黒にクランプするのも有効です。次に、典型的な内積ピクセル シェーダ命令の例を示します。


          dp3_sat r0, t0_bx2, v0_bx2    // t0 法線マップ、v0 はライトの方向

        

固定機能パイプラインでは、テクスチャ入力についてはテクスチャ引数修飾子 フラグ D3DTA_COMPLEMENT が、範囲 [-0.5,0.5] の結果についてはテクスチャ演算子 D3DTOP_ADDSIGNED が、範囲 [-1.0,1.0] の結果については D3DTOP_ADDSIGNED2X が使用されて、似たようなプロセスが実行されます。

これで、ピクセル単位のライティングに関する基本的な知識を得ることができました。次は、標準ライティング モデルを紹介し、ディフューズおよびスペキュラ ライティングがピクセル シェーダの中でどのように動作するかを説明します。

標準ライティング モデル

標準ライティング モデルにはディフューズおよびスペキュラ ライティングが含まれます。どちらのライティング モデルでも、ピクセル シェーダと、固定機能マルチテクスチャと互換のテクニックを使用することができます。これらのテクニックとその代替テクニックを理解しておくことで、複数の世代のグラフィックス カードに対応できるシェーダ戦略を作成することが可能となります。DirectX 6.0 世代のカードはマルチテクスチャに対応しており、ほぼすべてが減算ブレンディングを行うことができ、一部は内積ブレンディングを行うことができます。例としては TNT2、Rage128、Voodoo 5、および G400 などがあります。DirectX 7.0 世代のカードは、ハードウェア トランスフォームとマルチテクスチャの両方に対応しており、ほとんどすべてが減算ブレンディングと内積ブレンディングの両方を行うことができます。例としては、geForce2 や Radeon があります。すべての DirectX 8.0 カードは、頂点シェーダとピクセル シェーダをハードウェアで実行することができます。例としては、geForce3 や Radeon8500 があります。

ピクセル単位のディフューズは、スペキュラなしの標準ライティング モデルに相当します。別のライティング項を考慮する必要がないので便利です。ピクセル単位のディフューズ計算を行った後では、個々のピクセルに正しいライティングが行われています。**フィルタリングは大きな問題になりうることに注意してください。**法線は、さまざまな理由からフィルタリングが行えないためです。次に示すのは、ピクセル単位のディフューズ ライティングのためのセットアップを行う、頂点シェーダのコードの一部です。これには、接空間でのライトの方向の計算、ピクセル単位の内積を計算するためのバイアス、およびピクセル シェーダのためのテクスチャ座標のセットアップが含まれます。


          //v0 = 位置
//v3 = 法線(w ベクトル)
//v7 = テクスチャ座標
//v8 = 接線(u ベクトル)

vs.1.1
//位置のトランスフォーム
m4x4 oPos,v0,c4

//tsbの生成      
m3x3 r3,v8,c0         //法線の生成
m3x3 r5,v3,c0         //接線の生成

//外積によって従法線を生成
mul r0,-r3.zxyw,r5.yzxw;
mad r4,-r3.yzxw,r5.zxyw,-r0;
        
//ディフューズ、ライト ベクトルのトランスフォーム
dp3 r6.x,r3,-c16
dp3 r6.y,r4,-c16
dp3 r6.z,r5,-c16

//oD0のライト
mad oD0.xyz,r6.xyz,c20,c20   //ハーフを掛けた後にハーフを加える
     
//テクスチャ座標   
mov oT0.xy, v7.xy
mov oT1.xy, v7.xy

        

次に、典型的なディフューズ ピクセル シェーダを示します。


          ps.1.1
tex t0                  //サンプル テクスチャ
tex t1                  //サンプル法線

//ディフューズ
dp3_sat r1,t1_bx2,v0_bx2         //内積(法線、ライト) 

//最終的なカラーのアセンブリ
mul r0,t0,    r1            //ベース カラーに対して乗算

        

これは、dp3 を使って N と L の内積を計算する典型的な例です。概念上、これはピクセル シェーダを作成するための優れた方法です。図 4 は、このシェーダが実際に動作している様子の画面ショットです。計算と、最終的なカラーのアセンブリが分離されていることに注意してください。次に、使用されているレンダリング状態をエフェクト ファイル形式の構文で示します。


          VertexShaderConstant[0]  = World Matrix (transpose)      
VertexShaderConstant[8]  = Total Matrix (transpose)
VertexShaderConstant[12] = Light Direction;    
VertexShaderConstant[20] = (.5f,.5f,.5f,.5f)

Texture[0]         = normal map;
Texture[1]         = color map;

        

ピクセル シェーダがきわめて単純であることに注目してください。図 4 は、ピクセル単位のディフューズ ライティングのイメージの例を示しています。この例からわかるのは、ピクセル単位のディフューズでは、見栄えのする結果を簡単に得られるということです。すべてのピクセル シェーダ カードは dp3 演算子をサポートしているので、ピクセル シェーダ ハードウェアではこのテクニックを使うようにするのがいいでしょう。

それよりも前の世代のカードでは、主に 2 つの代替テクニックがあります。その第 1 のものは、D3DTOP_DOTPRODUCT3 固定機能演算子です。この演算子は DirectX 6.0 で導入されたものなので、前世代のカードのうちのいくつかの高機能なカードがサポートしています。D3DTEXOPCAPS_DOTPRODUCT3 能力をチェックして、このマルチテクスチャ能力がサポートされているかどうかを確認してください。ValidateDevice() を使うのもいいアイデアです。以下に、D3DTOP_DOTPRODUCT3 固定機能演算のためのマルチテクスチャのセットアップを示します(エフェクト フレームワーク構文を使用しています)。


          ColorOp[0]   = DotProduct3;
ColorArg1[0] = Texture;
ColorArg2[0] = Diffuse; 
      
ColorOp[1]   = Modulate;
ColorArg1[1] = Texture;
ColorArg2[1] = Current;

VertexShaderConstant[0]  = World Matrix (transpose)
VertexShaderConstant[8]  = Total Matrix (transpose)
VertexShaderConstant[12] = Light Direction;

Texture[0]   = normal map
Texture[1]   = color map;

        

colorop はテクスチャ ステージ演算を示しており、colorarg [n] はテクスチャ ステージ引数を示しています。ミップマッピングとフィルタリングも設定する必要がありますが、スペース上の理由からこれらの設定は無視することにします。固定機能パイプラインの中の D3DTOP_DOTPRODUCT3 演算子は、_sat および _bx2 演算を自動的に適用することに注意してください。これは、次の意味を持ちます。

  • _bx2 に正しい結果を生成させるためには、法線マップとしてバイアス化したアートを使用する必要があります。
  • _sat (クランプ) が自動的に使用されるということは、符号付きの結果は生成されないということを意味します。

図 4. ピクセル単位のディフューズ ライティング

第 2 の代替テクニックは、エンボス バンプ マッピングです。このテクニックのための唯一のハードウェア要件は、減算演算をサポートするデュアル テクスチャ ユニットです。これは D3DTEXOPCAPS_SUBTRACT 能力ビットの存在によって確認できます。ここでも、固定機能マルチテクスチャ パイプラインを使用するときには、ValidateDevice() を使うのがよいでしょう。エンボス バンプ マッピングは、高さマップをライト ベクトルの向きにシフトし、ベース マップから減算を行うことによって動作します。きわめて説得力のある結果が得られますが、微調整にはかなりの労力が必要になることがあります。次に、典型的なエンボス演算のための頂点シェーダのコード例を示します。


          //v0 = 位置
//v3 = 法線 (wベクトル)
//v7 = テクスチャ座標
//v8 = 接線 (uベクトル)

vs.1.1         // エンボス
m4x4 oPos, v0,c08      // 出力位置を生成

//diffuse
m3x3 r3, v8, c0       // 接線を接空間にトランスフォーム
m3x3 r5, v3, c0      // 法線を接空間にトランスフォーム

mul r0,r3.zxyw,r5.yzxw  // 外積によって従法線を生成
mad r4,r3.yzxw,r5.zxyw,-r0

dp3 r6.x,r3,c12      // 接空間のライトを r6 に
dp3 r6.y,r4,c12
//   dp3 r6.z,r5,c12  これは不要 
//                    x と y のシフトのみが意味を持つ

// ライトの方向をもとにしてテクスチャをセットアップ
mul r1.xy,  r6.xy, -c24.xy
mov oT0.xy, v7.xy         // ベース ハイト マップをコピー 
add oT1.xy, v7.xy,  r1.xy    // 法線ハイト マップをオフセット

// 単純な内積によってグローバルに暗くするエフェクトを得る
dp3 oD0.xyz,v3.xyz,c12.xyz

        

次に示すのは、D3DTOP_ADDSIGNED 固定機能演算のためのマルチテクスチャ状態のセットアップです(やはりエフェクト フレームワーク構文を使用しています)。ここでは、補数入力引数修飾子フラグを使用しています。


          ColorOp[0]   = SelectArg1;
ColorArg1[0] = Texture;

ColorOp[1]   = AddSigned;
ColorArg1[1] = Texture | Complement;
ColorArg2[1] = Current;
      
VertexShaderConstant[0]  = World Matrix (transpose)      
VertexShaderConstant[8]  = Total Matrix (transpose)
VertexShaderConstant[12] = Light Direction; 
VertexShaderConstant[24] = Offset Constant

Texture[0]   = base height map;
Texture[1]   = normal height map;

        

ミップマッピングとフィルタリングも設定する必要がありますが、スペース上の制限から、これらの設定は無視することにします。まとめると、エンボス スタイル バンプ マッピングは、内積マルチテクスチャ演算子をサポートしていないハードウェアでの、ディフューズの代替テクニックとして使うことが可能です。これには大部分の DirectX 6.x 世代のカードが含まれ、インストール ベースの大きな部分を占めています。このテクニックで理想的な結果を得るためには、アートワークを修正し、テクスチャの輝度をロード時に高める必要があります。別の方法として、 D3DTOP_MODULATE2X演算子を使って結果のスケールアップを行うこともできます。これには、輝度を高めるというビジュアルな効果があります。また、このテクニックでは法線マップよりも簡単にフィルタリングを適用できるので、内積演算をサポートしているハードウェア上であっても、内積マルチテクスチャ テクニックよりも優れた結果が得られる可能性があることにも注意してください。

ピクセル単位のスペキュラは、ディフューズに似ていますが、ピクセル シェーダを必要とします。ライトの方向の代わりに、補間されたハーフベクトル H を使用します。これは頂点シェーダの中で計算します。ピクセル シェーダの中では、H とピクセル法線の内積を計算した後に、事前に決定した値で累乗します。スペキュラの結果は、他のパスに加えられます。また、ピクセル シェーダの出力値は、r0 の 1 つだけなので、スペキュラの結果を r0 に加えるのを忘れないようにしてください。

この時点で、1 つの疑問が生じるかもしれません。累乗計算はどのようにして実行するのか、ということです。ここでは、乗算ベースとテーブル ベースの 2 つのテクニックが使用されます。一方は比較的単純で、小さな指数に適しており、もう一方は大きな指数に適しています。ここで取り上げる両テクニックでは、次のレンダ状態 (エフェクト フレームワーク構文) を使用しています。


          VertexShaderConstant[0] = World Matrix
VertexShaderConstant[8] = Total Matrix
VertexShaderConstant[12] = Light Direction
VertexShaderConstant[14] = Camera Position (World)
VertexShaderConstant[33] = (.5f,.5f,.5f,.5f)

Texture[0]   = normal map
Texture[1]   = color map

        

では、2 つのピクセル シェーダ スペキュラ テクニックについて説明しましょう。まず、乗算ベースの累乗計算のテクニックを取り上げます。次に示す頂点シェーダのコードは、(接空間でのライトの方向を計算し、ピクセル単位の内積のためにバイアシングを行い、ピクセル シェーダのためにテクスチャ座標をセットアップするというディフューズのアクションに加えて)視点の方向とライトの方向を使ってハーフ ベクトルを計算し、乗算ベースの累乗計算に使用される内積計算のためのスケール/バイアスを行っています。


          vs.1.1
//位置のトランスフォーム
m4x4 oPos,v0,c4
     
//tsbの生成     
m3x3 r3,v8,c0            //gen normal
m3x3 r5,v3,c0            //gen tangent

//外積によって従法線を生成
mul r0,-r3.zxyw,r5.yzxw;
mad r4,-r3.yzxw,r5.zxyw,-r0;

//スペキュラ
m4x4 r2,v0,c0            //位置のトランスフォーム
//カメラに向かうベクトルを計算
add r2,-r2,c24

dp3 r11.x,  r2.xyz,r2.xyz   //2 乗を r11 に格納
rsq r11.xyz,r11.x         //2 乗の逆数を計算
mul r2.xyz, r2.xyz,r11.xyz   //乗算、r0 = -(カメラ ベクトル)
add r2.xyz,r2.xyz,-c16      //ハーフベクトルを計算
   
//正規化
dp3 r11.x,r2.xyz,r2.xyz      //2 乗を r1 に格納
rsq r11.xyz,r11.x         //2 乗の逆数を計算
mul r2.xyz,r2.xyz,r11.xyz   //乗算、r2 = ハーフベクトル

//ハーフベクトル ベクトルをトランスフォーム
dp3 r8.x,r3,r2
dp3 r8.y,r4,r2
dp3 r8.z,r5,r2
     
//ハーフアングルを oD1 に格納 
mad oD1.xyz,   r8.xyz,c20,c20   //ハーフを掛け、ハーフを加える  
      
//テクスチャ座標     
mov oT0.xy,   v7.xy
mov oT1.xy,   v7.xy

        

次のピクセル シェーダのコードは、乗算ベースの累乗計算のテクニックを使用したピクセル単位のスペキュラを示しています。


          ps.1.1            // 乗算の結果による pow2
tex t0            // カラー マップ
tex t1            // 法線マップ

//  スペキュラ ライティングの内積
dp3_sat r0,t1_bx2,v1_bx2   // t0とv1のバイアシング(ライト カラー)

mul r1,r0,r0            //  2 乗   
mul r0,r1,r1            //  4 乗     
mul r1,r0,r0            //  8 乗     
mul r0,r1,r1            // 16 乗!

//最終的なカラーのアセンブル
mul r0,r0,t0         // カラー マップによる乗算
        

_bx2 修飾子の使用方法に注目してください。これは、範囲 [-1, 1] に制限されている処理系で起こることのあるオーバーフロー クランピングの前に、(スペキュラ計算で使用される)ダイナミックレンジを確保しながらも、入力データを符号付きの数値として処理できるようにするために使用しています。図は、乗算ベースのスペキュラ累乗計算を使って生成されたイメージを示しています。ハイライトの中のガタガタに注目してください。これは、個々の結果チャネルが 8 ビットに制限されているため、計算の過程で精度が失われたことが原因です。高精度のテクスチャ形式と高精度の内部計算を使用すれば、イメージの品質が向上します。まとめると、乗算ベースのピクセル単位のスペキュラは、実装は簡単ですが、精度の問題が生じる可能性があるので、16 乗以上の指数の場合は、このテクニックを使用するべきではありません。ただし、より精度の高いグラフィックス チップでは、これは問題にならないこともあります。

次のテクニックは、テーブル ルックアップ ベースのスペキュラの累乗計算です。ここで使用する例は、この演算を 3x2 のテーブルを使って実行します。テクスチャは、関数 y = pow (x) を格納した指数のテーブルとして使用されます。

図 5. 乗算ベースのスペキュラの累乗計算

このテクニックも、texm3x2tex命令の依存テクスチャ読み込み機能を使用します。3x2 の乗算は 2 つの内積でもあるので、このテクニックはスペキュラとディフューズを、または 2 つの光源を同時に処理することができます。次に、このテクニックのための頂点シェーダを示します。


          vs.1.1
//位置のトランスフォーム
m4x4 oPos,v0,c4
     
//tsbの生成     
m3x3 r3,v8,c0            //法線のトランスフォーム
m3x3 r5,v3,c0            //接線のトランスフォーム

//外積
mul r0,-r3.zxyw,r5.yzxw;
mad r4,-r3.yzxw,r5.zxyw,-r0;
      
//スペキュラ
m4x4 r2,v0,c0            //位置のトランスフォーム

//カメラに向かうベクトルの計算
add r2,-r2,c24

dp3 r11.x,r2.xyz,r2.xyz   //2乗をr11にロード
rsq r11.xyz,r11.x         //2乗の逆数を計算
mul r2.xyz,r2.xyz,r11.xyz //乗算、r0 = -(カメラ ベクトル)

add r2.xyz,r2.xyz,-c16    //ハーフベクトルを計算
   
//正規化
dp3 r11.x,r2.xyz,r2.xyz   //2乗をr1にロード
rsq r11.xyz,r11.x         //2乗の逆数を計算
mul r2.xyz,r2.xyz,r11.xyz //乗算、r2 = ハーフベクトル

//ハーフベクトルをトランスフォーム
dp3 r8.x,r3,r2
dp3 r8.y,r4,r2
dp3 r8.z,r5,r2
     
//テクスチャ座標             
mov oT0.xy, v7.xy         //テクスチャ座標ではなく
mov oT1.xyz,r8            //ハーフベクトルからの
mov oT2.xyz,r8            //法線のサンプリングを行うための座標
mov oT3.xy, v7.xy
        

テーブル ルックアップ頂点シェーダは、ハーフベクトル正規化の計算までは、乗算ベースの頂点シェーダと同じです。それ以降、このテクニックはテクスチャ座標を使って、ベクトルと、カラーおよび法線マップのインデックス付けに使用される真のテクスチャ座標を渡します。ハーフベクトルは、ステージ2のためのテクスチャ座標として渡され、ステージ1のテクスチャ座標がライトの方向を渡すために使用され、ステージ 0 とステージ 3 のテクスチャ座標がそれぞれ法線マップとカラー マップに使用されます。

次に、3x2 のテーブル ルックアップ スペキュラ ライティングのピクセル シェーダを示します。


          ps.1.1         // テーブル ルックアップによる累乗計算

//  texcoord t1      // ディフューズ ライトの方向
//  texcoord t2      // ハーフベクトル ベクトル
//  ステージt2のテクスチャはテーブル ルックアップ関数

tex t0         // 法線マップのサンプリング
texm3x2pad t1, t0_bx2     // 乗算の結果の第1行、第1の内積 u
texm3x2tex t2, t0_bx2     // 乗算の結果の第2行、第2の内積 v

//最終的なカラーのアセンブル
mov r0,t2         //上記の(u,v)を使って強度を得る
mul r0,r0,t3            //ブレンド項

        

このシェーダの重要な点は、texm3x2tex 命令と texm3x2pad 命令を使って、従属読み込みを行っていることです。texm3x2pad 命令は、他のテクスチャ アドレス演算子と組み合わせて、3x2 行列の乗算を行うために使用されています。これは、テクスチャ座標のみが使用されるステージの表現に使用されるので、このステージにはバインドされたテクスチャは存在しません (このシェーダでは t1)。入力引数の t0 は依然として指定する必要があります。

texm3x2pad 命令は、指定された入力カラー(ここでは t0)に、それ以降のステージ(ここでは t1 )のテクスチャ座標(u、v、およびw)をかけて、乗算の結果(内積)の第 1 行を計算し、u 座標を生成します。その後、texm3x2tex 命令が、指定された入力カラー(やはり t0)と、指定されたステージ(ここでは t2)のテクスチャ座標を使って、乗算の結果 (やはり内積) の第 2 行を計算して、v 座標を生成します。最後に、このステージのテクスチャ(ここでは t2)を使って、(u,v) での依存読み込みにより、テクスチャのサンプリングを行って、最終的なカラーが得られます。

残る問題は、ルックアップ テーブル テクスチャをどのように生成するかということです。D3DX を使用すると、次のコードによって、テーブル ルックアップ テクスチャを生成することができます。


          void LightEval(D3DXVECTOR4 *col, D3DXVECTOR2 *input
                         D3DXVECTOR2 *sampSize, void *pfPower)
{
    float fPower = (float) pow(input->y,*((float*)pfPower));
    col->x = fPower;
    col->y = fPower;
    col->z = fPower;
    col->w = input->x;
}

D3DXCreateTexture(m_pd3dDevice, 256,256, 0,0,
   D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &pLightMap100);

float fPower = 100;
D3DXFillTexture(m_pLightMap100,LightEval,&fPower);

        

下の図 6 は、テーブル ルックアップ スペキュラの累乗計算を示しています。注意深く見ると、ガタガタが小さく、より高品質のイメージが得られていることがわかります。このテクニックは 100 以上の指数をサポートしており、一部のビジュアル エフェクトにとっては重要です。テクスチャ読み込みユニットの精度は高く、テーブル ルックアップ テクスチャはきれいにフィルタリングされるので、バンディングは許容可能なレベルに抑えられます。累乗以外にも他の関数を使用できることに注意してください。

図 6. テーブル ルックアップ スペキュラの累乗計算

固定機能マルチテクスチャを使った代替テクニックは、スペキュラのエンボスに相当し、高さフィールド法線マップからライト マップ ハイライトを減算します。スペキュラ ハイライトの位置指定は手作業で行う必要がありますが、これは頂点シェーダでは難しいことではありません。その後、ピクセル シェーダの中で減算を使って値を結合し、その結果をピクセル単位のスペキュラ項として加えます。図 7 は、2つのテクスチャの図と、減算の結果で希望するエフェクトを得る様子を示しています。

図 7. スペキュラ エンボスの代替テクニックの図

スペース上の問題から、スペキュラの代替テクニックの実装は省略します。読者はすでに概要を理解していることでしょう。これで、標準のディフューズおよびスペキュラのピクセル単位のライティング モデルと、それらをピクセル シェーダで実現する方法についての解説は終わりです。次は、"do-it-yourself" のライティング モデルを取り上げ、独自のカスタム ライティング モデルを開発して実装する方法を説明します。

カスタムのピクセル単位のライティング

従来のテクニック、すなわち標準のライティング モデルの解説をひとまず終えて、次にカスタムの "do-it-yourself" ライティング モデル、または DIY ライティングの説明に進むことにしましょう。ピクセル シェーダ ハードウェアのいいところは、開発者を過去のライティング モデルから解放し、カスタム ライティングの新しい世界への道を開いてくれることです。過去のライティング モデルは、誰かが過去のいずれかの時点で考え付いた、ライティングの物理学をモデル化するという仕事をそこそここなしてくれる方程式のセットでしかありません。別に、魔法が含まれているわけではないのです。ですから、基本的なライティング モデルに縛られるべきではありません。ディフューズとスペキュラは簡単に計算することができ、一般論として許容可能な結果を生成してくれますが、全体的にどれも似通った結果を生成するという短所を持っています。高度なライティングという目標に沿って言えば、似たように見えるということは大きな欠点なのです。

では、DIY ライティングとは何なのでしょうか? これは、基本的なライティング モデルに縛られることなく、創造性と、基本的なライティング タスクの原理に関する知識を活用するということを意味します。SIGGRAPH の文献、ハードウェア ベンダのWebサイト、およびゲーム エンジンのレンダリング アプローチに関する情報など、基本的なディフューズおよびスペキュラ ライティング モデルへの興味深い代替アプローチのガイドとして利用できる資料は大量に存在します。

鍵となるのは、基本的なライティング モデルのプロセスを理解し、ライティング モデルの開発にあたって何らかのプロセスを利用するということです。これにより、ライティングのアイデアを評価するための一貫性のある基盤を確立することができます。ここでの説明はローカル ライティングに焦点を当てており、減衰エフェクト、大気エフェクトなどはスペースの関係上扱っていません。

基本的なライティング モデルのプロセスは、ライティングの物理学に基づいています。図 8 は、計算によって再現しようとする物理モデルを示しています。光を生成する光源があり、入射光のエネルギーを受け取って反射光を発するサーフェスのジオメトリ(入射および反射ジオメトリと呼ばれます)、ライティングを記録するカメラがあります。

図 8. ライティング プロセス

入射角と反射角は、ライティングの計算において重要な役割を果たします。ジオメトリと光の相互作用は、光が入射し、反射するサーフェスの物理的特性を定義するサーフェス モデルによっても制御されます。サーフェス モデルに使用できる要素にはさまざまなものがあります。最もよく使われるモデルは、拡散光の項を計算するための Lambert モデルです。Lambert モデルは、サーフェス上のマイクロファセットを定義し、入力エネルギーと、光の方向に垂直なマイクロファセットの面積をもとに光の強度を計算します。Lambert ディフューズは内積を使って簡単に計算できます。図 9 は Lambert モデルを示しています。ここでのキー ポイントは、計算のベースとなっている、このモデルの物理的な基盤です。

図 9. Lambert モデル

基本的なライティング モデルでの動作を理解した上で、"DIY" ライティングの内容を調べていきましょう。単純な "DIY" の評価プロセスの第一段階には識別の段階があります。ここでは、個々のシーンでどのような主なエフェクトを生成したいかを分析します。その後、これらに優先順位を付けます。これを終えたら、そのデータをもとに、個々のシーンとエフェクトのライティング環境の特徴を決定します。

これらのエフェクトには、光源はいくつ必要か? それらはどのような形状か? どのような影と反射が生じるか? さて、主要なエフェクトの識別と特徴付けが終わったら、エフェクトを実装するアルゴリズムを生成する必要があります。これらのアルゴリズムでは、最終結果に重要な寄与を行う項が残され、イメージの品質に大きく寄与しない項は削除されます。一般に、これを判断するためには実験が必要となりますが、ケースによっては、計算を行うだけで、ある項の寄与に意味があるのかどうかを予想できることもあります。一般に、個々の部分を個別に定義してテストすることができますが、いずれかの時点では、すべての部分を結合して、モデルの「最終的」な評価を行わなくてはなりません。また、このプロセスの中では、個々の計算部分がオブジェクト ベース(オーサリング時の計算)、頂点ベース(頂点シェーダ時の計算)、ピクセル ベース(ピクセル シェーダ時の計算)のいずれなのかを決定する必要もあります。

最後に、モデルへの入力として使われる値の範囲を理解し、出力値の予想される範囲を理解することで、等価な代替計算を見つけられることがあります。この代替計算は、概念的により単純である、コストの面で単純である、または単に十分な結果を得られるなどの性質を持っているものです。これは重要なポイントであり、習慣に縛られることなく、数学的なオープン マインドを持って、チャンスがあれば積極的に活用するようにしてください。

さて、これらを念頭に置きながら、単純だが効果的な "DIY" ライティング モデルのプロセスを紹介していきましょう。ここではローカル ライティングに焦点を当て、減衰エフェクトや大気エフェクトなどは考慮に入れません。ここで示すモデルは、エリアまたは分散ライティング エフェクトのバリエーションです。これは基本的にはディフューズ エフェクトですが、より興味深いアンビエント項が入っています。「シャドウ ディテール」を持ち、世界が与える影響の要因を考慮に入れることで、物体が実世界のシーンの中にあるという印象を生み出します。これは本質的に、イメージ ベースのライティング テクニックです。

図 10 は、このモデルのターゲット シーンを示す Lightwave 3D によるイメージです。モデルがターゲットにどれだけ近づくかが、モデルの評価基準となります。多くのレイ トレース ツールは似たようなモデルをサポートしており、レンダラが各ポイントで統合を行い、マイクロファセット ピクセルから半球のすべてのポイントにレイをキャストし、同じオブジェクトと環境マップ イメージの他の部分からのレイ インターセクションからのカラーを累積します。この処理には数時間かかることがあります。ここでの目的は、この品質にかなりのレベルまで近いイメージをリアルタイムで得ることです。

図 10. DIYモデルを評価するためのターゲット イメージ

図 11 に示す、ここで使用する分散ライティング モデルは、大きな面光源を使用します。単一の方向ベクトルがあるわけではありません。エネルギーは方向の数、すなわちマイクロファセットを照らす、可能な方向の数の比率によって決定されます。このモデルを屋外のシーンで使用する場合、光源は太陽と空の 2 つになります。太陽は、鋭い影を投じる、指向性を持つ光源です。空は、柔らかい影を投じる、無指向性の光源です。面光源を物体を囲む半球と見なせば、シーン内の物体のライティングは、半球の面積のどれだけの比率が物体に光を投じることができるかを考えるだけで済むようになります。

図11. 分散ライティング モデル

図 12、13、および 14 は、これを立方体、半球、および球について適用した様子を示しています。この図から、このモデルを使用するシーン内の物体がどのように照らされるかが簡単にわかるでしょう。図 12 は立方体を含んでおり、ライティングの強度な上の面で最も大きく、側面では徐々に低下します。図 13 は半球を含んでおり、ライティングの強度は上部の極の領域で最も大きく、赤道に向かって低下していきます。図 14 は球を含んでおり、ライティングの強度はやはり上部の極の領域で最も大きく、下部の極の領域に向かって低下していきます。

図 12. 立方体の「半球」ライティング

図 13. 半球の「半球」ライティング

図 14. 球の「半球」ライティング

マイクロファセットの平面近くのライトは、エネルギーの寄与が小さいので、フォーム ファクタの cos(q) の項を使用して、エネルギーのスケールダウンを行うことができます。このモデルの放射項にL = 1/p S cos(q) dd を統合することで、光源はファー フィールドとなります。通常のテクニックでは、環境マップを統合して、この項を得ます。この計算は DirectX 7 クラスのハードウェアでも可能です。

図 15 は、キューブ マップとその対応する合成を示しています。環境マップ 合成は、主に空の色と地面の色の 2 つであることに注目してください。正確な合成の計算は対話型のアプリケーションでは時間がかかりすぎるので、オーサリング時のプロセスが必要になること、また合成がゲーム内で変化してはならないことがわかります。これは理想的とは言えませんが、どうしようもありません。

図 15. キューブマップとその合成

これを二半球モデルと呼ぶことにしましょう。このモデルは、いまの時点では、空と地面の 2 つの重要な項のみの計算を行うモデルとして理解できます。図16はこの2半球モデルを示しており、図 17 はそのプロセス ブロック図です。

図16 二半球モデル

図17. 二半球モデルの要素

実際の合成は次のようになります。

color = a*Skycolor+(1-a)*GroundColor

ただし

a = 1-0.5*sin(q) for q<90

a = 0.5*sin(q) for q > 90

または、次の、より単純な形式が使用されます。

a = 0.5+0.5*cos(q)

単純化された合成と実際の合成の関係を図 18 に示します。曲線の形状は同じだが、鏡像関係にあること、また各曲線の下に同じ面積の明るい部分と暗い部分が存在することに注目してください。正確に一致するわけではありませんが、全体的な等価関係があります。

図 18. 合成の比較

ここに、シェーダの秘密の 1 つがあります。全体的に等価であれば、似たような計算に置き換えても大丈夫なのです。リアルタイムのフレーム レートでは、イメージ品質の小さな差は通常は気づかれないからです。この単純化によってパフォーマンスが十分に向上するのであれば、こちらの方を選んでもかまいません。このケースでは、dp3mad を使用するので、単純化には 2 クロックかかります。ビジュアル的に同一のものではありませんが(この解では、赤道に沿ってバンプのディテールが多く、光に面しているところではバンプのディテールが少なくなります)、実際の計算はリアルタイムで行うのには遅すぎるのに対し、2 クロックのコストしかかけずに目的のエフェクトを十分なレベルで生成してくれます。これは、クロック数の点でも、また DIY ライティングとシェーダ計算の本質と利点を理解するという点でも、大きな長所となります。

2 項の計算を突き詰めると、入射エネルギーのどれだけの比率がどのカラーを持っているかということになります。遠いフィールドの半球は、空と地面の 2 つの色を単純な比率で含んでいます。この例のように、環境が 2 つの色に単純化されているとしても、このモデルでは動的なアップデートが可能です。たとえば、車が谷やトンネルに入り、そこから出るというような場面で、舗道や地面の色、あるいは空や屋根の色を変えることができます。半球の実装は、頂点単位とピクセル単位のどちらでも行うことができます。

頂点単位の実装は、半球の軸をライトの方向とし、標準の接空間基底の頂点シェーダを使って、半球軸を接空間にトランスフォームします。頂点シェーダの実装は次のようになるでしょう。


          vs.1.1         // 頂点半球シェーダ
 
m4x4 oPos,v0,c8   // 位置のトランスフォーム
m3x3 r0, v3,c0   // 法線のトランスフォーム
 
dp3 r0,r0,c40   // c40は空のベクトル
mov r1,c33      // c33はすべてのチャネルの.5f
mad r0,r0,c33,r1   // バイアス操作

mov r1,c42      // c42は地面の色
sub r1,c41,r1      // c41は空の色
mad r0,r1,r0,c42   // lerp操作

//c44 = (1,1,1,1)
sub r1,c44,v7.zzz   // v7.zzz = 遮蔽項
mul r0,r0,r1
mul oD0,r0,c43

        

ピクセル単位のフラグメントの実装は次のようになるでしょう。


          // v0.rgbは接空間での半球軸
// v0.aは頂点シェーダのocclusionレシオ
tex t0         // 法線マップ
tex t1         // ベース テクスチャ

dp3_d2_sat r0,v0_bx2,t0_bx2   // 法線と半球軸の内積
add r0,r0,c5      // _satではなく、範囲へとマップ
lrp r0,r0,c1,c2
mul r0,r0,t1      // ベース テクスチャの乗算

        

さて、実際にはどのようなイメージが得られるのでしょうか? 図 19 は 2 項アプローチの結果を示しています。これは興味深い結果ですが、いくつかの問題があります。2 つのカラーの組み合わせには成功していますが、眼窩、鼻腔、歯の裏など、明らかに光が多すぎる領域がいくつかあります。

図 19. 2 項の DIY イメージ

では、DIY モデルを改良に着手しましょう。モデルを改良するにはどうすればいいでしょうか? もちろん、新しい項を追加することによってです。どんな項を追加するべきでしょうか? 最初の試みでは、オブジェクトのセルフ シャドウイングを考慮に入れていませんでしたが、結果として得られたイメージが、一部の領域で必要以上に明るくなっていたのはこれが理由でした。したがって、遮蔽項を追加する必要があります。

図 20 は、このアップデートされた DIY ライティング モデルのブロック図を示しています。この計算は、各法線から半球状のレイを発し、結果を頂点カラーとして格納することで頂点ベースで行うこともできますし、ハイト フィールドでレイを発し、結果を法線マップのアルファ チャネルに格納することでピクセル ベースで行うこともできます。また、頂点とピクセルから両方のレイを発し、結果をテクスチャ マップに格納することもできます。

図 20. アップデートされた DIY モデルの要素

ここで示すサンプルは、オフライン レンダリング プロセスを使って、この 遮蔽データを計算しています。頂点ベースのケースでは、この計算は「隣接するポリゴンは互いにどれだけの影を投げかけるか」という質問への答えを提供します。その結果を頂点属性に格納し、オブジェクト レベルでのエフェクトを処理することができます。隣の頂点のみを見るだけで十分だと思われます。

ピクセル ベースのケースでは、この計算は「隣接するピクセルは互いにどれだけの影を投げかけるか」という似たような質問への答えを提供します。例としては、バンプ マップされた地球があります。球はあらゆる箇所で凸形なので、ジオメトリに自己遮蔽はありません。これは、すべての 遮蔽をバンプ マップで行えるということを意味します。図 21 は、3 項の DIY ライティング モデルで得られたイメージを示しています。眼窩、鼻腔、および口の中の問題エリアの見栄えがはるかによくなっており、大きく改善されています。

図 21. 3 項の DIY ライティング モデルのイメージ

最後に、これを図 22 に示す指向性のライトと組み合わせれば、このように比較的単純なライティング モデルから驚くほどリアリスティックなイメージができあがります。

図22. 3 項の DIY ライティング モデルのイメージ

このように、DIY ライティング モデルの定義、評価、および改良は反復的なプロセスですが、この記事で示したようにそれほど難しいものではありません。目的の効果を得るためにどのような種類の照明が必要かを明確に考え、十分に見栄えが良くなるまで繰り返しを行うだけです。

ここには 2 つの教訓があります。第一に、DOT3 ベースのディフューズおよびスペキュラ ライティングを正しく行えるように接空間基底を理解し、第二に、"do-it-yourself" ライティングを行う方法を理解するということです。どちらの教訓も重要です。DIY ライティングは接空間基底の正しい使用方法に関する知識をベースにしていますが、真に重要なのは「ライティングのブラック ボックス」とそれとその機能上の等価物を理解し、少しだけ違う外見を持つイメージを作りたいときに、独自のライティングのアプローチを安心して使えるようにすることなのです。

最後に

本コラムの執筆にあたっての Chas Boyd、Dan Baker、Tony Cox、および Mike Burrows (Microsoft) の協力に感謝します。またLightwave 3D のイメージについては NewTek に、モデルについては Viewpoint Datalabs に感謝します。

ご意見をお寄せください。感想、質問、トピックに関するアイデア、コラムのトピックに関しての皆さんのやり方を示すリンクなどを (日本語で) directxj@microsoft.com まで送ってください。ただし、個別に返答したり、サポートに関する質問に回答したりすることはできませんので、あらかじめご了承ください。

Microsoftでは、同じような方向性を持った開発者が情報を共有できるようなフォーラムとして、活発なメーリング リストを運営しています (英語)。

Philip Taylor は DirectX SDK、Managed DirectX、Windows XP 3D スクリーンセーバーなどのプログラム マネージャです。 DirectX 3.0 から DirectX 8.0 までの DirectX エバンジェリズム グループのシニア エンジニアとして活躍し、多数のゲーム開発会社に対して DirectX 関連のサポートを行いました。GameSDK (DirectX 1.0) の最初のパブリック ベータの段階から手を染め、かつては実際に DirectX 2 でゲームを開発したこともありました。 時間に余裕があるときは、さまざまな 3-D グラフィックス プログラミング メーリング リストで彼に出会うことがあるかもしれません。

Driving DirectX コラム アーカイブ