(SSR) Screen-Space Reflections Shader v0.4

(SebastianMestre) #1

NOTE: this post was rewritten since this thread was originally started.

This is a 2d filter that does raymarching in screen space. Ray to Depth-Buffer intersections are found through a simple raymarch and refined with binary search.

Glossiness is achieved through the emission of multiple rays with importance sampled directions; Currently, the GGX NDF is used for this purpose. A home-brewed hashing function is the source source of the pseudo-random numbers used in sampling and jittering.

Schlick’s approximation is sampled once per ray to ensure proper weighting of rays.

The filter expects three properties to be added to the object containing the filter actuator:

  • Float - roughness (roughness of the reflections)
  • Float - reflectance (reflectance at normal incidence)
  • Integer - samples (amount of rays used per pixel)

There are also some settings within the filter that should be modified to match your camera setup.

16 samples. 9 resolve samples. roughness 0.2.

REFLECTIONS ONLY - 16 samples. 9 resolve samples. roughness 0.1.

A word on performance:
It runs really poorly (16 samples at 2fps on my pc, 2 samples at around 20). use only one or two samples and some amount of resolve samples. The new RNG and resolve filter makes using such low sample counts actually feasible.

-Turn it into a surface shader, this should enable normal, roughness, and specular maps to be used.

Get the filter here : LINK

This filter uses the functions used for reconstruction of screen space made by martinsh for his deferred render filter, as well as a modification of one of the functions made by TheLumCoin.

If you want to use this on your project, please let me know and credit me if you do.

NOTE: resolve was done in a separate filter pass using this filter

v0.4: LINK
-Improved jittering.
-Improved Random-Number-Generator
-Implemented Cook-Torrance BRDF
-Implemented Schlick’s approximation to Smith’s shadowing factor
-Cleaned up implementation
-Corrected gamma correction
-Somewhat improved interface
-Improved performance
-Implemented a better fallback method that suddenly stopped working and i don’t know why but nothing else broke so i am just leaving it in in its current broken state.
v0.3: LINK
-Added simple jittering.
-Added GGX sampling (and a few others if you are willing to look through the code. )
v0.2: LINK
-Added Blinn based glossy reflections and importance sampling.
-Improved all kinds of stuff.
v0.1: LINK
-Added binary search to the ray-buffer intersection. To disable this just set startScale (line 24) to 1.0.
v0.0: LINK
-Original “alpha” release.

(John_tgh) #2

First of all, that looks amazing! Kudos for the great work! : D

Second, what do you mean by this?

Apparently i have done something horribly wrong, but i can’t figure out what that something is.

You getting some kind of error?

(SebastianMestre) #3

Thank you.

No errors, the shader looks totally not as it should.

(SebastianMestre) #4

Update: i just figured out that my issue is with detecting the intersection of rays with the screen space.

