Condividi tramite


Mappe di profondità a cascata

Le mappe shadow a cascata (CSMS) sono il modo migliore per combattere uno degli errori più diffusi con l'ombreggiatura: aliasing della prospettiva. Questo articolo tecnico, che presuppone che il lettore abbia familiarità con il mapping shadow, affronta l'argomento delle macchine virtuali. Nello specifico:

  • spiega la complessità delle macchine virtuali;
  • fornisce informazioni dettagliate sulle possibili varianti degli algoritmi CSM;
  • descrive le due tecniche di filtro più comuni, ovvero il filtro più vicino percentuale (PCF) e il filtro con mappe shadow di varianza (VSMS);
  • identifica e risolve alcune delle insodenze comuni associate all'aggiunta di filtri alle macchine virtuali; E
  • mostra come eseguire il mapping delle macchine virtuali a Direct3D 10 tramite hardware Direct3D 11.

Il codice usato in questo articolo è disponibile negli esempi DirectX Software Development Kit (SDK) in CascadedShadowMaps11 e VarianceShadows11. Questo articolo sarà più utile dopo aver implementato le tecniche descritte nell'articolo tecnico, le tecniche comuni per migliorare le mappe di profondità shadow, vengono implementate.

Mappe ombreggiate a catena e alias di prospettiva

L'aliasing della prospettiva in una mappa shadow è uno dei problemi più difficili da superare. Nell'articolo tecnico, le tecniche comuni per migliorare le mappe di profondità shadow, l'aliasing della prospettiva è descritto e alcuni approcci per attenuare il problema vengono identificati. In pratica, le macchine virtuali tendono a essere la soluzione migliore e sono comunemente usate nei giochi moderni.

Il concetto di base di CSMs è facile da comprendere. Diverse aree del frustum della fotocamera richiedono mappe shadow con risoluzioni diverse. Gli oggetti più vicini all'occhio richiedono una risoluzione maggiore rispetto a quelli di altri oggetti distanti. Infatti, quando l'occhio si sposta molto vicino alla geometria, i pixel più vicini all'occhio possono richiedere così tanta risoluzione che anche una mappa ombreggiatura 4096 × 4096 è insufficiente.

L'idea di base dei CSMS consiste nel partizionare il frustum in più frusta. Viene eseguito il rendering di una mappa ombreggiatura per ogni subfrustum; il pixel shader quindi esempi dalla mappa che corrispondono più strettamente alla risoluzione richiesta (Figura 2).

Figura 1. Copertura mappa ombreggiatura

copertura della mappa ombreggiatura

Nella figura 1 viene visualizzata la qualità (a sinistra a destra) dal più alto al minimo. La serie di griglie che rappresentano mappe ombreggiate con un frustum di visualizzazione (cono invertito in rosso) mostra il modo in cui la copertura dei pixel è interessata da mappe shadow di risoluzione diverse. Le ombreggiature sono della qualità più elevata (pixel bianchi) quando è presente un mapping di pixel di rapporto 1:1 nello spazio chiaro ai texel nella mappa ombreggiatura. L'aliasing della prospettiva si verifica sotto forma di mappe di trame a blocchi di grandi dimensioni (immagine sinistra) quando troppi pixel mappano allo stesso texel ombreggiatura. Quando la mappa ombreggiatura è troppo grande, è sotto campione. In questo caso, i texel vengono ignorati, gli artefatti shimmering vengono introdotti e le prestazioni sono interessate.

Figura 2. Qualità dell'ombreggiatura CSM

Qualità dell'ombreggiatura csm

La figura 2 mostra i tagli dalla sezione di qualità più alta in ogni mappa ombreggiatura nella figura 1. La mappa ombreggiatura con i pixel più posizionati (al vertice) è più vicina all'occhio. Tecnicamente, queste sono mappe della stessa dimensione, con bianco e grigio usato per esemplificare il successo della mappa ombreggiatura a cascata. Bianco è ideale perché mostra una buona copertura, un rapporto 1:1 per pixel di spazio oculare e texel di mappa ombreggiatura.

