Jump to content
  • Advertisement
Sign in to follow this  

Screen Space Reflection Optimization

This topic is 1674 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi all,

I'm interested working on screen space reflection because it's the future.

Here the actual code I have, learned from a book :

struct PS_INPUT
  float4 Position : SV_POSITION;
  float3 Normal   : NORMAL;
  float4 Tangent  : TANGENT;
  float2 TexCoord : TEXCOORD0;
  float4 PosVS    : TEXCOORD1;

cbuffer PROJECTION_CBUFFER : register( b0 )
  float4x4 Projection;
  float4 PerspectiveValues;

cbuffer SSR_CBUFFER : register( b1 )
  float ViewAngleThreshold;
  float EdgeDistThreshold;
  float DepthBias;
  float ReflectionScale;

Texture2D DiffuseMap : register( t0 );
Texture2D DepthMap : register( t1 );
SamplerState LinearSampler : register( s0 );
SamplerState PointSampler : register( s1 );

float ConvertZToLinearDepth( in float DepthValue )
  return PerspectiveValues.z / (DepthValue - PerspectiveValues.w);

float3 PosFromDepth( in float2 uv, in float LinearDepth )
  return float3( PerspectiveValues.xy * uv * LinearDepth, LinearDepth );

static const float PixelSize = 2.0f / 720.0f;
static const int NumSteps = 1280;

float4 main( in PS_INPUT Input ) : SV_TARGET
  // Normalize the normal.
  float3 NormalVS = normalize( Input.Normal );
  // Compute the camera to pixel direction.
  float3 EyeToPixel = normalize( Input.PosVS.xyz );
  // Compute the reflected view direction.
  float3 ReflectVS = reflect( EyeToPixel, NormalVS );
  // The initial reflection color for the pixel.
  float4 ReflectColor = float4( 0.0f, 0.0f, 0.0f, 0.0f );
  // Check the angle using threshold.
  if( ReflectVS.z >= ViewAngleThreshold )
    // Fade the reflection as the view angles gets close to the threshold.
    float ViewAngleThresholdInv = 1.0f - ViewAngleThreshold;
    float ViewAngleFade = clamp( 3.0f * (ReflectVS.z - ViewAngleThreshold) / ViewAngleThresholdInv, 0.0f, 1.0f );
    // Transform the View Space Reflection to clip-space.
    float3 PosReflectVS = Input.PosVS.xyz + ReflectVS;
    float3 PosReflectCS = mul( float4( PosReflectVS, 1.0f ), Projection ).xyz / PosReflectVS.z;
    float3 ReflectCS = PosReflectCS - Input.Position.xyz;
    // Resize Screen Space Reflection to an appropriate length.
    float ReflectScale = PixelSize / length( ReflectCS.xy );
    ReflectCS *= ReflectScale;
    // Compute the first sampling position in screen-space.
    float2 SampPos = float2( 0.5f, -0.5f ) * (Input.Position.xy + ReflectCS.xy) + 0.5f;
    // Find each iteration step in screen-space.
    float2 Step = ReflectCS.xy * float2( 0.5f, -0.5f );
    // Build a plane laying on the reflection vector.
    float4 RayPlane;
    float3 Right = cross( EyeToPixel, ReflectVS );
    RayPlane.xyz = normalize( cross( ReflectVS, Right ) );
    RayPlane.w = dot( RayPlane.xyz, Input.PosVS.xyz );
    // Iterate over the texture searching for intersection.
    for( int CurStep = 0; CurStep < NumSteps; ++CurStep )
      // Get the current depth.
      float CurDepth = DepthMap.SampleLevel( PointSampler, SampPos, 0.0f ).x;
      // Reconstruct the position from depth.
      float CurDepthLin = ConvertZToLinearDepth( CurDepth );
      float3 CurPos = PosFromDepth( Input.Position.xy + ReflectCS.xy * ((float)CurStep + 1.0f), CurDepthLin );
      // The intersection happens between two positions on the oposite sides of the plane.
      if( RayPlane.w >= dot( RayPlane.xyz, CurPos ) + DepthBias )
        // Compute the actual position on the ray for the given depth value.
        float3 FinalPosVS = Input.PosVS.xyz + (ReflectVS / abs(ReflectVS.z)) * abs(CurDepthLin - Input.PosVS.z + DepthBias);
        float2 FinalPosCS = FinalPosVS.xy / PerspectiveValues.xy / FinalPosVS.z;
        SampPos = float2( 0.5f, -0.5f ) * FinalPosCS.xy + 0.5f;
        // Get the value at the current screen space location.
        ReflectColor.rgb = DiffuseMap.SampleLevel( LinearSampler, SampPos, 0.0f ).rgb;
        // Fade out samples as they get close to the texture edges.
        float EdgeFade = clamp( distance( SampPos, float2( 0.5f, 0.5f ) ) * 2.0f - EdgeDistThreshold, 0.0f, 1.0f );
        // Compute the fade value.
        ReflectColor.a = min( ViewAngleFade, 1.0f - EdgeFade * EdgeFade );
        // Apply the reflection scale.
        ReflectColor.a *= ReflectionScale;
        // Advance past the final iteration to break the loop.
        CurStep = NumSteps;
      // Advance to the next sample.
      SampPos += Step;
  // Return the reflect color.
  return ReflectColor;

This part is because it's fixed-size for test :

static const float PixelSize = 2.0f / 720.0f;
static const int NumSteps = 1280;

The problem is that cost very a lot.

Does a way exists to have a quality value ?


Edited by Alundra

Share this post

Link to post
Share on other sites

Well 1200 samples per pixel is pretty much. Is there a way to early out the loop? I mean, when you find an intersection, can you quit the loop?


[edit] seems that your loop has an early out condition.


I think that using a down scaled depth buffer might help to accelerate the intersection finding. Ie. you can discard easily bigger blocks of data where the intersection doesn't happen for sure. 



Edited by kauna

Share this post

Link to post
Share on other sites

A possible improvement is to swap from a fixed step march and instead use a depth hierarchy that you can traverse as you get further along your ray. You can use lower resolution depths for longer rays/distant steps and get away with some pretty good speed increases. You can also use the hierarchy for detecting invalid tiles on the screen that clearly wouldn't have an intersection (quad-tree style), allowing you to skip portions of the screen that a pixel will never hit.

Share this post

Link to post
Share on other sites
Sign in to follow this  

  • Advertisement

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!