This is the documentation for Enlighten.

7.1.1.2.4. プローブ ポイントのための解決


概要

Enlighten バージョン 2.21 以降、ライト プローブ値の解決方法は 2 つあります。RadProbeTask 構造体を使用する従来の Probe Solver と、EntireProbeSetTask 構造体を使用する新しい Entire Probe Set Solver です。

古い Probe Solver と比較して、 Entire Probe Set Solver で必要なプリコンピュートされたデータは大幅に圧縮されており、プローブ位置の一般的な正規グリッドのデータ サイズの 25~30% です。L1 と L2 出力に異なるデータをプリコンピュートする必要がある従来の Probe Solver とは違い、Entire Probe Set Solver では L1 と L2 球面調和関数の実行時に解決する際に同じデータを使用できます。また、実行時間も大幅に短縮されており、セットのすべてのプローブを解決する時間は従来の約 60% です。さらに、一時的なコヒーレンス最適化が Entire Probe Set Solver に実装されているため、ライティングの状態が変化していない場合に冗長な繰り返し計算をスキップできます。

トレードオフとして、Entire Probe Set Solver では個別のプローブを解決することができません。

データ ブロックのプリコンピュート

Enlighten プリコンピュートは、従来の Probe Solver と新しい Entire Probe Set Solver の両方でデータ ブロックを生成します。これはどちらも RadProbeSetCore クラスのメンバーです。

//--------------------------------------------------------------------------------------------------
/// プローブ セットの主要なプリコンピュートされたデータを構成します。
//--------------------------------------------------------------------------------------------------
class RadProbeSetCore
{
public:
    /// プローブセット、そのサイズ、要件、他システムとの関係性を記述するデータ
    RadProbeSetMetaData     m_MetaData;
 
    /// プローブ ラジオシティ ライティングに必要なプリコンピュートされたデータ
    RadDataBlock            m_ProbeSetPrecomp;
 
