Bayangan cahaya titik parabola ganda dalam pengaturan pencahayaan yang ditangguhkan

10

Saya telah bermain-main dengan kode contoh / tutorial ini yang menunjukkan implementasi sederhana pra-lulus cahaya, yang merupakan jenis pengaturan pencahayaan yang ditangguhkan.

Saya sedang dalam proses menerapkan titik cahaya bayangan, menggunakan peta bayangan parabola ganda. Saya mengikuti deskripsi DPM ini: http://gamedevelop.eu/en/tutorials/dual-paraboloid-shadow-mapping.htm

Saya dapat membuat peta bayangan, dan mereka tampak bagus.

Saya percaya bahwa masalah saat ini yang saya alami adalah di pixel shader saya yang mencari nilai kedalaman di peta bayangan saat merender titik lampu.

Berikut ini adalah kode shader light point saya: http://olhovsky.com/shadow_mapping/PointLight.fx

Fungsi pixel shader yang menarik adalah PointLightMeshShadowPS.

Adakah yang melihat kesalahan mencolok dalam fungsi itu?

Semoga seseorang telah mengatasi masalah ini sebelumnya :)

masukkan deskripsi gambar di sini

masukkan deskripsi gambar di sini

Seperti yang Anda lihat pada gambar di atas, bayangan posting tidak cocok dengan posisi tulisan, sehingga beberapa transformasi salah di suatu tempat ...

Ini terlihat seperti ketika titik cahaya sangat dekat dengan tanah (hampir menyentuh tanah).

masukkan deskripsi gambar di sini

Saat titik cahaya bergerak lebih dekat ke tanah, bayangan bergabung dan menyentuh sepanjang garis tempat dua peta bayangan bertemu (yaitu, di sepanjang pesawat tempat kamera cahaya dibalik untuk menangkap dua peta bayangan).


Edit:

Informasi lebih lanjut:

masukkan deskripsi gambar di sini

Ketika saya memindahkan titik cahaya dari asal, ada garis sejajar dengan vektor "kanan" kamera cahaya yang klip bayangan. Gambar di atas menunjukkan hasil dari memindahkan titik cahaya ke kiri. Jika saya memindahkan titik cahaya ke kanan, ada garis kliping yang setara di sebelah kanan. Jadi saya pikir ini menunjukkan bahwa saya mengubah sesuatu yang tidak benar dalam pixel shader, seperti yang saya kira.


Sunting: Untuk membuat pertanyaan ini lebih jelas, berikut adalah beberapa kode.

Berikut adalah kode yang saat ini saya gunakan untuk menggambar titik cahaya yang gelap . Ini berfungsi, dan menggunakan pemetaan bayangan seperti yang Anda harapkan.

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;
}

Sekarang di sini adalah kode titik cahaya yang saya gunakan, yang memiliki semacam bug dalam transformasi menjadi ruang cahaya saat menggunakan peta bayangan:

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;
}
Olhovsky
sumber
dan Anda mengatakan bayangan peta sendiri tidak memiliki masalah / (maksud saya jika Anda membakar peta bayangan ke texturemap mereka akan menggelapkan tempat yang benar?)
Ali1S232
Apakah Anda 100% yakin FOV kamera yang dirender dari posisi sumber cahaya sudah benar?
Roy T.
Render kamera dari posisi sumber cahaya tidak memiliki matriks proyeksi, karena proyeksi dilakukan secara manual untuk memiliki paraboloid warp. Saya akan memeriksa kode itu, ide bagus Roy T.
Olhovsky
Gajet: "Maksud saya jika Anda membakar peta bayangan ke ke texturemap mereka akan menggelapkan tempat yang benar?" Peta bayangan menyimpan bayangan di ruang cahaya, jika saya melihat peta, tidak ada cara mudah untuk mengetahui dengan pasti bahwa mereka benar karena saya melihatnya di layar. Apa itu "texturemap" - maksud Anda tekstur? Peta bayangan adalah tekstur.
Olhovsky
Roy T .: Memindahkan cahaya mengungkap bahwa peta bayangan akan terpotong, sehingga ada masalah dengan transformasi saat benar-benar menggunakan bayangan, bukan hanya saat membuatnya.
Olhovsky

Jawaban:

2

