エンジンにVSM(およびESM)を実装しましたが、ネットワークで公開されている多くの例で期待した結果が得られませんでした。
シャドウマップのフィルタリングをGL_LINEARに設定しましたが、結果を通常のシャドウマップと比較すると、明らかに悪くなります。
ほとんどのチュートリアルと同じように、ポイントライトシェーダーで直接モーメントを計算するか、テクスチャから取得しましたが、結果は同じです。
コード:
uniform samplerCubeShadow shadowMap;
...
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d2=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
...
float shadowTexel=texture(shadowMap,vec4(coord.xyz,d2));
// VSM (Variance Shadow Map)
// get partial derivatives
float dx = dFdx(d2);
float dy = dFdy(d2);
vec2 moments = vec2(d2, d2*d2+0.25*(dx*dx+dy*dy));
return chebychevInequality(moments, shadowTexel);
このコードを使用すると、上の画像のような結果が得られます。また、samplerCubeShadowではなくsamplerCubeを使用しようとしましたが、結果はさらに悪化しました。まず、ハードシャドウを取得しました。第2に、他のテクスチャからモーメントを取得するときに必要なように、シャドウは領域を塗りつぶします。セカンドスクリーンを見てください。ここでは、生成されたキューブマップも確認します。3つのチャネルすべてにdepth / moment1を配置しても、デプスマップにあるものとは異なります。
瞬間を取得するためのシェーダー:
// Vartex shader
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// Fragment shader
float depth = v_position.z / v_position.w ;
depth = depth * 0.5 + 0.5; //Don't forget to move away from unit cube ([-1,1]) to [0,1] coordinate system
float moment1 = depth;
float moment2 = depth * depth;
// Adjusting moments (this is sort of bias per pixel) using derivative
float dx = dFdx(depth);
float dy = dFdy(depth);
moment2 += 0.25*(dx*dx+dy*dy) ;
FragColor = vec4( moment1,moment2, 0.0, 0.0 );
私は本当に行き詰まっています。私の問題をmiが解決するのを手伝ってくれることを願っています。
編集:
2番目の問題の解決策を見つけました。ブレンドを有効にしていたため、深度マップが間違っていました。
また、最初の問題の結果も良くなりましたが、シャドウマップの深度と比較するために、適切な深度で戦っています。
単純なSMでは、次のコードを使用します。
vec4 worldSpace=inverse(ViewMatrix)*vec4(pos,1);
vec4 coord=LightViewMatrix*worldSpace;
vec4 abs_coord=abs(coord);
float fs_z=-max(abs_coord.x, max(abs_coord.y, abs_coord.z));
vec4 clip=LightProjectionMatrix*vec4(0.0,0.0,fs_z,1.0);
float d=(clip.z / clip.w)*0.5+0.5; // clamp to [0..1]
ここで、posはビュースペース内の位置です。次に、以下を使用してシャドウマップから値を読み取ります。
texture(shadowMap,vec4(coord.xyz,d))
VSMでは、RG32FテクスチャのRチャネルに深度を保存します。深度値は次のように計算されます。
// VS
gl_Position=ModelViewProjectionMatrix*Vertex;
v_position=gl_Position;
// FS
float depth = v_position.z/v_position.w;
depth = depth * 0.5 + 0.5;
次に、ポイントライトのシェーダーで、(標準のSMのように)座標ベクトルを使用してシャドウマップから値を読み取ります。これで問題ありません。しかし、問題はこの部分にあります:
// No shadow if depth of fragment is in front
if ( moments.x <= distance)
return 1.0;
どの座標で距離を取得する必要がありますか?シャドウマップからの深度はどの座標にありますか?それは線形でなければなりませんか?誰かが私にそれを説明できますか?私は今少し混乱しています。これを取得するために多くの方法を試しましたが、すべての結果が期待どおりではありません。
EDIT2 : JarkkoLのヒントとこのチュートリアルに従って、コードを変更しました。今、私はこのコードを使用して深さを保存しています:
// VS
v_position=ModelViewMatrix*Vertex;
gl_Position=ProjectionMatrix*v_position;
// FS
const float Near = 0.1;
const float Far = 90.0; // camera far plane
const float LinearDepthConstant = 1.0 / (Far - Near);
float depth = length(v_position)*LinearDepthConstant;
そして私はそれを私がこのようにして得た値と比較します:
float dist=length( vec3(inverse(ViewMatrix)*vec4(pos,1.0)) - PointLight.position )*LinearDepthConstant; // pos is read from depth buffer and is in view space so I want invert it to world space as it was in tutorial
そしてここに結果があります:
赤い円で、立方体のマップ面の間に見える境界をマークしました。まだ何か問題があります。View Matrixを反転することで問題が発生する可能性があると思いますが、よくわかりません。