Le macchine virtuali richiedono i passaggi seguenti per fotogramma.

  1. Partizionare il frustum nella subfrusta.

  2. Calcolare una proiezione ortografica per ogni subfrustum.

  3. Eseguire il rendering di una mappa shadow per ogni subfrustum.

  4. Eseguire il rendering della scena.

    1. Associare le mappe shadow e il rendering.

    2. Il vertex shader esegue le operazioni seguenti:

      • Calcola le coordinate della trama per ogni subfrustum chiaro (a meno che non venga calcolata la coordinata di trama necessaria nel pixel shader).
      • Trasforma e illumina il vertice e così via.
    3. Lo shader pixel esegue le operazioni seguenti:

      • Determina la mappa ombreggiatura appropriata.
      • Trasforma le coordinate della trama se necessario.
      • Esempi dell'estensione a cascata.
      • Illumina il pixel.

Partizionamento del Frustum

Il partizionamento del frustum è l'atto di creare subfrusta. Una tecnica per suddividere il frustum consiste nel calcolare gli intervalli da zero a cento per cento nella direzione Z. Ogni intervallo rappresenta quindi un piano vicino e un piano lontano come percentuale dell'asse Z.

Figura 3. Visualizzare i frustum partizionati arbitrariamente

visualizzare frustum partizionati arbitrariamente

In pratica, ricalcolando le suddivisioni frustum per fotogramma, i bordi ombreggiatura vengono sfumato. La pratica generalmente accettata consiste nell'usare un set statico di intervalli a catena per ogni scenario. In questo scenario viene usato l'intervallo lungo l'asse Z per descrivere un subfrustum che si verifica quando si partiziona il frustum. La determinazione degli intervalli di dimensioni corretti per una determinata scena dipende da diversi fattori.

Orientamento della geometria della scena

Rispetto alla geometria della scena, l'orientamento della fotocamera influisce sulla selezione dell'intervallo a catena. Ad esempio, una fotocamera molto vicino al terreno, ad esempio una telecamera a terra in un gioco di calcio, ha un set statico diverso di intervalli a cascata rispetto a una fotocamera nel cielo.

La figura 4 mostra alcune fotocamere diverse e le rispettive partizioni. Quando l'intervallo Z della scena è molto grande, sono necessari piani di divisione più elevati. Ad esempio, quando l'occhio è molto vicino al piano terra, ma gli oggetti distanti sono ancora visibili, è possibile che siano necessarie più cascata. Dividere il frustum in modo che più suddivisioni si trovino vicino all'occhio (dove l'aliasing di prospettiva sta cambiando il più veloce) è anche utile. Quando la maggior parte della geometria è raggruppata in una piccola sezione (ad esempio una vista di sovraccarico o un simulatore di volo) della visualizzazione frustum, sono necessarie meno cascata.

Figura 4. Configurazioni diverse richiedono suddivisioni di frustum diverse

configurazioni diverse richiedono suddivisioni di frustum diverse

(Sinistra) Quando la geometria ha un intervallo dinamico elevato in Z, sono necessarie molte cascata. (Centro) Quando la geometria ha un intervallo dinamico basso in Z, c'è poco vantaggio da più frustum. (Destra) Sono necessarie solo tre partizioni quando l'intervallo dinamico è medio.

Orientamento della luce e della fotocamera

Ogni matrice di proiezione a cascata si adatta strettamente alla relativa sottofrustum corrispondente. Nelle configurazioni in cui la fotocamera di visualizzazione e le direzioni di luce sono ortogonali, le cascate possono essere adattate strettamente con poche sovrapposizioni. La sovrapposizione diventa più grande come la luce e la fotocamera di visualizzazione si sposta in allineamento parallelo (Figura 5). Quando la luce e la fotocamera di visualizzazione sono quasi parallele, si chiama "duella frusta", ed è uno scenario molto difficile per la maggior parte degli algoritmi di ombreggiatura. Non è raro limitare la luce e la fotocamera in modo che questo scenario non si verifichi. Le macchine virtuali, tuttavia, eseguono molto meglio di molti altri algoritmi in questo scenario.

Figura 5. La sovrapposizione a cascata aumenta man mano che la direzione della luce diventa parallela con la direzione della fotocamera

la sovrapposizione a cascata aumenta man mano che la direzione della luce diventa parallela con la direzione della fotocamera

Molte implementazioni CSM usano frusta di dimensioni fisse. Il pixel shader può usare la profondità Z per indicizzare nella matrice di cascata quando il frustum è suddiviso in intervalli di dimensioni fisse.

Calcolo di un View-Frustum associato

Dopo aver selezionato gli intervalli di frustum, la subfrusta viene creata usando uno dei due elementi seguenti: adattarsi alla scena e adattarsi a cascata.

Adatta alla scena

