(SSR) Screen-Space Reflections Shader v0.7

NOTE: This thread is probably not going to be updated any more. You can check out the github page for this script instead

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

(Screenshots were taken using Intel Core i7 7500U integrated graphics)

This is a 2d filter that does screen-space reflections. Ray to Depth-Buffer intersections are found through a modified raymarch.

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. They are around line 20.


Glossy reflections are achieved through importance sampled montecarlo integration of the microfacet rendering equation; Currently, the GGX NDF is what is being sampled. Besides that, the shader uses Disney-style Fresnel (i.e. Schlick’s approximation once per specular ray and twice per diffuse ray), and the GGX Smith Geometry factor.

Random numbers are baked into the shader. Extra noise is achieved by manipulating the view vector.

TODO:

  • Use UPBGE multiple targets to get normal and position buffers, and maybe even per-pixel roughness.

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: I also made a (pretty bad) geometry-aware-blur filter to go along with this one. It should help if you want smoother reflections and don’t care about colours getting blurred.

Changelog:
v0.7: LINK
-Make raymarch steps a consistent size on 2d view space rather than on screen space. This reduces the amount of steps needed to reflect far-off things, and eliminates double-checking of the same pixel.
-Replace precalculated random numbers with a precalculated low-discrepancy sequence.
-Add jittering to rays, gets rid of an ugly artifact in exchange of some noise.
v0.6: LINK
-Improves Diffuse Fresnel.
-Fixes inconsistency with sign of normals and view vector.
-Does less repeated work by putting every useful value in a struct and passing it around (does passing a struct around have extra overhead in glsl?)
-Cleaner code
-Go back to simpler raymarch algorithm
v0.5: (doesn’t really exist)
-Improved BRDF
-Further cleaned up the code
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.

2 Likes

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?

Thank you.

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

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.

Are you using the camera projection matrix?

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);
    }else{
        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.

https://docs.blender.org/api/blender_python_api_2_76_0/bge.types.KX_Camera.html#bge.types.KX_Camera.projection_matrix

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:


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.

Attachments

GE_SSR_2.blend (743 KB)

1 Like

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:


SSGI + SSR = AWESOME:


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

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

LINK

enjoy.

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

How would i use it?

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

Great work!!!

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

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:

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

Is this right? :no:

Attachments

reflection.blend (574 KB)

@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.