I’m using (DX11) LookAtLH or LookTo functions to get my view matrix. When setting Camera Direction so that the y component is zero I have the correct reflexion (picture left). When changing the Dir.y to negative or positive values I have picture on middle or right. In my calculation this corresponds to a rotation on the x axis (e.g. parallel to screen). I’m not using arcball but keyboard input to modify the rotationx/y params. Roty -= 0.005f*iRotate;//iRotate =0 or 1 if keyboard left/right arrow pressed //iMove 0/1 is up/down arrow pressed Rotx += 0.005f*iLift;//iLift 0/1 if pageup/pagedown pressed m_WorldCamera.m_Dir = XMVector3Transform(XMVectorSet(1, 0, 0, 0), XMMatrixRotationY(Roty)*XMMatrixRotationX(Rotx)); m_WorldCamera.m_Eye += m_WorldCamera.m_Dir*(float)iMove XMVECTOR DEye = m_WorldCamera.m_Eye+XMVectorSet(0, -25, 0, 0); gView = XMMatrixLookToLH(DEye, m_WorldCamera.m_Dir, XMVectorSet(0, 1, 0, 0)); My SSR normal map is calculated in the shader as NSSR = Mesh.Normal*(float3x3)(ObjectWorldMat*CameraView). Is there something important I missed to be able to get the correct reflexion whatever the camera rotationX angle? [![enter image description here][1]][1] [1]: https://i.sstatic.net/u6AtF.png On the left you can see the generated textures used in my SSR shader. 1) Bumpped normals in view space (txNormal2); 2) unmodified mesh normals in view space used for reflexion calculation (txNormal1); 3) 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. 4) Depth buffer for main scene full res, not used in the SSR shader; 5) 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); }