Tutti i frusta possono essere creati con lo stesso piano vicino. In questo modo le cascate si sovrappongono. L'esempio CascadedShadowMaps11 chiama questa tecnica adatta alla scena.

Adatta a cascata

In alternativa, è possibile creare frusta con l'intervallo di partizione effettivo usato come piani vicini e lontani. Ciò causa un'adattabilità più stretta, ma degenera per adattarsi alla scena nel caso del duello frusta. Gli esempi CascadedShadowMaps11 chiamano questa tecnica adatta a cascata.

Questi due metodi sono visualizzati nella figura 6. Adattarsi ai rifiuti a cascata meno risoluzione. Il problema di adattarsi a cascata è che la proiezione ortografica cresce e si compatta in base all'orientamento del frustum della vista. L'adatta alla tecnica della scena inserisce la proiezione ortografica in base alle dimensioni massime della visualizzazione frustum rimuovendo gli artefatti visualizzati quando la fotocamera di visualizzazione si sposta. Tecniche comuni per migliorare le mappe di profondità shadow indirizza gli artefatti visualizzati quando la luce si sposta nella sezione "Spostamento della luce in incrementi di dimensioni di texel".

Figura 6. Adatta alla scena e adatta a cascata

adattarsi alla scena rispetto a adattarsi a cascata

Eseguire il rendering della mappa shadow

L'esempio CascadedShadowMaps11 esegue il rendering delle mappe ombreggiate in un buffer di grandi dimensioni. Questo perché PCF nelle matrici di trame è una funzionalità Direct3D 10.1. Per ogni cascata, viene creato un viewport che copre la sezione del buffer di profondità corrispondente a tale cascata. Un shader pixel Null è associato perché è necessaria solo la profondità. Infine, la matrice di visualizzazione e ombreggiatura corretta viene impostata per ogni cascata perché le mappe di profondità vengono sottoposte a rendering uno alla volta nel buffer shadow principale.

Eseguire il rendering della scena

Il buffer contenente le ombreggiature è ora associato al pixel shader. Esistono due metodi per selezionare la catena implementata nell'esempio CascadedShadowMaps11. Questi due metodi vengono illustrati con il codice shader.

Interval-Based Selezione a cascata

Figura 7. Selezione a catena basata su intervalli

Selezione a catena basata su intervalli

Nella selezione basata su intervalli (figura 7), il vertex shader calcola la posizione nello spazio mondiale del vertice.

Output.vDepth = mul( Input.vPosition, m_mWorldView ).z;

Il pixel shader riceve la profondità interpolata.

fCurrentPixelDepth = Input.vDepth;

La selezione a catena basata su intervalli usa un confronto vettoriale e un prodotto punto per determinare la cacade corretta. Il CASCADE_COUNT_FLAG specifica il numero di cascata. L'm_fCascadeFrustumsEyeSpaceDepths_data limita le partizioni della visualizzazione frustum. Dopo il confronto, fComparison contiene un valore pari a 1 dove il pixel corrente è maggiore della barriera e un valore pari a 0 quando la catena corrente è più piccola. Un prodotto dot somma questi valori in un indice di matrice.

        float4 vCurrentPixelDepth = Input.vDepth;
        float4 fComparison = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepths_data[0]);
        float fIndex = dot(
        float4( CASCADE_COUNT_FLAG > 0,
        CASCADE_COUNT_FLAG > 1,
        CASCADE_COUNT_FLAG > 2,
        CASCADE_COUNT_FLAG > 3)
        , fComparison );

        fIndex = min( fIndex, CASCADE_COUNT_FLAG );
        iCurrentCascadeIndex = (int)fIndex;

Dopo aver selezionato la cascata, la coordinata della trama deve essere trasformata nella catena corretta.

vShadowTexCoord = mul( InterpolatedPosition, m_mShadow[iCascadeIndex] );

Questa coordinata di trama viene quindi usata per esempio la trama con la coordinata X e la coordinata Y. La coordinata Z viene usata per eseguire il confronto di profondità finale.

Map-Based Selezione a cascata

La selezione basata su mappa (figura 8) verifica sui quattro lati delle cascate per trovare la mappa più stretta che copre il pixel specifico. Anziché calcolare la posizione nello spazio mondiale, il vertex shader calcola la posizione dello spazio di visualizzazione per ogni cascata. L'shader pixel esegue l'iterazione delle cascata per ridimensionare e spostare le coordinate della trama in modo che indicino la catena corrente. La coordinata della trama viene quindi testata sui limiti della trama. Quando i valori X e Y della coordinata della trama rientrano all'interno di una cascata, vengono usati per campionire la trama. La coordinata Z viene usata per eseguire il confronto di profondità finale.