This is my raycast loop.

        // vec3 position is the current screen space position of the ray.
        // vec2 sample is the pixel coordinates of the ray.
        // vec3 project is the screen space position of the geometry at the ray's pixel coordinates.
        // vec3 reflect is the reflection vector.

        // colided and isValid are int's that i am using as bools, they are also being multiplied over the reflection buffer to get rid of invalid reflections.

        for(int i = 0; i < 100 && colided == 0 && isValid == 1; i++){
            position = position - reflect * stepSize;
            sample   = getViewCoord(position);
            project  = getViewPosition(sample,sample);

            if(position.z > project.z){
                colided = 1;
            //clip ray coordinates to the view frustum
            if(sample.x < 0.0 || sample.x > 1.0 || sample.y < 0.0 || sample.y > 1.0 || position.z < 0.0){
                isValid = 0;

This is my collision conditional

            if(position.z > project.z){
                colided = 1;

this is what i am currently doing, a simple comparison between Z values.

I have also tried to do as follows:

        //lenSq returns a float with the squared length of each vector (the length of a screen space vector is also its distance to the camera)
            if(lenSq(position) > lenSq(project)){
                colided = 1;

that did not work, either.

Does anyone have any insight into collision detection of screen space rays?

I feel like i’ve hit a wall with this shader.

(BluePrintRandom) #5

Are you using the camera projection matrix?

(SebastianMestre) #6

I am not using the camera projection matrix, since i don’t know how to retrieve it. IF you can tell me or link me to some useful resource that has informatin on the matter then i’d be grateful.

Despite that, i have made some progress. But reflections only seem to be working on really sharp angles, when i get more perpendicular to a face then reflections disappear. I suspect this is because i am not using the Camera projection matrix to get the view vector and, instead, i’m doing some really sketchy math to calculate it.

here is an image of working reflections

here is an image of the point where reflections stop working:

this is my main function

void main(){
    float stepSize = 0.01;
    vec3 position = getViewPosition(texCoord, texCoord);
    vec3 reflect  = getViewReflection(texCoord, texCoord);
    position = position + reflect * stepSize * position.z;
    vec2 sample   = getViewCoord(position);
    vec3 project  = getViewPosition(sample, sample);
    vec2 equirct  = dirToEquir(reflect);
    vec4 spec;
    float fresnel = schlick(0.1);
    reflect.x = reflect.x * reflect.z;
    reflect.y = reflect.y * reflect.z;
    int colided   = 0;
    int isValid   = 1;
        for(int i = 0; i < 200 && colided == 0 && isValid == 1; i++){
            position = position + reflect * stepSize * position.z;
            sample   = getViewCoord(position);
            project  = getViewPosition(sample,sample);
            if(position.z > project.z){
                colided = 1;
            // clip to camera frustum
            if(sample.x < 0.0 || sample.x > 1.0 || sample.y < 0.0 || sample.y > 1.0 || position.z < 0.0){
                isValid = 0;
    if(colided == 1){
        spec = texture2D(bgl_RenderedTexture,sample);
        spec = texture2D(bgl_RenderedTexture,equirct);
    gl_FragColor = mix(texture2D(bgl_RenderedTexture,texCoord),spec,fresnel);

NOTE: weird streaks on reflections are caused by my bad alternative method of chosing a reflection color when SSR fails, not by SSR itself.

(BluePrintRandom) #7


(SebastianMestre) #8

After some thinking i realized that i have no need for a matrix, since i’m doing all my calculations in screen space.
I am still sort of wondering where tf i am supposed to get my view vector from. For now i’ll just keep my whacky maths and see how that works out in the long run.

I changed some things around and i’m now getting some better quality reflections and better performance. I still have the issue with reflections not apearing at large incidence angles, and i am not sure what to do about it, also reflections are skewed depending on where on the screen they are (posibly due to the way i am getting the view vector). Both of these issues are aliviated when i use the embeded game engine without being in camera view. Not sure why this is though.

I like having an image on every update so here is one:

(HG1) #9

I don’t read your thread.

I write here because I already have SSR shader for Blender 2.46. It works up to Blender 2.49b.
But it does not work correctly with the newer Blender versions.
I never released it because of the smooth surface issue (no screen space normal map) and without reflection map it was not possible to change the reflection individually for the objects.

Here is the shader. Maybe it will help you anyway.


GE_SSR_2.blend (743 KB)

(SebastianMestre) #10

After some examination of the functions made by Martinsh (the ones that i am using to reconstruct the view space), I figured out what was going on, it was rather stupid. It turns out that those functions assume an FOV of 90 to work properly, wich meant that all my view vectors were sligthly skewed one way or the other. After setting my camera to have an FOV of 90 degrees, both issues were resolved. I will now implement a correction factor.

I have done some general optimization to the code and i will publish it here once i have added the FOV correction. An actual finished version on a surface shader will come at a later date.

relfections only:


(Lostscience) #11

I like the reflections only version.I think that would be grerat for a videogame.

(SebastianMestre) #12

OK. Here is the filter. I have added the fov correction thing and cleaned up the code a bunch.



EDIT: There are some values in the script that have to be modified to match your current settings

(Lostscience) #13

How would i use it?

(SebastianMestre) #14

Just like you would use any type of 2d filter. Just stick it in a text datablock and call it using the custom filter option of the 2d filter actuator inside the game logic editor.

Reflections only are achieved by setting reflectance to 1 in the shader. There is a whole section of user selected values denoted by a bunch of hash symbols

(BluePrintRandom) #15

Great work!!!

(SebastianMestre) #16

Thank you! This was a real struggle to figure out. :slight_smile:

(adriansnetlis) #17

This looks nice. I suggest you to add in roughness now. One good way would be using a noise normalmap and sampling it a few times(each time at different coordinate offset) and get the average color of calculated. This would give the blurry roughness effect. :slight_smile:

(Thatimster) #18

You might be able to use the mipmap function from UPBGE, but not too sure about normal BGE.

(Lostscience) #19

Is this right? :no:


reflection.blend (574 KB)

(HG1) #20

@Lostscience. To get it working you need to change the camera values in the shader (line 14 to 17), according to your actual viewing camera setup.