Dengan PIX Anda dapat men-debug piksel yang terisolasi, mungkin Anda menemukan kesalahan dengan cara ini. FOV atau kesalahan proyeksi adalah isyarat panas. Atau apakah Anda lupa transformasi dunia ?!

christoph
sumber
Anda juga dapat mencoba men-debug dengan NVidia-fxComposer
Ali1S232
Saya tidak berpikir bahwa menatap nilai-nilai kode assembly akan sangat membantu saya pada saat ini, karena saya mengalami kesulitan memahami bagaimana transformasi harus dilakukan sejak awal. Jadi melihat nilai apa di register 10, atau di mana saja, tidak akan membantu.
Olhovsky
"Atau kamu lupa transformasi dunia ?!" Sebenarnya saya lupa menerapkan transformasi dunia saat membuat peta bayangan - doh! Ini bekerja sekarang, meninggalkan semua shader seperti yang saya miliki.
Olhovsky
1

Hai Olhovsky, pertanyaan menantang yang bagus. Saya tahu rasa sakit Anda, saya menerapkan Deferred Shading, Inferred Lighting, dan bayangan dalam pekerjaan terakhir saya. Itu benar-benar menyenangkan, tetapi banyak rasa sakit juga ketika itu tidak bekerja seperti yang diharapkan.

Saya pikir saran dengan PIX sebenarnya bagus. Anda tidak perlu dipusingkan dengan instruksi assembler dari shader, tetapi Anda dapat melihat peta bayangan dan target render lainnya, pilih piksel dan panggil pixel shader-nya dan melangkahinya dan juga vertex-shader-nya.

Trik debug umum untuk situasi semacam ini termasuk penyederhanaan adegan.

Salah satu yang terlintas di pikiran saya adalah: letakkan kamera pada posisi yang sama dengan sumber cahaya dengan sifat yang sama dan atribut lainnya seperti pada pass pencahayaan. Sekarang Anda dapat dengan mudah membandingkan nilai dalam pixel-shader. Pixel-xy pada render-pass normal untuk objek Anda saat ini harus sama dengan pixel-xy yang dihitung untuk pencarian dalam shadowmap, selama ia memiliki resolusi yang sama.

Yang lain adalah beralih ke proyeksi ortografis, membuat sesuatu mudah, dapat diprediksi, dan dapat diperiksa. Semakin sederhana semakin baik Anda dapat memeriksa setiap langkah perhitungan.

Selain itu, dapatkah Anda menunjukkan bagaimana Anda membuat matriks yang menghitung posisi dalam bayangan-peta untuk piksel saat ini, yaitu transformasi dari layar-ruang ke ruang-cahaya?

Maik Semder
sumber
Hanya melihat bayangan peta adalah Parabloid, yang membuatnya lebih sulit untuk di-debug dan gagasan untuk menempatkan kamera pada posisi lampu untuk membandingkan posisi piksel saat ini dan posisi di peta bayangan tidak akan bekerja, tidak masalah :)
Maik Semder
Jika Anda tertarik, kirimkan saya email di [email protected], dan saya akan membalas dengan salinan proyek saya. Jika tidak: CameraTransformMatriks ini sebenarnya adalah matriks dunia dari kamera yang saat ini melihat pemandangan. The LightViewProjmatrix sebenarnya hanya matriks dunia cahaya, sebagai pandangan matriks cahaya hanya matriks identitas.
Olhovsky
Bisakah Anda membuat proyek C ++ sederhana dengan itu? Seharusnya juga ada transformasi parabloid yang terlibat kan?
Maik Semder
Transformasi parabola ada di pixel shader yang saya tautkan dalam pertanyaan. Keterampilan C ++ saya terlalu terbatas untuk menghidupkan proyek C ++ cepat yang merangkum seluruh pipa render yang ditangguhkan saya pikir :) Namun, jika Anda mahir dengan C ++, maka saya pikir seharusnya tidak terlalu sulit untuk membaca kode C # saya. Terutama karena sebagian besar kekhawatirannya benar-benar ada di pixel shader, dan mungkin dengan matrik yang diteruskan ke sana.
Olhovsky
Saya merujuk ke g_mDPView dan g_mDPWorldView. Bisakah Anda menunjukkan bagaimana mereka dihitung.
Maik Semder