[RELEASED] Reflection Manager

Reflection Manager v0.4

“High Quality, Baked Refections”

Reflection Manager is a new plugin for Unity, designed to allow for simple creation, and use, of high quality reflection probes.
These probes can be pre-baked and are more efficient than screen space reflection solutions. Reflection probes are assigned to static and dynamic objects at run-time, allowing for beautiful efficient reflections, even on moving objects. The novel parallax correction technique demonstrated below is so efficient that it runs smoothly on today’s mobile devices.

Purchase Reflection Manager on the Asset Store

Check out the demo
Web Demo
WASD movement, Space bar to fire dynamic objects, P to toggle parallax. Hold left mouse button to look.

Windows Phone Demo

Download Oculus Demo

Transparency Demo

Web Demo

About the demo
Parallax correction is a cheap technique designed to make the most out of cubemaps/reflection probes. The technique can run on ipad level hardware.

Proper gloss handling allows for metallic surfaces

CubeMapper handles dynamic reflections using this same technique


Coming in version 0.5



Reflection Manager automatically links dynamic objects to the nearest valid reflection probe, and efficiently and seamlessly switches reflection probes as the objects move around the world. Additional ‘room’ geometry ensures dynamic objects link to the most appropriate cubemap, not just the closest.

Preview reflection probes as you generate them in editor, tweak settings and rebake all, or just one, in a single click.

What is parallax correction?
Standard cubemap reflection math assumes the reflections are infinitely far away. By supplying additional convex data (a cuboid in this case) we are able to accurately project planer reflections.

Standard cubemap projection:

Parallax corrected cubemap projection:

Feature List

  • One click baking of Reflection Probes
  • Parallax corrected volumes, provide amazing, cheap, reflections.
  • Automatic (runtime) linking to static AND dynamic objects. Dynamic objects cross fade between probes.
  • Reflection probe sets can share common settings, and can be mass re-baked.
  • Preview reflection probes are previewed in the editor, adjust mip bias to ensure smooth reflections
  • Two different baking implementations provided
  • Support for dynamic reflection probes

Credits
Dozens of production quality, royalty free textures provided by: GameTextures.com
Door Model provided by: GameArt.me
Additional content re-purposed from Unity samples.

Documentation
Reflection Manager Help
Quick Start Guide [FIRST DRAFT]

Change Log

Version 0.4
---------------
- Smoothly transition dynamic objects from one parallax volume to another
- Dynamic objects can optionally link to the closest probe ( m_ConsiderLinkingVolumes = false ).  This is useful for outdoor scenes
- Added ability to access alternate parallax volume bounds.  See dynamic objects sample
- Removed incorrect license from the project directory

I’d love to try this out - I’m just concerned about shader implementation. At the moment I’m using Shader Forge, creating an unlit/texture type shader. I’m definitely in the market for a way to add nice reflections!

this is the current simple shader I’m using:

// Shader created with Shader Forge Beta 0.33 
// Shader Forge (c) Joachim Holmer - http://www.acegikmo.com/shaderforge/
// Note: Manually altering this data may prevent you from opening it in Shader Forge
/*SF_DATA;ver:0.33;sub:START;pass:START;ps:flbk:,lico:1,lgpr:1,nrmq:1,limd:1,uamb:False,mssp:True,lmpd:False,lprd:False,enco:False,frtr:True,vitr:True,dbil:False,rmgx:True,rpth:1,hqsc:True,hqlp:False,blpr:0,bsrc:0,bdst:1,culm:0,dpts:2,wrdp:True,ufog:True,aust:True,igpj:False,qofs:0,qpre:1,rntp:1,fgom:False,fgoc:False,fgod:False,fgor:False,fgmd:0,fgcr:0.5,fgcg:0.5,fgcb:0.5,fgca:1,fgde:0.01,fgrn:0,fgrf:300,ofsf:0,ofsu:0,f2p0:False;n:type:ShaderForge.SFN_Final,id:1,x:32719,y:32712|diff-2-RGB,spec-82-OUT,gloss-272-OUT,amdfl-291-OUT;n:type:ShaderForge.SFN_Tex2d,id:2,x:33154,y:32542,ptlb:MainTex,ptin:_MainTex,tex:59e1e8b0203f6564bb9ae1af71b35908,ntxv:2,isnm:False;n:type:ShaderForge.SFN_Vector1,id:82,x:33154,y:32724,v1:0;n:type:ShaderForge.SFN_Slider,id:272,x:33154,y:32813,ptlb:Gloss slider,ptin:_Glossslider,min:0,cur:0,max:1;n:type:ShaderForge.SFN_Vector1,id:291,x:33154,y:32925,v1:1;proporder:2-272;pass:END;sub:END;*/

