Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Le mappe shadow a cascata (CSM) sono il modo migliore per combattere uno degli errori più diffusi con l'ombreggiatura: aliasing prospettica. Questo articolo tecnico, che presuppone che il lettore abbia familiarità con il mapping delle ombreggiature, affronta l'argomento dei csm. In particolare, è:
- spiega la complessità dei csm;
- 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 (VSM);
- identifica e risolve alcune delle insidie comuni associate all'aggiunta di filtri ai CSP; e
- illustra come eseguire il mapping dei csm all'hardware Direct3D 10 tramite Direct3D 11.
Il codice usato in questo articolo è disponibile in DirectX Software Development Kit (SDK) negli esempi CascadedShadowMaps11 e VarianceShadows11. Questo articolo risulterà più utile dopo l'implementazione delle tecniche descritte nell'articolo tecnico, tecniche comuni per migliorare le mappe di profondità shadow, vengono implementate.
Mappe shadow a cascata e aliasing prospettica
L'aliasing prospettica in una mappa ombreggiatura è uno dei problemi più difficili da superare. Nell'articolo tecnico Tecniche comuni per migliorare le mappe di profondità delle ombreggiature, viene descritto l'aliasing prospettica e vengono identificati alcuni approcci per attenuare il problema. In pratica, i CSM tendono a essere la soluzione migliore e sono comunemente impiegati nei giochi moderni.
Il concetto di base dei csm è facile da comprendere. Diverse aree del frustum della fotocamera richiedono mappe d'ombra con risoluzioni diverse. Gli oggetti più vicini all'occhio richiedono una risoluzione superiore rispetto a oggetti più 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 d'ombra 4096 × 4096 non è sufficiente.
L'idea di base dei CSM consiste nel partizionare il frustum in più frusta. Viene eseguito il rendering di una mappa ombreggiatura per ogni sottofrusto; il pixel shader esegue quindi campioni dalla mappa che corrispondono più strettamente alla risoluzione richiesta (figura 2).
Figura 1. Copertura mappa ombreggiatura
Nella figura 1 viene visualizzata la qualità (da sinistra a destra) dal più alto al più basso. La serie di griglie che rappresentano le mappe delle ombreggiature con un frustum di visualizzazione (cono invertito in rosso) mostra il modo in cui la copertura dei pixel è influenzata dalle diverse mappe ombreggiature di risoluzione. Le ombreggiature sono della massima qualità (pixel bianchi) quando è presente un rapporto di 1:1 mapping dei pixel nello spazio chiaro ai texel nella mappa ombreggiatura. L'aliasing prospettica si verifica sotto forma di mappe di trame in blocchi di grandi dimensioni (immagine sinistra) quando sono mappati troppi pixel allo stesso texel ombreggiatura. Quando la mappa delle ombreggiature è troppo grande, viene campionata. In questo caso, i texel vengono ignorati, vengono introdotti artefatti scintillanti e le prestazioni sono interessate.
Figura 2. Qualità dell'ombreggiatura CSM
La figura 2 mostra i ritagli della sezione di qualità più elevata in ogni mappa ombreggiatura nella figura 1. La mappa delle ombreggiature con i pixel più posizionati (al vertice) è più vicina all'occhio. Tecnicamente, si tratta di mappe delle stesse dimensioni, con bianco e grigio usato per esemplificare il successo della mappa delle ombreggiature a cascata. Il bianco è ideale perché mostra una buona copertura, ovvero un rapporto di 1:1 per i pixel dello spazio oculare e i texel della mappa ombreggiatura.
I csm richiedono i passaggi seguenti per ogni fotogramma.
Partizionare il frustum in subfrusta.
Calcolare una proiezione ortografica per ogni subfrustum.
Eseguire il rendering di una mappa ombreggiatura per ogni sottofrusto.
Eseguire il rendering della scena.
Associare le mappe shadow ed eseguire il rendering.
Il vertex shader esegue le operazioni seguenti:
- Calcola le coordinate delle trame per ogni subfrustum chiaro (a meno che non venga calcolata la coordinata della trama necessaria nel pixel shader).
- Trasforma e illumina il vertice e così via.
Il pixel shader esegue le operazioni seguenti:
- Determina la mappa delle ombreggiature appropriata.
- Trasforma le coordinate della trama, se necessario.
- Campiona la 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 frustum partizionati in modo arbitrario
In pratica, ricalcolando le divisioni frustum per fotogramma, i bordi dell'ombreggiatura vengono sfumati. La pratica generalmente accettata consiste nell'usare un set statico di intervalli a catena per scenario. In questo scenario, l'intervallo lungo l'asse Z viene usato per descrivere un subfrustum che si verifica durante il partizionamento del frustum. La determinazione degli intervalli di dimensioni corretti per una determinata scena dipende da diversi fattori.
Orientamento della geometria della scena
Per quanto riguarda la 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 fotocamera 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 più piani di divisione. Ad esempio, quando l'occhio è molto vicino al piano terra, ma gli oggetti distanti sono ancora visibili, possono essere necessarie più cascate. Dividendo il frustum in modo che più divisioni si trovino vicino all'occhio (dove l'aliasing prospettica sta cambiando il più veloce) è anche prezioso. Quando la maggior parte della geometria è raggruppata in una piccola sezione (ad esempio una visualizzazione overhead o un simulatore di volo) del frustum di visualizzazione, sono necessarie meno cascate.
Figura 4. Configurazioni diverse richiedono divisioni frustum diverse
(a sinistra) Quando la geometria ha un intervallo dinamico elevato in Z, sono necessarie numerose cascate. (Centro) Quando la geometria ha un intervallo dinamico basso in Z, c'è poco vantaggio da più frustum. (Destra) Quando l'intervallo dinamico è medio, sono necessarie solo tre partizioni.
Orientamento della luce e della fotocamera
La matrice di proiezione di ogni catena si adatta strettamente intorno al sottofrustum corrispondente. Nelle configurazioni in cui la fotocamera di visualizzazione e le direzioni della luce sono ortogonali, le cascate possono essere adattate strettamente con poca sovrapposizione. La sovrapposizione diventa più grande quando 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 "frusta duente" ed è uno scenario molto difficile per la maggior parte degli algoritmi di ombreggiatura. Non è raro vincolare la luce e la fotocamera in modo che questo scenario non si verifichi. I csm, tuttavia, offrono prestazioni molto migliori rispetto a molti altri algoritmi in questo scenario.
Figura 5. La sovrapposizione a catena aumenta man mano che la direzione della luce diventa parallela con la direzione della fotocamera
Molte implementazioni CSM usano frusta a dimensione fissa. Il pixel shader può usare la profondità Z per indicizzare nella matrice di cascate quando il frustum è suddiviso in intervalli di dimensioni fisse.
Calcolo di un View-Frustum associato
Dopo aver selezionato gli intervalli 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, frusta può essere creato con l'intervallo di partizione effettivo usato come piani vicini e lontani. Questo fa sì che un adattamento più stretto, ma degenera adattarsi alla scena nel caso di duella frusta. Gli esempi CascadedShadowMaps11 chiamano questa tecnica adatta alla propagazione.
Questi due metodi sono illustrati nella figura 6. Adattarsi ai rifiuti a cascata riduce la risoluzione. Il problema dell'adattamento a cascata è che la proiezione ortografica cresce e si riduce in base all'orientamento del frustum di visualizzazione. L'adattamento alla tecnica della scena inserisce la proiezione ortografica in base alle dimensioni massime del frustum di visualizzazione rimuovendo gli artefatti visualizzati quando la videocamera viene spostata. Tecniche comuni per migliorare le mappe di profondità delle ombreggiature risolve gli artefatti visualizzati quando la luce si sposta nella sezione "Spostamento della luce in incrementi di dimensioni di texel".
Figura 6. Adattarsi alla scena e adattarsi alla propagazione
a cascata
Eseguire il rendering della mappa ombreggiatura
L'esempio CascadedShadowMaps11 esegue il rendering delle mappe ombreggiate in un unico 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 pixel shader null è associato perché è necessaria solo la profondità. Infine, il riquadro di visualizzazione e la matrice di ombreggiatura corretti vengono impostati per ogni cascata perché il rendering delle mappe di profondità viene eseguito uno alla volta nel buffer ombreggiatura 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 sono illustrati con il codice dello shader.
Interval-Based selezione a cascata
Figura 7. Selezione a cascata basata su intervallo
Nella selezione basata su intervalli (figura 7), il vertex shader calcola la posizione nello spazio globale 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 intervallo usa un confronto vettoriale e un prodotto punto per determinare la cacade corretta. Il CASCADE_COUNT_FLAG specifica il numero di cascate. Il m_fCascadeFrustumsEyeSpaceDepths_data vincola le partizioni del frustum di visualizzazione. 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 cascata 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 cascata corretta.
vShadowTexCoord = mul( InterpolatedPosition, m_mShadow[iCascadeIndex] );
Questa coordinata della trama viene quindi usata per campionare 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) testa i quattro lati delle cascate per trovare la mappa più stretta che copre il pixel specifico. Anziché calcolare la posizione nello spazio globale, il vertex shader calcola la posizione dello spazio di visualizzazione per ogni cascata. Il pixel shader esegue l'iterazione sulle cascate per ridimensionare e spostare le coordinate della trama in modo da indicizzare la propagazione 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 campionare la trama. La coordinata Z viene usata per eseguire il confronto di profondità finale.
Figura 8. 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 sulla 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 catena.
La selezione basata su mappa usa la cascata in modo più efficiente quando le mappe ombreggiate non sono perfettamente allineate (vedere la figura 8).
Blend between Cascades
Le macchine virtuali virtuali (descritte più avanti in questo articolo) e le tecniche di filtro, ad esempio PCF, possono essere usate con csm a bassa risoluzione per produrre ombre morbide. Sfortunatamente, ciò comporta una giunzione visibile (figura 9) tra i livelli a cascata perché la risoluzione non corrisponde. La soluzione consiste nel creare una banda tra mappe ombreggiate in cui viene eseguito il test shadow per entrambe le cascate. Lo shader esegue quindi l'interpolazione lineare tra i due valori in base alla posizione del pixel nella banda di fusione. Gli esempi CascadedShadowMaps11 e VarianceShadows11 forniscono un dispositivo di scorrimento GUI che può essere usato per aumentare e ridurre questa banda sfocatura. Lo shader esegue un ramo dinamico in modo che la maggior parte dei pixel legga solo dalla cascata corrente.
Figura 9. Cuciture a cascata
di cuciture a cascata
(a sinistra) È possibile osservare una giunzione visibile in cui si sovrappongono le cascate. (Destra) Quando le cascate vengono mescolate tra loro, non si verifica alcuna giunzione.
Filtro delle mappe ombreggiate
PCF
Il filtro delle mappe delle ombre normali non produce ombre sfumate e sfocate. L'hardware di filtro sfoca i valori di profondità e quindi confronta i valori sfocati con il texel dello spazio chiaro. Il bordo rigido risultante dal test superato/negativo esiste ancora. Le mappe ombreggiature sfocate servono solo per 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 ombra 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 campionatore 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. Il campionatore di trame 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 basata sulle coordinate del sottotexel della trama per produrre una sfumatura liscia. Senza questa interpolazione lineare, il PCF a quattro tap sarà 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
È anche possibile eseguire PCF senza supporto hardware o estendere PCF a kernel più grandi. Alcune tecniche vengono anche campionate con un kernel ponderato. A tale scopo, creare un kernel (ad esempio un gaussiano) per una griglia N × N. I pesi devono aggiungere 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 profondità
La distorsione della profondità diventa ancora più importante quando vengono usati kernel PCF di grandi dimensioni. È valido solo per confrontare la profondità dello spazio di luce di un pixel rispetto al pixel mappato alla mappa di profondità. I vicini del texel della mappa di 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à non riesce erroneamente perché la profondità non è correlata alla profondità dello spazio di luce calcolata della geometria corrente. La soluzione consigliata a questo problema consiste nell'usare un offset più grande. Tuttavia, una quantità eccessiva di offset può comportare Peter Panning. Il calcolo di un piano vicino stretto e di un piano lontano consente di ridurre gli effetti dell'utilizzo di un offset.
Figura 11. Self-shadowing errato
di auto-ombreggiatura errata
L'auto shadowing errato comporta il confronto dei pixel nella profondità dello spazio chiaro con i texel nella mappa ombreggiatura che non sono correlati. La profondità nello spazio chiaro è correlata al texel 2 ombreggiatura nella mappa di profondità. Texel 1 è maggiore della profondità dello spazio della luce mentre 2 è uguale a 3 è minore. I texel 2 e 3 superano il test di profondità, mentre Texel 1 ha esito negativo.
Calcolo di una distorsione Per-Texel profondità con DDX e DDY per PCF di grandi dimensioni
Il calcolo di una distorsione della profondità per texel con ddx e fangoso per pcf di grandi dimensioni è una tecnica che calcola la distorsione corretta della profondità, presupponendo che la superficie sia planare, per il texel della mappa delle ombreggiature adiacente.
Questa tecnica adatta la profondità di confronto a un piano usando le informazioni derivate. Poiché questa tecnica è complessa dal livello di calcolo, deve essere usata solo quando una GPU ha cicli di calcolo da risparmiare. Quando si usano 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 leggero è 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à
della mappa di profondità
La scena sottoposta a rendering viene visualizzata a sinistra e la mappa di profondità con un blocco di texel di esempio viene visualizzata a destra. Il texel dello spazio oculare esegue il mapping al pixel etichettato D al centro del blocco. Questo confronto è accurato. Profondità corretta nello spazio oculare correlata ai pixel adiacenti D sconosciuti. 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 alla posizione dello spazio chiaro. La profondità è sconosciuta per i texel adiacenti nella mappa di profondità.
A livello generale, questa tecnica usa la ddx e operazioni ddy HLSL per trovare il derivato della posizione dello spazio leggero. Si tratta di un comportamento non irreversibile perché le operazioni derivate restituiscono la sfumatura della profondità dello spazio leggero rispetto allo spazio dello schermo. Per convertirlo in una sfumatura della profondità dello spazio chiaro rispetto allo spazio leggero, è necessario calcolare una matrice di conversione.
Spiegazione con il codice shader
I dettagli del resto dell'algoritmo vengono forniti come spiegazione del codice dello shader che esegue questa operazione. Questo codice è disponibile nell'esempio CascadedShadowMaps11. Nella figura 13 viene illustrato il mapping delle coordinate della trama dello spazio chiaro alla mappa di profondità e il modo in cui i derivati in X e Y possono essere usati per creare una matrice di trasformazione.
Figura 13. Matrice dello spazio sullo schermo per lo 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 . Questi due derivati costituiscono le righe di una matrice 2 × 2. Nella forma corrente, questa matrice può essere usata per convertire gli spazi dello schermo vicini ai pixel in pendii dello spazio chiaro. Tuttavia, è necessario l'inverso di questa matrice. È necessaria una matrice che trasforma gli spazi di luce adiacenti pixel in pendenze 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 sullo schermo
Questa matrice viene quindi usata per trasformare i due texel sopra e a destra del texel corrente. Questi vicini sono rappresentati come offset rispetto al 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 di 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 CSM
PCF non funziona sulle matrici di trame in Direct3D 10. Per usare PCF, tutte le cascate vengono archiviate in un unico grande atlas di trame.
offset Derivative-Based
L'aggiunta degli offset basati su derivati per i csm presenta alcune sfide. Ciò è dovuto a un calcolo derivato all'interno di un controllo di flusso divergente. Il problema si verifica a causa di un modo fondamentale per il funzionamento delle GPU. Le GPU Direct3D11 funzionano 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. Il modo in cui ciò accade 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 quad pixel scelgono una cascata diversa rispetto al resto dei pixel. Ciò comporta la visualizzazione di cuciture visibili tra mappe ombreggiature perché gli offset basati su derivati sono ora completamente errati. 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 i kernel PCF
Indice dei kernel PCF all'esterno di una partizione a catena se il buffer shadow non è riempito. La soluzione consiste nel riempire il 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 cascata abbastanza grande da mantenere il bordo.
Mappe ombreggiature di varianza
VsMs (vedere varianza shadow maps by Donnelly e Lauritzen per altre informazioni) abilitare il filtro diretto della mappa shadow. Quando si usano LE MACCHINE VIRTUALI, è possibile usare tutta la potenza dell'hardware di filtro delle trame. È possibile usare il filtro trilineare e anisotropico (figura 15). Inoltre, le VM possono essere sfocate direttamente tramite la convoluzione. Le MACCHINE virtuali presentano 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 i CMS.
Figura 15. Filtro anisotropico
Dettagli algoritmo
Le MACCHINE VIRTUALI funzionano eseguendo il rendering della profondità e della profondità quadrata in una mappa shadow a due canali. Questa mappa shadow 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 di area pixel che supererebbe il test di profondità.
Il pixel shader recupera i valori di profondità e profondità al quadrato.
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 della profondità ha esito negativo, viene stimata la percentuale di pixel illuminata. La varianza viene calcolata come media dei quadrati meno quadrati di media.
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 delle VM è il sanguinamento leggero (figura 16). Il sanguinamento della luce si verifica quando più cast di ombreggiature si occudono l'un l'altro lungo i bordi. Le VSM oscurano i bordi delle ombre in base alle disparità di profondità. Quando le ombre si sovrappongono tra loro, esiste una disparità di profondità al 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
Una soluzione parziale al problema consiste nell'elevare fPercentLit a una potenza. Questo ha l'effetto di smorzare la sfocatura, 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 accesa a una potenza consiste nell'evitare configurazioni in cui le ombre si sovrappongono. Anche le configurazioni delle ombreggiature altamente ottimizzate hanno diversi vincoli sulla luce, la fotocamera e la geometria. Anche il sanguinamento leggero è diminuito usando trame con risoluzione più elevata.
Le mappe delle ombreggiature a varianza a più livelli (LVSM) risolvono il problema a scapito della rottura del frustum in livelli perpendicolare alla luce. Il numero di mappe necessarie sarebbe piuttosto elevato quando vengono usati anche i csm.
Inoltre, Andrew Lauritzen, co-autore del documento su VSMs, e autore di un documento su LVSMs, ha illustrato la combinazione di mappe shadow esponenziali (ESM) con VSMs per contrastare la fusione della luce in un Beyond3D Forum.
VSMs with CSMs
L'esempio VarianceShadow11 combina VSMs e CSM. La combinazione è piuttosto semplice. L'esempio segue gli stessi passaggi dell'esempio CascadedShadowMaps11. Poiché PCF non viene usato, le ombreggiature sono sfocate in una convoluzione separabile a due passaggi. L'uso di PCF consente anche all'esempio di usare matrici di trame invece di un atlas di trame. 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 informazioni, ad esempio il livello mipmap, necessario per il filtro. Questo 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. In questo modo si ottiene una giunzione frastagliata lungo la mappa dell'ombra.
Figura 17. Seams on cascade borders due to anisotropic filtering with divergent flow control
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 cascata selezionata. I derivati calcolati possono essere ridimensionati dalla parte di scala della matrice di 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 MACCHINE virtuali che il PCF tentano di approssimare la frazione di area in pixel che supererebbe il test di profondità. Le VM 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 MACCHINE virtuali confrontano una profondità dello spazio chiaro rispetto a un valore nella mappa di profondità dello spazio chiaro. Ciò significa che le MACCHINE virtuali non presentano gli stessi problemi di offset di PCF. Tecnicamente, le VM sono profondità di campionamento su un'area maggiore, nonché l'esecuzione di un'analisi statistica. Questo è meno preciso di PCF. In pratica, le MACCHINE VIRTUALI svolgono un ottimo lavoro di fusione, il che comporta una riduzione dell'offset necessario. Come descritto in precedenza, il numero uno svantaggio delle VM è il sanguinamento leggero.
Le MACCHINE virtuali e pcf rappresentano un compromesso tra potenza di calcolo GPU e larghezza di banda delle trame GPU. Per calcolare la varianza, le MACCHINE virtuali richiedono un maggior numero di calcoli matematici. PCF richiede una maggiore larghezza di banda della 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 GPU, le MACCHINE virtuali stanno diventando più pratiche dei due algoritmi. Le macchine virtuali di Gestione shadow sono inoltre più simili alle mappe ombreggiature a risoluzione inferiore a causa della fusione e del filtro.
Sommario
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 CSP per ridurre l'aliasing.
Referenze
Donnelly, W. e Lauritzen, A. Variance shadow maps. 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. mappa ombreggiatura a varianza a più livelli. Documentazione dell'interfaccia grafica 2008, 28-30 maggio 2008, Windsor, Ontario, Canada.
La Sezione 4. Mappe ombreggiate a cascata. ShaderX5 , tecniche di rendering avanzate, Wolfgang F. Buffer, Ed. Charles River Media, Boston, Massachusetts. 2006. pp. 197–206.