แสงเงาคู่จุดพาราโบลาในการตั้งค่าแสงที่เลื่อนออกไป


10

ฉันได้ลองเล่นกับโค้ดตัวอย่าง / กวดวิชานี้ซึ่งแสดงให้เห็นถึงการติดตั้ง light-pre-pass อย่างง่ายซึ่งเป็นประเภทของการตั้งค่าแสงที่เลื่อนออกไป

ฉันกำลังใช้การนำแสงเงาแบบชี้เข้ามาใช้แผนที่เงาคู่ ฉันทำตามคำอธิบายของ DPM นี้: http://gamedevelop.eu/en/tutorials/dual-paraboloid-shadow-mapping.htm

ฉันสามารถสร้างแผนที่เงาและดูเหมือนว่าจะดูดี

ฉันเชื่อว่าปัญหาปัจจุบันที่ฉันมีอยู่ในส่วนของพิกเซลที่มองหาค่าความลึกในแผนที่เงาเมื่อแสดงผลจุดไฟ

นี่คือรหัสจุดแสงของฉัน: http://olhovsky.com/shadow_mapping/PointLight.fx

ฟังก์ชั่น Pixel Shader PointLightMeshShadowPSที่น่าสนใจคือ

ไม่มีใครเห็นข้อผิดพลาดที่จ้องมองในฟังก์ชั่นนั้น?

หวังว่าใครบางคนเคยแก้ปัญหานี้มาก่อน :)

ป้อนคำอธิบายรูปภาพที่นี่

ป้อนคำอธิบายรูปภาพที่นี่

ดังที่คุณเห็นในภาพด้านบนเงาของโพสต์ไม่ตรงกับตำแหน่งของโพสต์ดังนั้นการแปลงบางอย่างผิดปกติ ...

นี่คือสิ่งที่ดูเหมือนว่าเมื่อแสงจุดใกล้มากกับพื้น (เกือบสัมผัสพื้นดิน)

ป้อนคำอธิบายรูปภาพที่นี่

เมื่อแสงจุดเคลื่อนที่ใกล้กับพื้นดินเงาจะมารวมกันและสัมผัสไปตามเส้นที่แผนที่เงาทั้งสองมาบรรจบกัน (นั่นคือตามแนวระนาบที่กล้องแสงถูกพลิกเพื่อจับภาพแผนที่เงาทั้งสอง)


แก้ไข:

ข้อมูลเพิ่มเติม:

ป้อนคำอธิบายรูปภาพที่นี่

เมื่อฉันย้ายจุดแสงให้ห่างจากจุดกำเนิดจะมีเส้นคู่ขนานกับเวกเตอร์ "ขวา" ของกล้องแสงที่จับเงา ภาพด้านบนแสดงผลลัพธ์ของการเลื่อนไฟจุดไปทางซ้าย ถ้าฉันย้ายไฟจุดไปทางขวาจะมีเส้นรูปวาดที่เทียบเท่ากันทางด้านขวาแทน ดังนั้นฉันคิดว่าสิ่งนี้บ่งชี้ว่าฉันกำลังเปลี่ยนแปลงบางอย่างในพิกเซลเชดเดอร์อย่างที่ฉันคิด


แก้ไข: เพื่อให้คำถามนี้ชัดเจนยิ่งขึ้นนี่คือโค้ดบางส่วน

นี่คือรหัสที่ฉันกำลังใช้การวาดเงาจุดไฟ วิธีนี้ใช้งานได้และใช้การจับคู่เงาตามที่คุณคาดหวัง

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

ตอนนี้นี่คือรหัสแสงจุดที่ฉันใช้ซึ่งมีข้อบกพร่องบางอย่างในการเปลี่ยนเป็นพื้นที่แสงเมื่อใช้แผนที่เงา:

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

และคุณบอกว่าแผนที่เงาเองนั้นไม่มีปัญหา / (ฉันหมายความว่าถ้าคุณเขียนเงาแผนที่ไปยังพื้นผิวแผนที่พวกเขาจะทำให้จุดที่ถูกต้องมืดลง?)
Ali1S232

คุณแน่ใจ 100% ว่า FOV ของการเรนเดอร์กล้องจากตำแหน่งแหล่งกำเนิดแสงถูกต้องหรือไม่?
Roy T.

การเรนเดอร์กล้องจากตำแหน่งแหล่งกำเนิดแสงไม่มีเมทริกซ์การฉายภาพเนื่องจากการฉายภาพถูกทำขึ้นเองเพื่อให้ได้รูปโค้งแบบพาราโบลา ฉันจะตรวจสอบรหัสนั้น แต่ความคิดที่ดี Roy T.
Olhovsky

Gajet: "ฉันหมายความว่าถ้าคุณเขียนเงาแผนที่ไปยังพื้นผิวแผนที่พวกเขาจะมืดแก้ไขจุด? แผนที่เงาเก็บเงาในพื้นที่แสงถ้าฉันดูแผนที่ไม่มีวิธีที่ง่ายที่จะรู้ว่ามันถูกต้องเพราะฉันเห็นมันในหน้าจอ "texturemap" คืออะไร - คุณหมายถึง texture แผนที่เงาเป็นพื้นผิว
Olhovsky

Roy T .: การเคลื่อนย้ายแสงรอบ ๆ แสดงให้เห็นว่าแผนที่เงาถูกตัดดังนั้นจึงมีปัญหากับการเปลี่ยนแปลงเมื่อใช้เงาจริง ๆ ไม่ใช่เฉพาะเมื่อสร้างขึ้น
Olhovsky

คำตอบ:


2

