遅延照明設定での二重放物面ポイントライトシャドウ


10

私はこのチュートリアル/サンプルコードをいじってみました。このサンプルコードは、遅延照明設定の一種であるlight-pre-passの簡単な実装を示しています。

デュアル放物面シャドウマップを使用して、ポイントライトシャドウを実装しているところです。このDPMの説明に従っています:http : //gamedevelop.eu/en/tutorials/dual-paraboloid-shadow-mapping.htm

シャドウマップを作成できましたが、見栄えが良いようです。

私が現在抱えている問題は、ポイントライトをレンダリングするときにシャドウマップで深度値を検索するピクセルシェーダーにあると思います。

これが私のポイントライトシェーダーコードです:http : //olhovsky.com/shadow_mapping/PointLight.fx

対象のピクセルシェーダー関数はですPointLightMeshShadowPS

誰かがその関数に目立ったエラーを見ていますか?

うまくいけば、誰かが前にこの問題に取り組みました:)

ここに画像の説明を入力してください

ここに画像の説明を入力してください

上の画像からわかるように、投稿の影は投稿の位置と一致していないため、変換がどこかで間違っています...

これは、ポイントライトが地面に非常に近い(ほとんど地面に触れている)場合の外観です。

ここに画像の説明を入力してください

ポイントライトが地面に近づくと、シャドウが集まり、2つのシャドウマップが交わる線に沿って(つまり、ライトカメラが反転して2つのシャドウマップをキャプチャした平面に沿って)影が接触します。


編集:

さらに詳しい情報:

ここに画像の説明を入力してください

ポイントライトを原点から遠ざけると、ライトカメラの「右」のベクトルに平行な線が影を切り取ります。上の画像は、ポイントライトを左に移動した結果を示しています。ポイントライトを右に移動すると、代わりに右側に同等のクリッピングラインがあります。だから、これは私が思ったように、ピクセルシェーダーで何かを間違って変換していることを示していると思います。


編集:この質問をより明確にするために、ここにいくつかのコードを示します。

これが、シャドウスポットライトを描画するために現在使用しているコードです。これは機能し、期待どおりにシャドウマッピングを使用します。

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

光源の位置からのカメラレンダリングのFOVが正しいことを100%確信していますか?
ロイT.

光源は放物面ワープにするために手動で行われるため、光源の位置からのカメラレンダリングには投影行列がありません。私はそのコードをチェックしますが、良い考えですRoy T.
Olhovsky

ガジェット:「シャドウマップをテクスチャマップに書き込むと、正しいスポットが暗くなるのですか?」シャドウマップは、ライトスペースにシャドウを格納します。マップを見ると、スクリーンスペースに表示されているため、それらが正しいことを確認する簡単な方法はありません。「テクスチャマップ」とは何ですか-テクスチャを意味しますか?シャドウマップテクスチャです。
Olhovsky

Roy T .:ライトを移動すると、シャドウマップがクリップされることが明らかになるため、シャドウを作成するときだけでなく、実際にシャドウを使用するときに変換に問題があります。
Olhovsky

回答:


2

PIXを使用すると、分離されたピクセルをデバッグできます。この方法でエラーを見つけることができます。FOVまたは投影エラーはホットなヒントです。または、世界の変革を忘れましたか?!


NVidia-fxComposerでデバッグを試すこともできます
Ali1S232

そもそも、変換をどのように行うべきかを理解するのに苦労しているので、アセンブリコードの値を見つめるだけでは、現時点ではあまり役に立たないと思います。したがって、レジスター10の値がどこにあるかを確認しても、実際には役に立ちません。
Olhovsky

「または、世界の変革を忘れましたか?!」シャドウマップを作成するときに、ワールドトランスフォームを適用するのを忘れていました-doh!これは現在機能しており、すべてのシェーダーをそのまま使用できます。
Olhovsky

1

ちょっとOlhovsky、いい挑戦的な質問。私はあなたの痛みを知っています、私は私の最後の仕事で据え置きのシェーディング、推測された照明と影を実装しました。本当に楽しかったですが、期待通りに動かなかったときも大変な苦労でした。

PIXのアドバイスは、実際には良いものだと思います。シェーダーのアセンブラー命令をいじる必要はありませんが、シャドウマップやその他のレンダーターゲットを見て、ピクセルを選択してそのピクセルシェーダーを呼び出し、そのピクセルシェーダーとその頂点シェーダーをステップ実行できます。

このような状況での一般的なデバッグトリックには、シーンの簡略化が含まれます。

頭に浮かぶのは、ライティングパスと同じように、カメラを光源と同じ位置に、同じような濃淡とその他の属性で配置することです。これで、ピクセルシェーダーの値を簡単に比較できます。現在のオブジェクトの通常のレンダーパスのpixel-xyは、同じ解像度である限り、シャドウマップのルックアップで計算されたpixel-xyと同じである必要があります。

もう1つは、正射投影に切り替えて、何かを簡単、予測可能、チェック可能にすることです。単純なほど、各計算ステップを確認できます。

それ以外に、現在のピクセルのシャドウマップ内の位置、つまり画面空間から光空間への変換を計算する行列を作成する方法を示すことができますか?


ただ、シャドウマップを:)用事、しません作業、シャドウマップでは、現在のピクセル位置と位置を比較するために、ライトの位置にカメラを置くことさえ難しく、デバッグやアイデアにしますParabloid、されて見て
はMaik Semderを

興味があれば、kris @ olhovsky.com宛てにメールを送ってください。プロジェクトのコピーを返信します。それ以外の場合:CameraTransformマトリックスは、実際には現在シーンを表示しているカメラのワールドマトリックスです。LightViewProj光のビュー行列は、単に単位行列であるようなマトリックスは、実際に光のちょうど世界の行列です。
Olhovsky

それで簡単なC ++プロジェクトを作成できますか?放物面の変換も必要でしょうか?
Maik Semder

放物面変換は、質問でリンクしたピクセルシェーダーにあります。私のC ++スキルは、私が考える遅延レンダリングパイプライン全体をカプセル化する簡単なC ++プロジェクトを太らせるにはあまりにも制限があります:)ただし、C ++に習熟している場合は、C#コードを読むのはそれほど難しくないと思います。特に、ほとんどの懸念は実際にはピクセルシェーダーにあり、おそらくマトリックスが渡されます。
Olhovsky

私はg_mDPViewとg_mDPWorldViewを参照していました。計算方法を教えてください。
Maik Semder
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.