Ombre luminose a doppio punto paraboloide nella configurazione dell'illuminazione differita


10

Ho giocato con questo tutorial / codice di esempio che dimostra una semplice implementazione di light-pre-pass, che è un tipo di impostazione dell'illuminazione differita.

Sono in procinto di implementare le ombre di luce puntiforme, usando mappe d'ombra a doppio paraboloide. Sto seguendo questa descrizione di DPM: http://gamedevelop.eu/en/tutorials/dual-paraboloid-shadow-mapping.htm

Sono in grado di creare le mappe delle ombre e sembrano avere un bell'aspetto.

Credo che l'attuale problema che sto riscontrando sia nel mio pixel shader che cerca un valore di profondità nella mappa delle ombre durante il rendering delle luci puntiformi.

Ecco il mio codice shader punto luce: http://olhovsky.com/shadow_mapping/PointLight.fx

La funzione di pixel shader di interesse è PointLightMeshShadowPS.

Qualcuno vede un errore evidente in quella funzione?

Spero che qualcuno abbia affrontato questo problema prima :)

inserisci qui la descrizione dell'immagine

inserisci qui la descrizione dell'immagine

Come puoi vedere nelle immagini sopra, le ombre del post non corrispondono alle posizioni dei post, quindi alcune trasformazioni sono sbagliate da qualche parte ...

Questo è come appare quando la luce del punto è molto vicino al suolo (quasi toccando il suolo).

inserisci qui la descrizione dell'immagine

Mentre la luce del punto si avvicina al suolo, le ombre si uniscono e si toccano lungo la linea in cui si incontrano le due mappe delle ombre (ovvero lungo il piano in cui è stata capovolta la videocamera per catturare le due mappe delle ombre).


Modificare:

Ulteriori informazioni:

inserisci qui la descrizione dell'immagine

Quando sposto la luce puntiforme lontano dall'origine, c'è una linea parallela al vettore "giusto" della videocamera luminosa che ritaglia l'ombra. L'immagine sopra mostra il risultato dello spostamento della luce del punto a sinistra. Se sposto il punto luce a destra, sulla destra c'è invece una linea di ritaglio equivalente. Quindi penso che questo indichi che sto trasformando qualcosa di errato nel pixel shader, come pensavo.


Modifica: per rendere più chiara questa domanda, ecco alcune parti di codice.

Ecco il codice che attualmente uso per disegnare una luce spot ombreggiata . Funziona e utilizza la mappatura dell'ombra come ti aspetteresti.

VertexShaderOutputMeshBased SpotLightMeshVS(VertexShaderInput input)
{
    VertexShaderOutputMeshBased output = (VertexShaderOutputMeshBased)0;    
    output.Position = mul(input.Position, WorldViewProjection);

    //we will compute our texture coords based on pixel position further
    output.TexCoordScreenSpace = output.Position;
    return output;
}

//////////////////////////////////////////////////////
// Pixel shader to compute spot lights with shadows
//////////////////////////////////////////////////////
float4 SpotLightMeshShadowPS(VertexShaderOutputMeshBased input) : COLOR0
{
    //as we are using a sphere mesh, we need to recompute each pixel position into texture space coords
    float2 screenPos = PostProjectionSpaceToScreenSpace(input.TexCoordScreenSpace) + GBufferPixelSize;
    //read the depth value
    float depthValue = tex2D(depthSampler, screenPos).r;

    //if depth value == 1, we can assume its a background value, so skip it
    //we need this only if we are using back-face culling on our light volumes. Otherwise, our z-buffer
    //will reject this pixel anyway

    //if depth value == 1, we can assume its a background value, so skip it
    clip(-depthValue + 0.9999f);

    // Reconstruct position from the depth value, the FOV, aspect and pixel position
    depthValue*=FarClip;

    //convert screenPos to [-1..1] range
    float3 pos = float3(TanAspect*(screenPos*2 - 1)*depthValue, -depthValue);

    //light direction from current pixel to current light
    float3 lDir = LightPosition - pos;

    //compute attenuation, 1 - saturate(d2/r2)
    float atten = ComputeAttenuation(lDir);

    // Convert normal back with the decoding function
    float4 normalMap = tex2D(normalSampler, screenPos);
    float3 normal = DecodeNormal(normalMap);

    lDir = normalize(lDir);

    // N dot L lighting term, attenuated
    float nl = saturate(dot(normal, lDir))*atten;

    //spot light cone
    half spotAtten = min(1,max(0,dot(lDir,LightDir) - SpotAngle)*SpotExponent);
    nl *= spotAtten;

    //reject pixels outside our radius or that are not facing the light
    clip(nl -0.00001f);

    //compute shadow attenuation

    float4 lightPosition = mul(mul(float4(pos,1),CameraTransform), MatLightViewProjSpot);

    // Find the position in the shadow map for this pixel
    float2 shadowTexCoord = 0.5 * lightPosition.xy / 
                            lightPosition.w + float2( 0.5, 0.5 );
    shadowTexCoord.y = 1.0f - shadowTexCoord.y;
    //offset by the texel size
    shadowTexCoord += ShadowMapPixelSize;

    // Calculate the current pixel depth
    // The bias is used to prevent floating point errors 
    float ourdepth = (lightPosition.z / lightPosition.w) - DepthBias;

    nl = ComputeShadowPCF7Linear(nl, shadowTexCoord, ourdepth);

    float4 finalColor;

    //As our position is relative to camera position, we dont need to use (ViewPosition - pos) here
    float3 camDir = normalize(pos);

    // Calculate specular term
    float3 h = normalize(reflect(lDir, normal));
    float spec = nl*pow(saturate(dot(camDir, h)), normalMap.b*50);
    finalColor = float4(LightColor * nl, spec); 

    //output light
    return finalColor * LightBufferScale;
}

