My SSR normal map is calculated in the shader as NSSR = Mesh.Normal*(float3x3)(World*ViewObjectWorldMat*CameraView).
On the left you can see the generated textures used in my SSR shader.
Bumpped normals in view space (txNormal2);
unmodified mesh normals in view space used for reflexion calculation (txNormal1);
Layers of the scene (txdiffuse1). In the topright part 1/2 screen resolution for main scene and other parts 1/4 resolution for layers containing occluded objects.
Depth buffer for main scene full res, not used in the SSR shader;
Depth buffer (txDepth1) for layers texture (txdiffuse1).
Using layers at 1/2 and 1/4 resolution gives the shader below for 3 layers including the main scene. The resulting color is blended to the main target. Note that doing everything in full screen resolution gives me the same problem.
static const float2 TpDecal[4] = {{0,0}, {0.25,0}, {0,0.25} , {0.25,0.25} };
#define STEP 2.5*0.02
#define ITER 30
#define STEP2 0.5f
#define BUMP 0.075f
float4 PS_PostDeferredReflex3(PS_INPUTQUAD Input) : SV_TARGET
{
//the screen quad mesh used for rendering has regular 0..1 texture coordinates.
//As we use 1/2 resolution from the topright quad of txDiffuse1
//we modify Input.Tex.xy as UV to sample topright quad.
float2 UV = mad(Input.Tex, 0.5, float2(0.5,0));
//sample 1/2 resolution Depth
float D = txDepth1.SampleLevel(sPoint, UV, 0).r;
if ( D > 0.99999 ) return float4(0,0,0,0);
//sample full screen resolution mesh normals
float2 NN = txNormal1.SampleLevel(sPoint, Input.Tex, 0);
if ( NN.r == 0 ) return float4(0,0,0,0);
//the line below uses precalculated value from the inverse projection matrix (only _11, __22,_34 and _43 values are necessary) to obtain Pos in view space with a mad op.
float3 PosV = float3(mad(Input.Tex.xy, float2( 0.707243978977203, -0.397824734449387),float2( -0.353621989488602, 0.198912367224693)), 1)/(-0.998999953269959*D+1);
// line above equivalent to
// float4 PosV = mul(float4(mad(Input.Tex.xy, float2( 2, -2),float2( -1, 1)), D, 1), InvProj);
// PosV/=PosV.w;
NN = mad(NN,2,-1);
//normals are stored in view space as RG format.
//We retrieve B value below with Pithagorean theorem.
float3 VSDir = normalize(reflect(normalize(PosV),float3(NN.rg, 1-sqrt(dot(NN.rg, NN.rg)))));
#ifdef BUMP // add some deviation in the ray based on the bumpy normals in txNormal2
//not the origin of my problem
NN = txNormal2.SampleLevel(sPoint, Input.Tex, 0);
NN = mad(NN,2,-1);
VSDir += VSDir*float3(NN.rg, 1-sqrt(dot(NN.rg, NN.rg)))*BUMP;
#endif
float3 SSray = float3(UV, D);//set the start 1/2 resolution
//line below calculate the end of ray in full screen
float4 SSEnd = mul ( float4(PosV + VSDir*500, 1), Proj);
SSEnd/= SSEnd.w;
//adjust the result to half screen, topright panel (z component
//not affected) and do the difference to SSray to give the
//maximum length of the ray in screen space.
SSEnd.xyz=mad(SSEnd.xyz, float3(0.5,0.5,1), float3(0.5,0,0))-SSray;
float3 SSDir = SSEnd.xyz*STEP;//set the step for 1/2 res
float2 SSDir2 = SSDir.xy*STEP2;//set the step UV for 1/4 res
float2 TpRay = Input.Tex*0.25;//set the start UV for 1/4 res
float2 DZH;
float3 EndRay;
float3 MinRay;
float3 MaxRay;
float2 EndRay2;
float3 T;
float3 ZBufferVal;
int i=0;
while (i<ITER)
{
i++;
EndRay = SSray;
EndRay2 = TpRay;
SSray+=SSDir;//increments 1/2 res
TpRay+=SSDir2;//increments 1/4 res
ZBufferVal.r = txDepth1.SampleLevel(sPoint, SSray.xy, 0);//sample 1/2 res
ZBufferVal.g = txDepth1.SampleLevel(sPoint, TpRay.xy, 0)+0.00002f;//sample 1/4 layer 1, if Z.g=Z.r the offset ensure selection of the main higher res layer
ZBufferVal.b = txDepth1.SampleLevel(sPoint, TpRay.xy+TpDecal[1], 0)+0.00002f;//sample 1/4 layer 2
T = SSray.zzz-ZBufferVal;//select the final layer based on comparison of T.rgb and DZH.x
DZH = float2(0.005, 0);//set a starting maximum value for DZH.x. The DZH.y component will contain the layer index (1-3)
//this conditionnal assignment performs better than if statments
DZH = (( T.r>=0 )&& (T.r<DZH.x))?float2(T.r,1):DZH;
DZH = (( T.g>=0 )&& (T.g<DZH.x))?float2(T.g,2):DZH;
DZH = (( T.b>=0 )&& (T.b<DZH.x))?float2(T.b,3):DZH;
if ( DZH.y>0) //if layer encoutered by the ray
if ( DZH.x < 0.0005) //if delta depth < 0.0005
{
MinRay = EndRay;//refinement loop
MaxRay = SSray;
//if not main layer 1 (1/2 res) modify the MinMaxRay
//UV coordinates according to the 1/4 res viewports
//decals stored in TPDecal
if(DZH.y>1)
{
float2 dtp = TpDecal[DZH.y-2];
MinRay.xy = EndRay2+dtp;
MaxRay.xy = TpRay+dtp;
}
for(int j = 0; j < 8; j++)//do refinement loop
{
EndRay = (MinRay+MaxRay)*0.5f;
T.r = txDepth1.SampleLevel(sPoint, EndRay.xy, 0);
if ( EndRay.z > T.r) MaxRay = EndRay; else MinRay = EndRay;
}
if (abs(EndRay.z-T.r)<0.000025) //output color if delta depth low enought with blend according to ray length done
{
return float4(txDiffuse2.SampleLevel(sPoint, EndRay.xy, 0).rgb,1-clamp((EndRay.z-D)/SSEnd.z, 0, 1));
}
}
}
return float4(0,0,0,0);
}