Figura 8. Selezione a catena basata su mappa

Selezione a catena basata su mappa

selezione Interval-Based e selezione Map-Based

La selezione basata su intervalli è leggermente più veloce rispetto alla selezione basata su mappa perché la selezione a catena può essere eseguita direttamente. La selezione basata su mappa deve intersecare la coordinata della trama con i limiti a cascata.

La selezione basata su mappa usa la catena in modo più efficiente quando le mappe shadow non si allineano perfettamente (vedere la figura 8).

Fusione tra le cascate

LE MACCHINE VIRTUALI (descritte più avanti in questo articolo) e le tecniche di filtro, ad esempio PCF, possono essere usate con csms a bassa risoluzione per produrre ombreggiature morbide. Sfortunatamente, questo risultato è un seam visibile (Figura 9) tra livelli a cascata perché la risoluzione non corrisponde. La soluzione consiste nel creare una banda tra mappe shadow in cui viene eseguito il test shadow per entrambe le cascate. Lo shader quindi interpola in modo lineare tra i due valori in base alla posizione del pixel nella banda di blend. Gli esempi CascadedShadowMaps11 e VarianceShadows11 forniscono un dispositivo di scorrimento GUI che può essere usato per aumentare e ridurre questa banda blur. Lo shader esegue un ramo dinamico in modo che la maggior parte dei pixel venga lette solo dalla catena corrente.

Figura 9. Seams a cascata

seams a cascata

(Sinistra) Un seam visibile può essere visto dove si sovrappongono le cascate. (Destra) Quando le cascate vengono combinate tra, non si verifica alcun seam.

Filtro delle mappe shadow

PCF

Il filtro delle mappe ombreggiature normali non produce ombreggiature morbide e sfocate. L'hardware di filtro riduce i valori di profondità e quindi confronta i valori sfocati al texel dello spazio chiaro. Il bordo rigido risultante dal test pass/fail esiste ancora. Le mappe ombreggiatura sfocate servono solo a spostare erroneamente il bordo rigido. PCF consente di filtrare le mappe shadow. L'idea generale di PCF consiste nel calcolare una percentuale del pixel in ombreggiatura in base al numero di sottocampionamento che superano il test di profondità sul numero totale di sottocampionamento.

L'hardware Direct3D 10 e Direct3D 11 può eseguire PCF. L'input di un sampler PCF è costituito dalla coordinata della trama e da un valore di profondità di confronto. Per semplicità, PCF viene spiegato con un filtro a quattro tap. L'esempio di trama legge la trama quattro volte, simile a un filtro standard. Tuttavia, il risultato restituito è una percentuale dei pixel che hanno superato il test di profondità. La figura 10 mostra come un pixel che supera uno dei quattro test di profondità è il 25% in ombra. Il valore effettivo restituito è un'interpolazione lineare in base alle coordinate subtexel della trama letti per produrre una sfumatura uniforme. Senza questa interpolazione lineare, il PCF a quattro toccare sarebbe in grado di restituire solo cinque valori: { 0.0, 0.25, 0.5, 0.75, 1.0 }.

Figura 10. Immagine filtrata pcF, con il 25% del pixel selezionato coperto

immagine filtrata pcf, con il 25% del pixel selezionato coperto

È anche possibile eseguire PCF senza supporto hardware o estendere PCF a kernel più grandi. Alcune tecniche anche di esempio con un kernel ponderato. A tale scopo, creare un kernel (ad esempio un Gaussian) per una griglia N × N. I pesi devono essere aggiunti fino a 1. La trama viene quindi campionata N2 volte. Ogni esempio viene ridimensionato in base ai pesi corrispondenti nel kernel. L'esempio CascadedShadowMaps11 usa questo approccio.

Distorsione della profondità

La distorsione della profondità diventa ancora più importante quando vengono usati kernel PCF di grandi dimensioni. È valido solo confrontare la profondità dello spazio chiaro di un pixel rispetto al pixel mappato alla mappa approfondita. I vicini del texel della mappa della profondità fanno riferimento a una posizione diversa. Questa profondità è probabilmente simile, ma può essere molto diversa a seconda della scena. La figura 11 evidenzia gli artefatti che si verificano. Una singola profondità viene confrontata con tre texel adiacenti nella mappa ombreggiatura. Uno dei test di profondità ha esito errato perché la profondità non è correlata alla profondità dello spazio chiaro calcolato della geometria corrente. La soluzione consigliata a questo problema consiste nell'usare un offset più grande. Tuttavia, un offset troppo grande può comportare Peter Panning. Il calcolo di un piano vicino stretto e di un piano lontano consente di ridurre gli effetti dell'uso di un offset.