    /// プローブセット全体を自動的に解決し、
    /// オプションの一時的なコヒーレンス最適化を備えた「プローブ セット ソルバー」用のプリコンピュートされたデータ ブロック
    RadDataBlock            m_EntireProbeSetPrecomp;

これらのデータ ブロックのうち、ロードする必要があるのは 1 つだけです。従来の Probe Solver を使用する場合、m_ProbeSetPrecomp データ ブロックだけをロードします。逆に、Entire Probe Set Solver を使用する場合、m_EntireProbeSetPrecomp データ ブロックだけをロードします。

Probe Solver へのライティング データの提供

入力をプローブ タスクに渡すメカニズムはイラディアンス タスクと同じであり、入力ライティングバッファ ポインターの順序付けられたリストを渡します。このリストを準備するためのヘルパー関数があります。

Entire Probe Set Solver を使用する際は、RadDataBlock ポインターを取り、m_EntireProbeSetPrecomp ブロックを渡すバージョンを使用する必要があります。(RadProbeSetCore ポインターを取る古いバージョンでは、従来の Probe Solver 向けの結果を返すか、データ ブロックがロードされていない場合は失敗します。)

/// このコア データを使用してラジオシティを解決する際に求められる入力ライティング バッファ リストの長さを返します。
Geo::s32 GEO_CALL GetInputWorkspaceListLength(const Enlighten::RadDataBlock* dataBlock);
 
/// 想定される入力ライティング バッファ リストの特定のエントリーの GUID を返します。
Geo::GeoGuid GEO_CALL GetInputWorkspaceGUID(const Enlighten::RadDataBlock* dataBlock, Geo::s32 index);
 
/// ライティング バッファの順序付けられていないリストをソルバー向けに正しい順序にします。
bool GEO_CALL PrepareInputLightingList(const Enlighten::RadDataBlock* dataBlock,
                                       const Enlighten::InputLightingBuffer** inputLightingBuffers,
                                       Geo::s32 numLightingBuffers,
                                       const Enlighten::InputLightingBuffer** listILBOut);

例 - Probe Solver

以下の簡素化された例では、従来の Probe Solver を使用してプローブ セットの最初のインデックスを解決します。ここでは、入力インデックスのリストと出力ポインターのリストは非常にシンプルです。現実的なユースケースでは、再度ライティングしたい動的オブジェクトのワールドでの位置に基づき、計算するインデックスを決定する必要があります。

RadProbeTask probeTask;
probeTask.m_CoreProbeSet = probeSetCore;
probeTask.m_InputLighting = GEO_NEW_ARRAY(const InputLighitngBuffer*, Enlighten::GetInputWorkspaceListLength(probeSetCore));
Enlighten::PrepareInputLightingList(probeSetCore, const_cast<const InputLightingBuffer**>(inputLightingBuffers), numSystems, probeTask.m_InputLighting);
 
// 解決したいインデックスのリストを作成します。
 
s32 indexList[1] = { 0 };
probeTask.m_NumIndicesToSolve = 1;
probeTask.m_IndicesToSolve = indexList;
// 入力インデックスのリスト - わずか 1 エントリーです。 
 
// 出力のスペースを確保します。
// SolveProbeTaskL1 は、インデックスあたり 12 の浮動小数点仮数を生成します。
float shOutput[12];
float* pShOutput = shOutput;
probeTask.m_OutputPointers = &pShOutput;
// 出力ポインターのリスト - わずか 1 エントリーです。 
 
// Enlighten にライト プローブ データを計算するよう指示します。
bool solveStatus = Enlighten::SolveProbeTaskL1(&probeTask);

例 - Entire Probe Set Solver

EntireProbeSetTask probeTask;
probeTask.m_CoreProbeSet = probeSetCore;
probeTask.m_InputLighting = GEO_NEW_ARRAY(const InputLighitngBuffer*, Enlighten::GetInputWorkspaceListLength(probeSetCore));
Enlighten::PrepareInputLightingList(&probeSetCore->m_EntireProbeSetPrecomp, const_cast<const InputLightingBuffer**>(inputLightingBuffers), numSystems, probeTask.m_InputLighting);probeTask.m_Environment = <pointer to an input lighting buffer for the emissive environment>
 
probeTask.m_OutputShOrder = <Enlighten::SH_ORDER_L1 or Enlighten::SH_ORDER_L2>          // L1 と L2 SH のどちらを解決するかを選びます
probeTask.m_Output = <pointer to an array of 12 floats per probe for L1 or 27 for L2>   // このポインターを float の出力用に提供します
probeTask.m_U8Output = NULL;                                                            // U8 出力であればこのポインターを提供します (L1 に対してのみ有効)
probeTask.m_U8OutputScale = 1.0f;                                                       // U8 を使用する場合、これで範囲を変更します 
 
// これらのパラメーターを設定し、一時的なコヒーレンス最適化を有効にします
probeTask.m_TemporalCoherenceThreshold = 0.01f;
probeTask.m_TemporalCoherenceBuffer = <a persistent buffer of length probeSetCore->m_MetaData.m_RequiredTemporalCoherenceBufferSize>
 
// このワークスペースは、SolveEntireProbeSetTask から常に要求されます
void* workspace = <a buffer of length probeSetCore->m_MetaData.m_RequiredWorkspaceSize>
 
bool solveStatus = Enlighten::SolveEntireProbeSetTask(&probeTask, workspace timeUs, numSolvedProbes);

一時的なコヒーレンス最適化を使用した場合、ライティングの変化を見逃さないためにすべてのプローブ セットを同期しておく必要があります。それでも、solve の代わりに Freeze 関数を使用することで一部のプローブ セットの更新頻度を下げることができます。FreezeEntireProbeSetTask は、一時的な最適化に必要なライトの変化を追跡するために必要な、最小限のハウスキーピング処理を実行しますが、出力 SH 係数は更新されません。入力パラメーターは、SolveEntireProbeSetTask のものとまったく同じです。

bool solveStatus = Enlighten::FreezeEntireProbeSetTask(&probeTask, workspace timeUs, numSolvedProbes);

圧縮された出力

プローブ ボリュームの計算は浮動小数点を使って行われますが、最終的な解にそこまでの精度を常に維持する必要があるわけではありません。L1 ソルバーのメモリ要件を低減するため、u8 のアレイをスケール値 (BounceScale に類似) とともに probeTask に渡すことができます。その他の設定と関数呼び出しは影響を受けません。圧縮エンコーディングの詳細については、ライト プローブの評価をご覧ください。

// 出力のスペースを確保します。
// SolveProbeTaskL1/SolveEntireProbeSetTask は、インデックスあたり 12 の u8 係数を生成します。
Geo::u8 shOutput[12];
Geo::u8* pShOutput = shOutput;
probeTask.m_U8OutputPointers = &pShOutput;
probeTask.m_U8OutputScale = 1.0f;