Ora ecco il codice luce punto che sto usando, che ha una sorta di bug nella trasformazione in spazio luce quando si usano le mappe d'ombra:

VertexShaderOutputMeshBased PointLightMeshVS(VertexShaderInput input)
{
    VertexShaderOutputMeshBased output = (VertexShaderOutputMeshBased)0;    
    output.Position = mul(input.Position, WorldViewProjection);

    //we will compute our texture coords based on pixel position further
    output.TexCoordScreenSpace = output.Position;
    return output;
}

float4 PointLightMeshShadowPS(VertexShaderOutputMeshBased input) : COLOR0
{
    // as we are using a sphere mesh, we need to recompute each pixel position 
    // into texture space coords
    float2 screenPos = 
        PostProjectionSpaceToScreenSpace(input.TexCoordScreenSpace) + GBufferPixelSize;

    // read the depth value
    float depthValue = tex2D(depthSampler, screenPos).r;

    // if depth value == 1, we can assume its a background value, so skip it
    // we need this only if we are using back-face culling on our light volumes. 
    // Otherwise, our z-buffer will reject this pixel anyway
    clip(-depthValue + 0.9999f);

    // Reconstruct position from the depth value, the FOV, aspect and pixel position
    depthValue *= FarClip;

    // convert screenPos to [-1..1] range
    float3 pos = float3(TanAspect*(screenPos*2 - 1)*depthValue, -depthValue);

    // light direction from current pixel to current light
    float3 lDir = LightPosition - pos;

    // compute attenuation, 1 - saturate(d2/r2)
    float atten = ComputeAttenuation(lDir);

    // Convert normal back with the decoding function
    float4 normalMap = tex2D(normalSampler, screenPos);
    float3 normal = DecodeNormal(normalMap);

    lDir = normalize(lDir);

    // N dot L lighting term, attenuated
    float nl = saturate(dot(normal, lDir))*atten;

    /* shadow stuff */

    float4 lightPosition = mul(mul(float4(pos,1),CameraTransform), LightViewProj);

    //float4 lightPosition = mul(float4(pos,1), LightViewProj);
    float posLength = length(lightPosition);
    lightPosition /= posLength;

    float ourdepth = (posLength - NearClip) / (FarClip - NearClip) - DepthBias;
    //float ourdepth = (lightPosition.z / lightPosition.w) - DepthBias;

    if(lightPosition.z > 0.0f)
    {
        float2 vTexFront;
        vTexFront.x =  (lightPosition.x /  (1.0f + lightPosition.z)) * 0.5f + 0.5f; 
        vTexFront.y =  1.0f - ((lightPosition.y /  (1.0f + lightPosition.z)) * 0.5f + 0.5f);    

        nl = ComputeShadow(FrontShadowMapSampler, nl, vTexFront, ourdepth);
    }
    else
    {
        // for the back the z has to be inverted        
        float2 vTexBack;
        vTexBack.x =  (lightPosition.x /  (1.0f - lightPosition.z)) * 0.5f + 0.5f; 
        vTexBack.y =  1.0f - ((lightPosition.y /  (1.0f - lightPosition.z)) * 0.5f + 0.5f); 

        nl = ComputeShadow(BackShadowMapSampler, nl, vTexBack, ourdepth);
    }

    /* shadow stuff */

    // reject pixels outside our radius or that are not facing the light
    clip(nl - 0.00001f);

    float4 finalColor;
    //As our position is relative to camera position, we dont need to use (ViewPosition - pos) here
    float3 camDir = normalize(pos);

    // Calculate specular term
    float3 h = normalize(reflect(lDir, normal));
    float spec = nl*pow(saturate(dot(camDir, h)), normalMap.b*100);
    finalColor = float4(LightColor * nl, spec);

    return finalColor * LightBufferScale;
}

e dici che le stesse mappe d'ombra non stanno avendo problemi / (Voglio dire se bruciando le mappe d'ombra nella mappa del testo si oscurano i punti corretti?)
Ali1S232