Figura 11. Auto-ombreggiatura errata

self-shadowing errato

Il risultato dell'auto shadowing errato deriva dal confronto dei pixel nella profondità dello spazio chiaro ai texel nella mappa ombreggiatura che non correlano. La profondità nello spazio chiaro è correlata all'ombreggiatura texel 2 nella mappa di profondità. Texel 1 è maggiore della profondità dello spazio chiaro mentre 2 è uguale e 3 è minore. Texels 2 e 3 superano il test di profondità, mentre Texel 1 ha esito negativo.

Calcolo di un bias Per-Texel profondità con DDX e DDY per pc di grandi dimensioni

Il calcolo di un pregiudizio di profondità per texel con ddx e ddy per pc di grandi dimensioni è una tecnica che calcola il pregiudizio di profondità corretto, presupponendo che la superficie sia planare, per il texel della mappa ombreggiatura adiacente.

Questa tecnica adatta la profondità di confronto a un piano usando le informazioni derivate. Poiché questa tecnica è complessa in modo computazionale, deve essere usata solo quando una GPU ha cicli di calcolo da risparmiare. Quando vengono usati kernel molto grandi, questa può essere l'unica tecnica che funziona per rimuovere gli artefatti auto shadowing senza causare Peter Panning.

La figura 12 evidenzia il problema. La profondità nello spazio chiaro è nota per il texel che viene confrontato. Le profondità dello spazio chiaro che corrispondono ai texel adiacenti nella mappa di profondità sono sconosciute.

Figura 12. Mappa della scena e della profondità

mappa della scena e della profondità

La scena di rendering viene visualizzata a sinistra e la mappa di profondità con un blocco texel di esempio viene visualizzata a destra. Il texel dello spazio oculare mappa al pixel etichettato D al centro del blocco. Questo confronto è accurato. La profondità corretta nello spazio oculare correlata ai pixel che vicino D è sconosciuto. Il mapping dei texel adiacenti allo spazio oculare è possibile solo se si presuppone che il pixel si riferisca allo stesso triangolo di D.

La profondità è nota per il texel che correla con la posizione dello spazio chiaro. La profondità è sconosciuta per i texel adiacenti nella mappa di profondità.

A livello generale, questa tecnica usa le operazioni ddx e ddy HLSL per trovare il derivato della posizione dello spazio chiaro. Ciò non è possibile perché le operazioni derivate restituiscono la sfumatura della profondità dello spazio chiaro rispetto allo spazio dello schermo. Per convertire questa proprietà in una sfumatura della profondità dello spazio chiaro rispetto allo spazio chiaro, è necessario calcolare una matrice di conversione.

Spiegazione con il codice shader

I dettagli del resto dell'algoritmo vengono forniti come spiegazione del codice shader che esegue questa operazione. Questo codice è disponibile nell'esempio CascadedShadowMaps11. La figura 13 mostra come le coordinate della trama dello spazio chiaro mappano alla mappa della profondità e come è possibile usare i derivati in X e Y per creare una matrice di trasformazione.

Figura 13. Matrice dello spazio dello schermo per lo spazio chiaro

spazio sullo schermo per la matrice dello spazio chiaro

I derivati della posizione dello spazio chiaro in X e Y vengono usati per creare questa matrice.

Il primo passaggio consiste nel calcolare la derivata della posizione dello spazio di visualizzazione della luce.

          float3 vShadowTexDDX = ddx (vShadowMapTextureCoordViewSpace);
          float3 vShadowTexDDY = ddy (vShadowMapTextureCoordViewSpace);

