Skip to main content
1 of 4
philB
  • 159
  • 9

Last hole artifact in screen space refection with Hierarchical Z

After lot of efforts I have an almost nice shader doing SSR with HiZ. The shader is provided below. Some parameters are tunable and you can speed up things but the difficulty in my scene (picture) are the spheres that limit the coarsiness of the ray marching. So the benefit of HiZ is not as high as expected for this scene but there’s some (130 FPS instead of 110-120, 960x540). Unfortunately there is a last annoying problem I was not able to solve in this SSR HiZ shader. The problem is shown in green on the left picture and on right what I have without HiZ. It is annoying because I did not have this problem with my regular SSR shader whitout HiZ. I have tried a thickness test like in the McGuirre method but it is not working.

 e.g : if abs ( ray.z-zbuff)< thickness.

enter image description here

So if someone can help below is my shader. enjoy if you're interested.

In contrast to other codes available on websites (this one was easy to implement and give fine results) I have decoupled the increasing/decreasing Mip level parts. A first while advances the ray with increasing Mip and raystep until ray.z>zbuff. Then starting from the last position where ray.z<zbuff a second loop works with only Mip=0. Finally a refinement step is done. Doing it this way avoids the single inc/dec mip while loop found elsewhere that I found not so fast enought for me at the end. Also other codes are using the concept of boundary cells and I had difficulties to understand how they calculate the ray increase from these cells.

 //parameter for finer or coarser primary while results
 #define offset0 0.02
 #define offsetR 2
 //my initial resolution (Mip=0)
 static const int2 Resolution = {960, 540};
 //uv offset1 : critical to ensure correct uv sampling. modulate the values higher than 1 to see effect
 static const float2 offset1 = {1, 1.035};
 //parameter for finer or coarser secondary while results
 #define offset2  1.15
 //parameter for final dicotomic refinement
 #define refinemax 3
 //parameter for removing vertical artifact for objects not on ground
 //based on the normal of the object
 #define dot3falloff 0.00005

 float4 PS_PostDeferredReflex(PS_INPUTQUAD Input) : SV_TARGET
 {
     float D = txDepth1.SampleLevel(samPoint, Input.Tex, 0).r;
     if ( D == 1 ) return float4(0,0,0,0);
     //only the ground mesh is alpha=1 to receive reflections
     if ( txDiffuse1.SampleLevel(samPoint, Input.Tex, 0).a == 0)  return float4(0,0,0,0);
     //calculalate raydir in view space
     float3 PosV = float3(InvProj.x*(Input.Tex.x*2-1),InvProj.y*(1-Input.Tex.y*2), 1)/(InvProj.z*D+1);

     float3 VSDir = normalize(reflect(normalize(PosV.xyz),txNormal1.SampleLevel(samPoint, Input.Tex, 0).rgb*2-1));    
     if ( VSDir.z < 0 ) return float4(0,0,0,0);//filter off ray towards camera

the ray dir initilization method in screen space comes from here

     float4 SSEnd = mul(float4(PosV + VSDir*200, 1), Proj);
     SSEnd/= SSEnd.w;
     SSEnd.xy = SSEnd.xy*float2(0.5,-0.5)+float2(0.5,0.5); 
     float3 SSray = float3(Input.Tex, D);
     float3 SSDir = normalize(SSEnd.xyz-SSray);
     float dSS;
     if ( abs(SSDir.x) < abs(SSDir.y) ) dSS = abs(SSDir.y); else dSS = abs(SSDir.x);
     SSDir/=dSS;//"normalize" SSDir according to longest x or y
     SSDir*=offset0;//modulate step size
     int Mip = 0;//you can start at higher Mip level but I don't see improvements
     //also starting with higher Mip level produces a loss of reflection at the bottom screen 
     float ZBufferVal;
     float2 CurResol;
     float3 curSSDir = SSDir*pow(2,Mip)*offsetR;//pow useless at Mip=0

primary while : move ray with Mip-dependant increasing steps until ray.z > zbuffer

     while (Mip<10)//my resolution corresponds to 10 miplevel
     {
             //using the offset1 ensure correct uv sampling. remove to see what i mean
             //use Load as below or SampleLevel
             //CurResol = (Resolution>>Mip)*SSray.xy*offset1;
             //ZBufferVal = txDepth1.Load(int3(CurResol, Mip)).r;
             ZBufferVal = txDepth1.SampleLevel(samPoint, SSray.xy*offset1, Mip).r; //or use Load as above
             if ( ZBufferVal==0 ) ZBufferVal=1; //avoid disappearing reflexions at bottom screen
             if (SSray.z > ZBufferVal) break;
             SSray += curSSDir;
             if ( Mip <10 ) 
             {
                 Mip++;
                 curSSDir*=2;
             }
    }
    if (SSray.z>=1) return float4(0,0,0,0);

secondary while : move ray from last position (z<zbuffer) now only at level Mip = 0 until ray.z > zbuffer

         float3 poslast = SSray;
         SSDir*=offset2;
         while (ZBufferVal!=0)//otherwise you should be outbound according to DX specs
         {
             SSray += SSDir;
             ZBufferVal = txDepth1.SampleLevel(samPoint, SSray.xy, 0).r;    
             if (SSray.z > ZBufferVal) break;
             poslast = SSray;
         }

refinement step

         float3 MinRay = poslast;
         float3 MaxRay = SSray;
         for(int j = 0; j < refinemax; j++)
         {
             poslast = (MinRay+MaxRay)*0.5f;
             ZBufferVal = txDepth1.SampleLevel(samPoint, poslast.xy, 0).r;
             if ( poslast.z > ZBufferVal) MaxRay = poslast; else MinRay = poslast;
         }

some alpha checking before returning the color. remember D is the depth at start

         float3 N = txNormal1.SampleLevel(samPoint, poslast.xy,0).rgb*2-1;
         //N.x+N.y+N.z>2.99f is for the spot lights (red/blue..) not lited. If femoved the spotlight are not reflected
         float Dot3 = ( N.x+N.y+N.z>2.99f)?dot3falloff:(dot(-VSDir, N)<0)?0:dot3falloff;
         if (abs(poslast.z-ZBufferVal)<Dot3) 
             return float4(txDiffuse1.SampleLevel(samPoint, poslast.xy, 0).rgb, 1-abs(poslast.z-D)*500);
         return float4(0,0,0,0);
     }
philB
  • 159
  • 9