ด้วย PIX คุณสามารถดีบักพิกเซลแยกได้บางทีคุณอาจพบข้อผิดพลาดด้วยวิธีนี้ FOV หรือข้อผิดพลาดในการฉายภาพเป็นคำใบ้ที่ร้อนแรง หรือคุณลืมการเปลี่ยนแปลงของโลก!


คุณสามารถลองแก้จุดบกพร่องด้วย NVidia-fxComposer
Ali1S232

ฉันไม่คิดว่าการจ้องมองที่ค่ารหัสแอสเซมบลีจะช่วยฉันได้มาก ณ จุดนี้เนื่องจากฉันมีปัญหาในการทำความเข้าใจว่าการเปลี่ยนแปลงควรทำอย่างไรในตอนแรก ดังนั้นการดูว่าคุณค่าใดในการลงทะเบียน 10 หรือที่ใดก็ตามจะไม่ช่วยได้จริงๆ
Olhovsky

"หรือคุณลืมการเปลี่ยนแปลงของโลก!" จริง ๆ แล้วฉันลืมที่จะใช้การแปลงโลกเมื่อสร้างแผนที่เงา - doh! ตอนนี้ใช้งานได้แล้วปล่อยให้เฉดสีทั้งหมดเหมือนที่ฉันมี
Olhovsky

1

เฮ้ Olhovsky คำถามท้าทายที่ดี ฉันรู้ถึงความเจ็บปวดของคุณฉันได้ใช้การแรเงาแบบรอการตัดต่อแสงและเงาในงานสุดท้าย มันสนุกมากจริงๆ แต่ก็มีความเจ็บปวดมากมายเช่นกันเมื่อมันไม่ทำงานตามที่คาดไว้

ฉันคิดว่าคำแนะนำของ PIX นั้นดีจริง ๆ คุณไม่ต้องยุ่งกับคำสั่งแอสเซมเบลอร์ของ shader แต่คุณสามารถดูแผนที่เงาและเป้าหมายการเรนเดอร์อื่น ๆ เลือกพิกเซลและเรียกพิกเซล shader และก้าวผ่านมันและจุดสุดยอดของมัน

เทคนิคการดีบักทั่วไปสำหรับสถานการณ์ประเภทนี้รวมถึงการทำให้ฉากง่ายขึ้น

สิ่งหนึ่งที่อยู่ในใจของฉันคือ: วางกล้องในตำแหน่งเดียวกันกับแหล่งกำเนิดแสงที่มีค่า fovy และคุณลักษณะอื่น ๆ เช่นเดียวกับในบัตรผ่านแสง ตอนนี้คุณสามารถเปรียบเทียบค่าใน pixel-shader ได้อย่างง่ายดาย pixel-xy ใน render-pass ปกติสำหรับวัตถุปัจจุบันของคุณควรเหมือนกับ pixel-xy ที่คำนวณได้สำหรับการค้นหาใน shadowmap ตราบใดที่มีความละเอียดเท่ากัน

อีกอันหนึ่งคือเปลี่ยนเป็นการฉายภาพแบบออโธกราฟฟิคทำให้ง่ายต่อการคาดเดาและตรวจสอบได้ ยิ่งง่ายยิ่งดีคุณสามารถตรวจสอบแต่ละขั้นตอนการคำนวณได้

นอกเหนือจากนั้นคุณสามารถแสดงวิธีการสร้างเมทริกซ์ที่คำนวณตำแหน่งในแผนที่เงาสำหรับพิกเซลปัจจุบันนั่นคือการเปลี่ยนแปลงจากพื้นที่หน้าจอไปเป็นพื้นที่แสงหรือไม่


เพิ่งเห็นแผนที่เงาเป็น Parabloid ซึ่งทำให้ยากต่อการดีบั๊กและความคิดในการวางกล้องในตำแหน่งแสงเพื่อเปรียบเทียบตำแหน่งพิกเซลปัจจุบันและตำแหน่งในแผนที่เงาจะไม่ทำงานไม่เป็นไร :)
Maik Semder

หากคุณสนใจส่งอีเมลถึงฉันที่ kris@olhovsky.com และฉันจะตอบกลับด้วยสำเนาโครงการของฉัน มิฉะนั้น: CameraTransformเมทริกซ์เป็นเมทริกซ์โลกของกล้องที่กำลังดูฉากอยู่ LightViewProjเมทริกซ์เป็นจริงเพียงเมทริกซ์โลกของแสงเป็นเมทริกซ์มุมมองของแสงเป็นเพียงเมทริกซ์เอกลักษณ์
Olhovsky

คุณสามารถสร้างโครงการ C ++ อย่างง่ายได้หรือไม่ ควรมีการเปลี่ยนแปลงพาราโบริดที่เกี่ยวข้องใช่มั้ย
Maik Semder

การแปลงพาราโบลาอยู่ในส่วนพิกเซลที่ฉันเชื่อมโยงในคำถาม ทักษะ C ++ ของฉันถูก จำกัด เกินกว่าที่จะบีบอัดโครงการ C ++ อย่างรวดเร็วที่รวมเอาขั้นตอนการเรนเดอร์ที่เลื่อนออกไปทั้งหมดที่ฉันคิดไว้ :) อย่างไรก็ตามถ้าคุณชำนาญกับ C ++ แล้วฉันคิดว่ามันคงไม่ยากเกินไปที่จะอ่านรหัส C # ของฉัน โดยเฉพาะอย่างยิ่งเนื่องจากความกังวลส่วนใหญ่อยู่ใน pixel shader จริง ๆ และบางทีด้วย matricies ที่ส่งผ่านไป
Olhovsky

ฉันอ้างถึง g_mDPView และ g_mDPWorldView คุณสามารถแสดงวิธีการคำนวณได้อย่างไร
Maik Semder
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.