Le GPU di classe Direct3D 11 calcolano questi derivati eseguendo 2 × 2 quad di pixel in parallelo e sottraendo le coordinate della trama dal vicino in X per ddx e dal vicino in Y per ddy. Questi due derivati costituiscono le righe di una matrice di 2 × 2. Nella sua forma corrente, questa matrice potrebbe essere usata per convertire lo spazio dello schermo vicino ai pixel in pendenza dello spazio chiaro. Tuttavia, l'inverso di questa matrice è necessario. È necessaria una matrice che trasforma lo spazio leggero vicino ai pixel in pendenza dello spazio dello schermo.

          float2x2 matScreentoShadow = float2x2( vShadowTexDDX.xy, vShadowTexDDY.xy );
          float fInvDeterminant = 1.0f / fDeterminant;

          float2x2 matShadowToScreen = float2x2 (
          matScreentoShadow._22 * fInvDeterminant,
          matScreentoShadow._12 * -fInvDeterminant,
          matScreentoShadow._21 * -fInvDeterminant,
          matScreentoShadow._11 * fInvDeterminant );

Figura 14. Spazio chiaro per lo spazio dello schermo

spazio chiaro per lo spazio dello schermo

Questa matrice viene quindi usata per trasformare i due texel sopra e a destra del texel corrente. Questi vicini sono rappresentati come offset dal texel corrente.

          float2 vRightShadowTexelLocation = float2( m_fTexelSize, 0.0f );
          float2 vUpShadowTexelLocation = float2( 0.0f, m_fTexelSize );
          float2 vRightTexelDepthRatio = mul( vRightShadowTexelLocation,
          matShadowToScreen );
          float2 vUpTexelDepthRatio = mul( vUpShadowTexelLocation,
          matShadowToScreen );

Il rapporto creato dalla matrice viene infine moltiplicato per i derivati della profondità per calcolare gli offset di profondità per i pixel adiacenti.

            float fUpTexelDepthDelta =
            vUpTexelDepthRatio.x * vShadowTexDDX.z
            + vUpTexelDepthRatio.y * vShadowTexDDY.z;
            float fRightTexelDepthDelta =
            vRightTexelDepthRatio.x * vShadowTexDDX.z
            + vRightTexelDepthRatio.y * vShadowTexDDY.z;

Questi pesi possono ora essere usati in un ciclo PCF per aggiungere un offset alla posizione.

    for( int x = m_iPCFBlurForLoopStart; x < m_iPCFBlurForLoopEnd; ++x ) 
    {
        for( int y = m_iPCFBlurForLoopStart; y < m_iPCFBlurForLoopEnd; ++y )
            {
            if ( USE_DERIVATIVES_FOR_DEPTH_OFFSET_FLAG )
            {
            depthcompare += fRightTexelDepthDelta * ( (float) x ) +
            fUpTexelDepthDelta * ( (float) y );
            }
            // Compare the transformed pixel depth to the depth read
            // from the map.
            fPercentLit += g_txShadow.SampleCmpLevelZero( g_samShadow,
            float2(
            vShadowTexCoord.x + ( ( (float) x ) * m_fNativeTexelSizeInX ) ,
            vShadowTexCoord.y + ( ( (float) y ) * m_fTexelSize )
            ),
            depthcompare
            );
            }
     }

PCF e CSMS

PCF non funziona sulle matrici di trame in Direct3D 10. Per usare PCF, tutte le cascata vengono archiviate in un grande atlas di trame.

offset Derivative-Based

L'aggiunta degli offset basati su derivati per le macchine virtuali presenta alcune sfide. Ciò è dovuto a un calcolo derivato all'interno del controllo del flusso divergente. Il problema si verifica a causa di un modo fondamentale in cui operano le GPU. Le GPU Direct3D11 operano su 2 × 2 quad di pixel. Per eseguire una derivata, le GPU in genere sottraggono la copia del pixel corrente di una variabile dalla copia del pixel adiacente della stessa variabile. In che modo ciò avviene varia da GPU a GPU. Le coordinate della trama sono determinate dalla selezione a catena basata su mappa o a intervalli. Alcuni pixel in un pixel quad scelgono una cascata diversa rispetto al resto dei pixel. Ciò comporta la presenza di seams visibili tra mappe ombreggiature perché gli offset basati su derivati sono ora completamente sbagliati. La soluzione consiste nell'eseguire la derivata sulle coordinate della trama dello spazio di visualizzazione della luce. Queste coordinate sono uguali per ogni cascata.

Riempimento per kernel PCF

Indice dei kernel PCF all'esterno di una partizione a catena se il buffer shadow non è riempimento. La soluzione consiste nel pad del bordo esterno della cascata di una metà delle dimensioni del kernel PCF. Questa operazione deve essere implementata nello shader che seleziona la cascata e nella matrice di proiezione che deve eseguire il rendering della catena a cascata abbastanza grande che il bordo viene mantenuto.