Shader "Shader Forge/unlit_texture_SF" {
    Properties {
        _MainTex ("MainTex", 2D) = "black" {}
        _Glossslider ("Gloss slider", Range(0, 1)) = 0
    }
    SubShader {
        Tags {
            "RenderType"="Opaque"
        }
        Pass {
            Name "PrePassBase"
            Tags {
                "LightMode"="PrePassBase"
            }
            
            
            Fog {Mode Off}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #define UNITY_PASS_PREPASSBASE
            #include "UnityCG.cginc"
            #pragma exclude_renderers xbox360 ps3 flash d3d11_9x 
            #pragma target 3.0
            uniform float _Glossslider;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float4 posWorld : TEXCOORD0;
                float3 normalDir : TEXCOORD1;
            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o;
                o.normalDir = mul(float4(v.normal,0), _World2Object).xyz;
                o.posWorld = mul(_Object2World, v.vertex);
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                return o;
            }
            fixed4 frag(VertexOutput i) : COLOR {
                i.normalDir = normalize(i.normalDir);
                float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
/////// Normals:
                float3 normalDirection =  i.normalDir;
                return fixed4( normalDirection * 0.5 + 0.5, _Glossslider );
            }
            ENDCG
        }
        Pass {
            Name "PrePassFinal"
            Tags {
                "LightMode"="PrePassFinal"
            }
            ZWrite Off
            
            Fog {Mode Off}
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #define UNITY_PASS_PREPASSFINAL
            #include "UnityCG.cginc"
            #pragma multi_compile_prepassfinal
            #pragma exclude_renderers xbox360 ps3 flash d3d11_9x 
            #pragma target 3.0
            uniform sampler2D _LightBuffer;
            #if defined (SHADER_API_XBOX360)  defined (HDR_LIGHT_PREPASS_ON)
                sampler2D _LightSpecBuffer;
            #endif
            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            uniform float _Glossslider;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv0 : TEXCOORD0;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 posWorld : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
                float4 projPos : TEXCOORD3;
            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o;
                o.uv0 = v.uv0;
                o.normalDir = mul(float4(v.normal,0), _World2Object).xyz;
                o.posWorld = mul(_Object2World, v.vertex);
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.projPos = ComputeScreenPos (o.pos);
                COMPUTE_EYEDEPTH(o.projPos.z);
                return o;
            }
            fixed4 frag(VertexOutput i) : COLOR {
                i.normalDir = normalize(i.normalDir);
                float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
/////// Normals:
                float3 normalDirection =  i.normalDir;
////// Lighting:
                half4 lightAccumulation = tex2Dproj(_LightBuffer, UNITY_PROJ_COORD(i.projPos));
                #if defined (SHADER_API_GLES) || defined (SHADER_API_GLES3)
                    lightAccumulation = max(lightAccumulation, half4(0.001));
                #endif
                #ifndef HDR_LIGHT_PREPASS_ON
                    lightAccumulation = -log2(lightAccumulation);
                #endif
                #if defined (SHADER_API_XBOX360)  defined (HDR_LIGHT_PREPASS_ON)
                    lightAccumulation.w = tex2Dproj (_LightSpecBuffer, UNITY_PROJ_COORD(i.projPos)).r;
                #endif
/////// Diffuse:
                float3 diffuse = lightAccumulation.rgb * 0.5;
////// Specular:
                float node_82 = 0.0;
                float3 specularColor = float3(node_82,node_82,node_82);
                float3 specular = lightAccumulation.rgb*lightAccumulation.a * specularColor;
                float3 finalColor = 0;
                float3 diffuseLight = diffuse;
                float node_291 = 1.0;
                diffuseLight += float3(node_291,node_291,node_291); // Diffuse Ambient Light
                float2 node_307 = i.uv0;
                finalColor += diffuseLight * tex2D(_MainTex,TRANSFORM_TEX(node_307.rg, _MainTex)).rgb;
                finalColor += specular;
/// Final Color:
                return fixed4(finalColor,1);
            }
            ENDCG
        }
        Pass {
            Name "ForwardBase"
            Tags {
                "LightMode"="ForwardBase"
            }
            
            
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #define UNITY_PASS_FORWARDBASE
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdbase_fullshadows
            #pragma exclude_renderers xbox360 ps3 flash d3d11_9x 
            #pragma target 3.0
            uniform float4 _LightColor0;
            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            uniform float _Glossslider;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv0 : TEXCOORD0;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 posWorld : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
                LIGHTING_COORDS(3,4)
            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o;
                o.uv0 = v.uv0;
                o.normalDir = mul(float4(v.normal,0), _World2Object).xyz;
                o.posWorld = mul(_Object2World, v.vertex);
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                TRANSFER_VERTEX_TO_FRAGMENT(o)
                return o;
            }
            fixed4 frag(VertexOutput i) : COLOR {
                i.normalDir = normalize(i.normalDir);
                float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
/////// Normals:
                float3 normalDirection =  i.normalDir;
                float3 lightDirection = normalize(_WorldSpaceLightPos0.xyz);
                float3 halfDirection = normalize(viewDirection+lightDirection);
////// Lighting:
                float attenuation = LIGHT_ATTENUATION(i);
                float3 attenColor = attenuation * _LightColor0.xyz;
/////// Diffuse:
                float NdotL = dot( normalDirection, lightDirection );
                float3 diffuse = max( 0.0, NdotL) * attenColor;
///////// Gloss:
                float gloss = _Glossslider;
                float specPow = exp2( gloss * 10.0+1.0);
////// Specular:
                NdotL = max(0.0, NdotL);
                float node_82 = 0.0;
                float3 specularColor = float3(node_82,node_82,node_82);
                float3 specular = (floor(attenuation) * _LightColor0.xyz) * pow(max(0,dot(halfDirection,normalDirection)),specPow) * specularColor;
                float3 finalColor = 0;
                float3 diffuseLight = diffuse;
                float node_291 = 1.0;
                diffuseLight += float3(node_291,node_291,node_291); // Diffuse Ambient Light
                float2 node_308 = i.uv0;
                finalColor += diffuseLight * tex2D(_MainTex,TRANSFORM_TEX(node_308.rg, _MainTex)).rgb;
                finalColor += specular;
/// Final Color:
                return fixed4(finalColor,1);
            }
            ENDCG
        }
        Pass {
            Name "ForwardAdd"
            Tags {
                "LightMode"="ForwardAdd"
            }
            Blend One One
            
            
            Fog { Color (0,0,0,0) }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #define UNITY_PASS_FORWARDADD
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
            #pragma multi_compile_fwdadd_fullshadows
            #pragma exclude_renderers xbox360 ps3 flash d3d11_9x 
            #pragma target 3.0
            uniform float4 _LightColor0;
            uniform sampler2D _MainTex; uniform float4 _MainTex_ST;
            uniform float _Glossslider;
            struct VertexInput {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float2 uv0 : TEXCOORD0;
            };
            struct VertexOutput {
                float4 pos : SV_POSITION;
                float2 uv0 : TEXCOORD0;
                float4 posWorld : TEXCOORD1;
                float3 normalDir : TEXCOORD2;
                LIGHTING_COORDS(3,4)
            };
            VertexOutput vert (VertexInput v) {
                VertexOutput o;
                o.uv0 = v.uv0;
                o.normalDir = mul(float4(v.normal,0), _World2Object).xyz;
                o.posWorld = mul(_Object2World, v.vertex);
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                TRANSFER_VERTEX_TO_FRAGMENT(o)
                return o;
            }
            fixed4 frag(VertexOutput i) : COLOR {
                i.normalDir = normalize(i.normalDir);
                float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
/////// Normals:
                float3 normalDirection =  i.normalDir;
                float3 lightDirection = normalize(lerp(_WorldSpaceLightPos0.xyz, _WorldSpaceLightPos0.xyz - i.posWorld.xyz,_WorldSpaceLightPos0.w));
                float3 halfDirection = normalize(viewDirection+lightDirection);
////// Lighting:
                float attenuation = LIGHT_ATTENUATION(i);
                float3 attenColor = attenuation * _LightColor0.xyz;
/////// Diffuse:
                float NdotL = dot( normalDirection, lightDirection );
                float3 diffuse = max( 0.0, NdotL) * attenColor;
///////// Gloss:
                float gloss = _Glossslider;
                float specPow = exp2( gloss * 10.0+1.0);
////// Specular:
                NdotL = max(0.0, NdotL);
                float node_82 = 0.0;
                float3 specularColor = float3(node_82,node_82,node_82);
                float3 specular = attenColor * pow(max(0,dot(halfDirection,normalDirection)),specPow) * specularColor;
                float3 finalColor = 0;
                float3 diffuseLight = diffuse;
                float2 node_309 = i.uv0;
                finalColor += diffuseLight * tex2D(_MainTex,TRANSFORM_TEX(node_309.rg, _MainTex)).rgb;
                finalColor += specular;
/// Final Color:
                return fixed4(finalColor * 1,0);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
    CustomEditor "ShaderForgeMaterialInspector"
}

any tips?

Hello BCoyle,

You are the third person to request Shader Forge integration, clearly this is something people want! :slight_smile:

Reflection Manager requires some custom shader code in order to function, which needs to be wrapped in the Shader Forge system for you to use.

I will try to write a custom shader ‘node’ later this week, that should be easily integrated in your existing Shader Forge material. I will reply to this thread when that is done.

Reflection Manager comes with documentation on integrating into your own custom shaders, as well as several example shader implementations. Including the “Reflective Lighting” material used in the Parallax Demo.

Integration is fairly straight forward, but can become more tricky if you have constraints.

#include "ReflectionManager.cginc" 
float3 reflection_colour = ReflectionManager( _Cubemap, _GlossMap, world_reflection_vector, world_position, uv );

EDIT : See below, I still haven’t finished Shader Forge integration.

Really interesting stuff. Great job!!

Looks really cool!!

Will this work with Unity Free and if so are there any limitations?

Hello Zaddo67,

Yes! Reflection Manager can be used with Unity Free.

The built-in Unity generation algorithm does not work in Unity Free, so you have to simply select FRAME_BUNKER as your generation algorithm.

I just noticed some seaming, weirdness and warnings on the dynamic cubemap (the one in the last room) when running Unity Free, I’ll take a look at that ASAP and have a fix in the next build.

Thx GameArt. I have put this on my shopping list.

I have looked at integration with Shader Forge, and had a few problems. I could not finish the integration inside the Shader Forge node editor. I can’t get #includes to be added in the correct place from the node editor. I’ll ask the Shader Forge people about this, and will include an example material in the next release.

But… Reflection Manager comes with an array of really great materials out of the box, so I encourage you to not wait! :slight_smile:

Could you give an example of a shaderforge shader with the parallax refection code manually added.

Hey CaseJnr,

The problem I ran into was that when I opened the shader with my manual changes, it would be erased. I looked at restructuring my code to work inside a shader forge node but it was becoming a big mess. So this example is not really that useful.

I’m not at the computer I tried this at tonight, so 'll post the shader example tomorrow. You can use it to put the ‘final’ touches on your shader forge shader. But be warned you won’t be able to use Shader forge functionality after the fact!

It’s all good. I already sorted it out. Thanks anyway.

Is it possible to have reflective glass (reflective + semi-transparent + diffuse map) using Reflection Manager?

EDIT: Sorry, never mind :slight_smile: I used this as a template and integrated reflection manager as instructed in manual.

Awesome to hear the instructions helped you integrate the shader easily!

In the next version of ReflectionManager I will include a nice glass shader so others don’t need to do this.

Hi GameArt

I have a problem when i enable “force parallax” the cross fade between cubemaps bug, it disable force parallax during the cross fade, do you know how to fix it ?

( sorry if my english is bad i’m french )

Hello al3d,

I see that in the demo, when I push a sphere through two rooms with parallax forced on, I get a noticeable pop when entering the 2nd room. I believe this is because we are not considering the 2nd parallax volume when blending. I will address this issue in the next maintenance release.

All we need to do to fix this is to supply our shader with information for the 2nd parallax volume.

I will supply a fixed version of this shader later this week.

Thanks for the answer ! can’t wait for the next release :slight_smile:

1 Like

Just wondering, how many probes do you have set up in this scene? Is it one per room or more than that? And what resolution? Are there options for changing the resolution?

The sample scene features one probe per room. The static probes are 512. The last room features a dynamic probe, which is at a lower resolution.

Generation is customizable, allowing you to select static targets, automatically generate targets and dynamic targets.
Resolution is also configurable, and several generation and smoothing methods are available.
Fine grain controls are also available, for doing things like enabling and disabling rendering layers, backgrounds and runtime linking options.

do you managed to fix the shader? thx

Version 0.4 was just sent for review, it includes your requested change.