Sei sicuro al 100% che il FOV del rendering della telecamera dalla posizione della sorgente luminosa sia corretto?
Roy T.

Il rendering della telecamera dalla posizione della sorgente luminosa non ha una matrice di proiezione, poiché la proiezione viene eseguita manualmente al fine di ottenere l'ordito paraboloide. Controllerò quel codice, buona idea Roy T.
Olhovsky,

Gajet: "Voglio dire, se si bruciano le mappe d'ombra sulla mappa del testo, scuriranno i punti corretti?" Le mappe delle ombre memorizzano le ombre nello spazio luminoso, se guardo la mappa, non c'è modo semplice di sapere con certezza che sono corrette perché le vedo nello spazio dello schermo. Che cos'è una "texturemap": intendi una trama? Le mappe d'ombra sono trame.
Olhovsky,

Roy T .: Spostare la luce rivela che la mappa dell'ombra viene troncata, quindi c'è un problema con la trasformazione quando si utilizza effettivamente l'ombra, non solo quando la si crea.
Olhovsky,

Risposte:


2

Con PIX puoi eseguire il debug di pixel isolati, forse trovi l'errore in questo modo. FOV o un errore di proiezione è un suggerimento caldo. O hai dimenticato la trasformazione del mondo ?!


puoi anche provare a eseguire il
debug

Non penso che fissare i valori del codice assembly mi aiuterà molto a questo punto, perché ho difficoltà a capire come dovrebbe essere fatta la trasformazione in primo luogo. Quindi, vedere qual è il valore nel registro 10, o ovunque, non sarà davvero d'aiuto.
Olhovsky,

"O hai dimenticato la trasformazione del mondo ?!" In realtà ho dimenticato di applicare la trasformazione del mondo durante la creazione delle mappe d'ombra - doh! Funziona ora, lasciando tutti gli shader come li avevo io.
Olhovsky,

1

Ehi Olhovsky, bella domanda stimolante. Conosco il tuo dolore, ho implementato Deferred Shading, Inferred Lighting e shadows nel mio ultimo lavoro. È stato davvero divertente, ma anche molto dolore quando non ha funzionato come previsto.

Penso che il consiglio con PIX sia in realtà buono. Non devi fare confusione con le istruzioni dell'assemblatore dello shader, ma puoi guardare le mappe d'ombra e altri target di rendering, selezionare un pixel e chiamare il suo pixel shader e attraversarlo e anche il suo vertice-shader.

Trucchi di debug generali per questo tipo di situazioni includono la semplificazione della scena.

Uno che mi viene in mente è: metti la telecamera nella stessa posizione della sorgente luminosa con lo stesso aspetto fovy e altri attributi del passaggio di illuminazione. Ora puoi facilmente confrontare i valori nel pixel-shader. Il pixel-xy nel normale rendering-pass per l'oggetto corrente dovrebbe essere uguale al pixel-xy calcolato per la ricerca nella shadowmap, purché abbia la stessa risoluzione.

Un altro è passare alla proiezione ortografica, rendere qualcosa di facile, prevedibile e verificabile. Più semplice è, meglio è possibile controllare ogni passaggio di calcolo.

Oltre a questo, puoi mostrare come si crea la matrice che calcola la posizione nella mappa ombra per il pixel corrente, ovvero la trasformazione da spazio-schermo a spazio-luce?


Solo vedere la mappa d'ombra è un parabloide, che rende ancora più difficile il debug e l'idea di mettere la fotocamera nella posizione delle luci per confrontare la posizione attuale dei pixel e la posizione nella mappa d'ombra non funzionerà, non
importa

Se sei interessato, inviami una email a kris@olhovsky.com e risponderò con una copia del mio progetto. Altrimenti: la CameraTransformmatrice è in realtà la matrice mondiale della telecamera che sta visualizzando la scena. La LightViewProjmatrice è in realtà solo la matrice mondiale della luce, poiché la matrice della vista della luce è solo la matrice dell'identità.
Olhovsky,

Puoi realizzare un semplice progetto C ++ con esso? Dovrebbe esserci anche la trasformazione parabloide in gioco, giusto?
Maik Semder,

La trasformazione paraboloide è nel pixel shader che ho collegato alla domanda. Le mie abilità in C ++ sono troppo limitate per creare un rapido progetto C ++ che incapsuli l'intera pipeline di rendering differita, penso :) Tuttavia, se sei abile con C ++, penso che non dovrebbe essere troppo difficile leggere il mio codice C #. Soprattutto dal momento che la maggior parte della preoccupazione è proprio nel pixel shader, e forse con le matrici passate ad esso.
Olhovsky,

Mi riferivo a g_mDPView e g_mDPWorldView. Puoi mostrare come vengono calcolati.
Maik Semder
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.