Mappe shadow di varianza

VsMs (vedere Mappe ombreggiatura varianza da Donnelly e Lauritzen per altre informazioni) abilitare il filtro della mappa shadow diretta. Quando si usano LE MACCHINE virtuali, è possibile usare tutta la potenza dell'hardware di filtro della trama. È possibile usare filtri trilineari e anisotropici (figura 15). Inoltre, le MACCHINE virtuali possono essere sfocate direttamente attraverso la convoluzione. Le macchine virtuali hanno alcuni svantaggi; è necessario archiviare due canali di dati di profondità (profondità e profondità quadrata). Quando le ombre si sovrappongono, il sanguinamento della luce è comune. Funzionano bene, tuttavia, con risoluzioni inferiori e possono essere combinati con le macchine virtuali.

Figura 15. Filtro anisotropico

filtro anisotropico

Informazioni sugli algoritmi

Le MACCHINE virtuali funzionano eseguendo il rendering della profondità e della profondità quadrata in una mappa shadow a due canali. Questa mappa ombreggiatura a due canali può quindi essere sfocata e filtrata esattamente come una trama normale. L'algoritmo usa quindi la disuguaglianza di Chebychev nel pixel shader per stimare la frazione dell'area pixel che passerebbe il test di profondità.

Lo shader pixel recupera i valori di profondità e profondità quadrati.

        float  fAvgZ  = mapDepth.x; // Filtered z
        float  fAvgZ2 = mapDepth.y; // Filtered z-squared

Viene eseguito il confronto di profondità.

        if ( fDepth <= fAvgZ )
        {
        fPercentLit = 1;
        }

Se il confronto di profondità ha esito negativo, viene stimata la percentuale del pixel acceso. La varianza viene calcolata come media di quadrati meno quadrati quadrati.

        float variance = ( fAvgZ2 ) − ( fAvgZ * fAvgZ );
        variance = min( 1.0f, max( 0.0f, variance + 0.00001f ) );

Il valore fPercentLit viene stimato con la disuguaglianza di Chebychev.

        float mean           = fAvgZ;
        float d              = fDepth - mean;
        float fPercentLit    = variance / ( variance + d*d );

Sanguinamento chiaro

Lo svantaggio principale per le VSMS è il sanguinamento leggero (Figura 16). Il sanguinamento leggero si verifica quando più castatori ombreggiatura si occludeno l'uno l'altro lungo i bordi. Le VSMS ombreggiaturano i bordi delle ombreggiature in base alle differenze di profondità. Quando le ombre si sovrappongono tra loro, esiste una disparità di profondità nel centro di un'area che deve essere ombreggiata. Si tratta di un problema relativo all'uso dell'algoritmo VSM.

Figura 16. Sanguinamento di luce VSM

vsm chiaro sanguinamento

Una soluzione parziale al problema consiste nell'aumentare il valore fPercentLit a un potere. Questo ha l'effetto di attenuare il blur, che può causare artefatti in cui la disparità di profondità è piccola. A volte esiste un valore magico che risolve il problema.

fPercentLit = pow( p_max, MAGIC_NUMBER );

Un'alternativa all'aumento della percentuale di luce a una potenza consiste nell'evitare configurazioni in cui si sovrappongono le ombre. Anche le configurazioni ombreggiate altamente ottimizzate hanno diversi vincoli sulla luce, la fotocamera e la geometria. Il sanguinamento chiaro è anche ridotto usando trame di risoluzione più elevate.

Le mappe ombreggiate a livelli (LVSMs) risolvono il problema a causa dell'interruzione del frustum in livelli perpendicolare alla luce. Il numero di mappe necessarie sarebbe abbastanza grande quando vengono usate anche le macchine virtuali.

Inoltre, Andrew Lauritzen, coautore del documento su VSMs, e autore di un documento su LVSMs, ha discusso di combinare mappe shadow esponenziali (ESMs) con VSMs per contrastare la fusione della luce in un Forum Beyond3D.

VSMs with CSMs

L'esempio VarianceShadow11 combina VSMs e CSM. La combinazione è abbastanza semplice. L'esempio segue gli stessi passaggi dell'esempio CascadedShadowMaps11. Poiché PCF non viene usato, le ombreggiature vengono sfocate in una convoluzione separabile a due passaggi. L'uso di PCF consente anche all'esempio di usare matrici di trame anziché un atlas con trama. PCF nelle matrici di trame è una funzionalità Direct3D 10.1.

Sfumature con csm

L'uso di sfumature con csm può produrre una giunzione lungo il bordo tra due cascate, come illustrato nella figura 17. L'istruzione di esempio usa derivati tra pixel per calcolare le informazioni, ad esempio il livello mipmap, necessarie per il filtro. Ciò causa un problema in particolare per la selezione mipmap o il filtro anisotropico. Quando i pixel in un quad accettano rami diversi nello shader, i derivati calcolati dall'hardware GPU non sono validi. Ciò comporta una cucitura frastagliata lungo la mappa ombreggiatura.

Figura 17. Seams on cascade borders due to anisotropic filtering with divergent flow control

cuciture sui bordi a catena a causa di un filtro anisotropico con controllo di flusso divergente

Questo problema viene risolto calcolando i derivati sulla posizione nello spazio di visualizzazione della luce; la coordinata dello spazio di visualizzazione della luce non è specifica della catena selezionata. I derivati calcolati possono essere ridimensionati dalla parte di scala della matrice della trama di proiezione al livello mipmap corretto.

        float3 vShadowTexCoordDDX = ddx( vShadowMapTextureCoordViewSpace );
        vShadowTexCoordDDX *= m_vCascadeScale[iCascade].xyz;
        float3 vShadowTexCoordDDY = ddy( vShadowMapTextureCoordViewSpace );
        vShadowTexCoordDDY *= m_vCascadeScale[iCascade].xyz;

        mapDepth += g_txShadow.SampleGrad( g_samShadow, vShadowTexCoord.xyz,
        vShadowTexCoordDDX, vShadowTexCoordDDY );

VSMs rispetto alle ombreggiature standard con PCF

Sia le VM che il PCF tentano di approssimare la frazione di area in pixel che supererebbe il test di profondità. Le MACCHINE VIRTUALI funzionano con l'hardware di filtro e possono essere sfocate con kernel separabili. I kernel di convoluzione separabili sono notevolmente più economici da implementare rispetto a un kernel completo. Inoltre, le VSM confrontano una profondità dello spazio chiaro con un valore nella mappa di profondità dello spazio chiaro. Ciò significa che le VM non presentano gli stessi problemi di offset di PCF. Tecnicamente, le VSM sono profondità di campionamento su un'area maggiore, nonché l'esecuzione di un'analisi statistica. Questo è meno preciso di PCF. In pratica, le VSM svolgono un ottimo lavoro di fusione, il che comporta una riduzione dell'offset necessario. Come descritto in precedenza, il numero uno svantaggio delle VSMS è l'emorragia leggera.

LE MACCHINE VIRTUALI e PCF rappresentano un compromesso tra la potenza di calcolo gpu e la larghezza di banda della trama della GPU. Per calcolare la varianza, le MACCHINE virtuali richiedono un numero maggiore di calcoli matematici. PCF richiede una maggiore larghezza di banda per la memoria della trama. I kernel PCF di grandi dimensioni possono diventare rapidamente colli di bottiglia dalla larghezza di banda della trama. Con la potenza di calcolo della GPU che cresce più rapidamente rispetto alla larghezza di banda della GPU, le VM stanno diventando più pratiche dei due algoritmi. Le VSMS hanno anche un aspetto migliore con mappe ombreggiature con risoluzione inferiore a causa della fusione e del filtro.

Riepilogo

I csm offrono una soluzione al problema di aliasing prospettica. Esistono diverse configurazioni possibili per ottenere la fedeltà visiva necessaria per un titolo. PCF e VSMs sono ampiamente usati e devono essere combinati con i csm per ridurre l'aliasing.

Riferimenti

Donnelly, W. e Lauritzen, A. Variance shadow maps. In SI3D '06: Proceedings of the 2006 symposium on Interactive 3D graphics and games.In SI3D '06: Proceedings of the 2006 symposium on Interactive 3D graphics and games. 2006. pp. 161–165. New York, NY, USA: ACM Press.

Lauritzen, Andrew e McCool, Michael. Mappe ombreggiature a varianza su più livelli. Documentazione dell'interfaccia grafica 2008, 28-30 maggio 2008, Windsor, Ontario, Canada.

La sezione 4. Mappe ombreggiate a catena. ShaderX5 , Tecniche di rendering avanzate, Wolfgang F. Esegue, Ed. Charles River Media, Boston, Massachusetts. 2006. pp. 197-206.