Search Unity

GLSL Reflective Bump Shader

Discussion in 'Shaders' started by melong, Apr 5, 2012.

  1. melong

    melong

    Joined:
    Sep 2, 2011
    Posts:
    22
    Hi guyz !

    I am working on a GLSL Reflective Bump shader. This is actually based on the wet floor shader from Angry bots that was originally written in CG and that i tried to convert into GLSL for the stake of learning and understanding shader code.
    It is using the original ReflectionFX script from the same project (i also tested the MirrorReflection script from the Unify wiki and i nearly get the same result).
    Everything seems to work miraculously great regarding the fact my knowledge in shader language and more generally in programmation language is close to nothing but i still have have a little problem that i couldn't get rid of. Here's a screenshot of my problem (it will be clearer than any written explanation):



    And the shader code:

    Code (csharp):
    1. Shader "RealtimeReflectionWaterFlow_GLSL"
    2. {
    3.    
    4.     Properties
    5.     {
    6.         _MainTex ("Base", 2D) = "white" {}
    7.         _Normal("Normal", 2D) = "bump" {}
    8.         _ReflectionTex("_ReflectionTex", 2D) = "black" {}
    9.         _FakeReflect("Fake reflection", 2D) = "black" {}
    10.         _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2)
    11.         _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0)   
    12.     }
    13.  
    14.     SubShader
    15.     {
    16.         Tags {"Queue" = "Transparent"}
    17.    
    18.         Pass
    19.         {
    20.             GLSLPROGRAM
    21.        
    22.             uniform vec4 _Time;
    23.             uniform vec3 _WorldSpaceCameraPos;
    24.             uniform mat4 _Object2World;
    25.            
    26.             varying mediump vec2 uv;
    27.             varying highp vec4 normalScrollUv;
    28.             varying mediump vec4 screen;
    29.             varying mediump vec2 fakeRefl;
    30.            
    31.             uniform mediump vec4 _DirectionUv;
    32.             uniform mediump vec4 _TexAtlasTiling;
    33.  
    34.             uniform mediump sampler2D _MainTex;
    35.             uniform mediump sampler2D _Normal;     
    36.             uniform mediump sampler2D _ReflectionTex;
    37.             uniform mediump sampler2D _FakeReflect;
    38.            
    39.             varying highp vec4 position_in_world_space;
    40.        
    41.             #ifdef VERTEX
    42.             void main()
    43.             {
    44.                 gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    45.                 uv = gl_MultiTexCoord0.xy;
    46.                
    47.                 normalScrollUv.xyzw = gl_MultiTexCoord0.xyxy * _TexAtlasTiling + fract(_Time.xxxx * _DirectionUv);
    48.                
    49.                 position_in_world_space = _Object2World * gl_Vertex;
    50.                 vec3 worldSpace = position_in_world_space.xyz;
    51.                 worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07;
    52.                 fakeRefl = worldSpace.xy;
    53.  
    54.                 vec4 ScreenPos = gl_Position;
    55.                 ScreenPos.x = 0.5 * (ScreenPos.w + ScreenPos.x);
    56.                 ScreenPos.y = 0.5 * (ScreenPos.w + ScreenPos.y);
    57.                 ScreenPos.z = ScreenPos.w;
    58.            
    59.                 screen = ScreenPos;
    60.             }
    61.             #endif
    62.                
    63.             #ifdef FRAGMENT
    64.             void main()
    65.             {
    66.                 vec4 nrml = texture2D(_Normal, normalScrollUv.xy);
    67.                 nrml += texture2D(_Normal, normalScrollUv.zw);
    68.                
    69.                 nrml.xy *= 0.05;
    70.                
    71.                 vec4 rtRefl = texture2D(_ReflectionTex, (screen.xy / screen.w) + nrml.xy);
    72.                 rtRefl += texture2D (_FakeReflect, fakeRefl + nrml.xy * 2.0);
    73.                
    74.                 vec4 tex = tex2D (_MainTex, uv.xy + nrml.xy * 0.05);
    75.                 vec4 texalpha = tex2D (_MainTex, uv);
    76.                
    77.                 tex  = tex + tex.a * rtRefl;
    78.                
    79.                 gl_FragColor = tex;
    80.             }
    81.             #endif
    82.                
    83.             ENDGLSL
    84.         }
    85.     }
    86. }
    So as you can see the reflected image's position of the cube is incorrect, after a few dumb tests i found that it was the nrml value addition to the _ReflectionTex uv that was offsetting my reflected image, i'm not totally sure of that though but if i comment out the "+ nrml.xy" part, the reflected images gets positioned correctly. So this where i'm stuck now, i couldn't find a solution to fix this so if you guyz have an idea, it would be very much appreciated.
     
  2. hippocoder

    hippocoder

    Digital Ape

    Joined:
    Apr 11, 2010
    Posts:
    29,723
    I think the nrml.xy is just an offset to make it "look good" for angry bots, I could be wrong.
     
  3. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    I think it would be easier to find the problem when you would post the correctly working(!) Cg shader.
     
  4. melong

    melong

    Joined:
    Sep 2, 2011
    Posts:
    22
    That's what i thought at first but looking at the AngryBots demo more closely there doesn't seem to be an offset or it's so small that we can't notice.

    Sure! Here it is :

    Code (csharp):
    1. Shader "AngryBots/RealtimeReflectionInWaterFlow" {
    2.    
    3. Properties {
    4.     _MainTex ("Base", 2D) = "white" {}
    5.     _Normal("Normal", 2D) = "bump" {}
    6.     _ReflectionTex("_ReflectionTex", 2D) = "black" {}
    7.     _FakeReflect("Fake reflection", 2D) = "black" {}
    8.     _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2)
    9.     _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0)   
    10. }
    11.  
    12. CGINCLUDE      
    13.  
    14. struct v2f_full
    15. {
    16.     half4 pos : SV_POSITION;
    17.     half2 uv : TEXCOORD0;
    18.     half4 normalScrollUv : TEXCOORD1;  
    19.     half4 screen : TEXCOORD2;
    20.     half2 fakeRefl : TEXCOORD3;
    21.     #ifdef LIGHTMAP_ON
    22.         half2 uvLM : TEXCOORD4;
    23.     #endif 
    24. };
    25.    
    26. #include "AngryInclude.cginc"      
    27.  
    28. half4 _DirectionUv;
    29. half4 _TexAtlasTiling;
    30.  
    31. sampler2D _MainTex;
    32. sampler2D _Normal;     
    33. sampler2D _ReflectionTex;
    34. sampler2D _FakeReflect;
    35.            
    36. ENDCG
    37.  
    38. SubShader {
    39.     Tags { "RenderType"="Opaque" }
    40.  
    41.     LOD 300
    42.  
    43.     Pass {
    44.         CGPROGRAM
    45.        
    46.         float4 _MainTex_ST;
    47.         float4 unity_LightmapST;   
    48.         sampler2D unity_Lightmap;
    49.        
    50.         v2f_full vert (appdata_full v)
    51.         {
    52.             v2f_full o;
    53.             o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    54.             o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex);
    55.            
    56.             #ifdef LIGHTMAP_ON
    57.                 o.uvLM = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    58.             #endif
    59.            
    60.             o.normalScrollUv.xyzw = v.texcoord.xyxy * _TexAtlasTiling + _Time.xxxx * _DirectionUv;
    61.                        
    62.             o.fakeRefl = EthansFakeReflection(v.vertex);
    63.             o.screen = ComputeScreenPos(o.pos);
    64.                
    65.             return o;
    66.         }
    67.                
    68.         fixed4 frag (v2f_full i) : COLOR0
    69.         {
    70.             half3 nrml = UnpackNormal(tex2D(_Normal, i.normalScrollUv.xy));
    71.             nrml += UnpackNormal(tex2D(_Normal, i.normalScrollUv.zw));
    72.            
    73.             nrml.xy *= 0.025;
    74.                                        
    75.             fixed4 rtRefl = tex2D (_ReflectionTex, (i.screen.xy / i.screen.w) + nrml.xy);
    76.             rtRefl += tex2D (_FakeReflect, i.fakeRefl + nrml.xy * 2.0);
    77.                        
    78.             fixed4 tex = tex2D (_MainTex, i.uv.xy + nrml.xy * 0.05);
    79.        
    80.             #ifdef LIGHTMAP_ON
    81.                 fixed3 lm = ( DecodeLightmap (tex2D(unity_Lightmap, i.uvLM)));
    82.                 tex.rgb *= lm;
    83.             #endif 
    84.            
    85.             tex  = tex + tex.a * rtRefl;
    86.            
    87.             return tex;
    88.         }  
    89.        
    90.         #pragma vertex vert
    91.         #pragma fragment frag
    92.         #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
    93.         #pragma fragmentoption ARB_precision_hint_fastest
    94.    
    95.         ENDCG
    96.     }
    97. }
    98.  
    99.  
    100. SubShader {
    101.     Tags { "RenderType"="Opaque" }
    102.  
    103.     LOD 200
    104.  
    105.     Pass {
    106.         CGPROGRAM
    107.        
    108.         float4 _MainTex_ST;
    109.         float4 unity_LightmapST;   
    110.         sampler2D unity_Lightmap;
    111.        
    112.         v2f_full vert (appdata_full v)
    113.         {
    114.             v2f_full o;
    115.             o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
    116.             o.uv = TRANSFORM_TEX(v.texcoord,_MainTex);
    117.            
    118.             #ifdef LIGHTMAP_ON
    119.                 o.uvLM = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw;
    120.             #endif
    121.            
    122.             o.normalScrollUv.xyzw = v.texcoord.xyxy * _TexAtlasTiling + _Time.xxxx * _DirectionUv;
    123.                        
    124.             o.fakeRefl = EthansFakeReflection(v.vertex);
    125.             o.screen = ComputeScreenPos(o.pos);
    126.                
    127.             return o;
    128.         }
    129.                
    130.         fixed4 frag (v2f_full i) : COLOR0
    131.         {
    132.             // assuming this is on mobile, so no texture unpacking needed
    133.            
    134.             fixed4 nrml = tex2D(_Normal, i.normalScrollUv.xy);
    135.             nrml = (nrml - 0.5) * 0.1;
    136.                                        
    137.             fixed4 rtRefl = tex2D (_ReflectionTex, (i.screen.xy / i.screen.w) + nrml.xy);
    138.            
    139.             // needed optimization for now
    140.             //rtRefl += tex2D (_FakeReflect, i.fakeRefl + nrml.xy);
    141.                        
    142.             fixed4 tex = tex2D (_MainTex, i.uv);
    143.        
    144.             #ifdef LIGHTMAP_ON
    145.                 fixed3 lm = ( DecodeLightmap (tex2D(unity_Lightmap, i.uvLM)));
    146.                 tex.rgb *= lm;
    147.             #endif 
    148.            
    149.             tex  = tex + tex.a * rtRefl;
    150.            
    151.             return tex;
    152.         }  
    153.        
    154.         #pragma vertex vert
    155.         #pragma fragment frag
    156.         #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON
    157.         #pragma fragmentoption ARB_precision_hint_fastest
    158.    
    159.         ENDCG
    160.     }
    161. }
    162.  
    163. FallBack "AngryBots/Fallback"
    164. }
    165.  
    Actually i had to rewrite some parts because it was calling functions located in "UnityCG.cginc" and "AngryInclude.cginc" which are not available for GLSL. Here they are:

    From "UnityCG.cginc":
    Code (csharp):
    1. // Projected screen position helpers
    2. #define V2F_SCREEN_TYPE float4
    3. inline float4 ComputeScreenPos (float4 pos) {
    4.     float4 o = pos * 0.5f;
    5.     #if defined(UNITY_HALF_TEXEL_OFFSET)
    6.     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw;
    7.     #else
    8.     o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w;
    9.     #endif
    10.    
    11.     #if defined(SHADER_API_FLASH)
    12.     o.xy *= unity_NPOTScale.xy;
    13.     #endif
    14.    
    15.     o.zw = pos.zw;
    16.     return o;
    17. }
    And from "AngryInclude.cginc":

    Code (csharp):
    1. half2 EthansFakeReflection (half4 vtx) {
    2.     half3 worldSpace = mul(_Object2World, vtx).xyz;
    3.     worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07;
    4.     return worldSpace.xz;
    5. }
    Thanks for your help :)
     
  5. Martin-Kraus

    Martin-Kraus

    Joined:
    Feb 18, 2011
    Posts:
    617
    You would "debug" this by checking whether all variables in the CG and GLSL versions of the shader have the same values. You do this by setting the fragment color to the value of variables that you want to check. You can quickly find out that fakeRefl doesn't have the same values in the two shaders. The reason is line 52 in your GLSL shader, it should say: fakeRefl = worldSpace.xz;
    Also, line 69 should probably say nrml.xy *= 0.025;
     
  6. melong

    melong

    Joined:
    Sep 2, 2011
    Posts:
    22
    Hi !
    I think i got the solution.
    Thank you Martin for the errors you pointed out (this did not have a big influence though).

    So there were another part i ommitted from the original CG shader that is the UnpackNormal() function which i actually still don't understand very well (i read this has a relation with the compression of the normal maps).

    I had tried the following modification before as an equivalent to Unpacknormal():
    Code (csharp):
    1.  
    2. vec4 packednormal = texture2D(_Normal, normalScrollUv.xy);
    3. packednormal += texture2D(_Normal, normalScrollUv.zw);
    4. vec3 nrml = packednormal.xyz * 2 - 1;
    5.  
    Unfortunately this had not much effect on the visuals so i just put this away thinking it was unnecessary.

    But today i realised this code was "unpacking" the sum of the two packed normal map layer whereas the original shader was actually adding the two maps already unpacked which apparently is not equal or maybe this is the above code that is wrong but anyway i tried this instead:
    Code (csharp):
    1.  
    2. vec4 packednormal1 = texture2D(_Normal, normalScrollUv.xy);
    3. vec4 packednormal2 = texture2D(_Normal, normalScrollUv.zw);
    4. vec3 nrml = (packednormal1.xyz * 2 - 1) + (packednormal2.xyz * 2 - 1);
    5.  
    And here's what i get:

    The offset is gone :)

    So here's the final shader (for people who may be interested):
    Code (csharp):
    1. Shader "RealtimeReflectionWaterFlow_GLSL"
    2.     {
    3.        
    4.         Properties
    5.         {
    6.             _MainTex ("Base", 2D) = "white" {}
    7.             _Normal("Normal", 2D) = "bump" {}
    8.             _ReflectionTex("_ReflectionTex", 2D) = "black" {}
    9.             _FakeReflect("Fake reflection", 2D) = "black" {}
    10.             _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2)
    11.             _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0)  
    12.         }
    13.      
    14.         SubShader
    15.         {
    16.             Tags {"Queue" = "Geometry"}
    17.        
    18.             Pass
    19.             {
    20.                 GLSLPROGRAM
    21.            
    22.                 uniform vec4 _Time;
    23.                 uniform vec3 _WorldSpaceCameraPos;
    24.                 uniform mat4 _Object2World;
    25.                
    26.                 varying mediump vec2 uv;
    27.                 varying highp vec4 normalScrollUv;
    28.                 varying mediump vec4 screen;
    29.                 varying mediump vec2 fakeRefl;
    30.                
    31.                 uniform mediump vec4 _DirectionUv;
    32.                 uniform mediump vec4 _TexAtlasTiling;
    33.      
    34.                 uniform mediump sampler2D _MainTex;
    35.                 uniform mediump sampler2D _Normal;    
    36.                 uniform mediump sampler2D _ReflectionTex;
    37.                 uniform mediump sampler2D _FakeReflect;
    38.                
    39.                 varying highp vec4 position_in_world_space;
    40.            
    41.                 #ifdef VERTEX
    42.                 void main()
    43.                 {
    44.                     gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    45.                     uv = gl_MultiTexCoord0.xy;
    46.                    
    47.                     normalScrollUv.xyzw = gl_MultiTexCoord0.xyxy * _TexAtlasTiling + fract(_Time.xxxx * _DirectionUv);
    48.                    
    49.                     position_in_world_space = _Object2World * gl_Vertex;
    50.                     vec3 worldSpace = position_in_world_space.xyz;
    51.                     worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07;
    52.                     fakeRefl = worldSpace.xz;
    53.      
    54.                     vec4 ScreenPos = gl_Position;
    55.                     ScreenPos.x = 0.5 * (ScreenPos.w + ScreenPos.x);
    56.                     ScreenPos.y = 0.5 * (ScreenPos.w + ScreenPos.y);
    57.                     ScreenPos.z = ScreenPos.w;
    58.                
    59.                     screen = ScreenPos;
    60.                 }
    61.                 #endif
    62.                    
    63.                 #ifdef FRAGMENT
    64.                 void main()
    65.                 {
    66.                     vec4 packednormal1 = texture2D(_Normal, normalScrollUv.xy);
    67.                     vec4 packednormal2 = texture2D(_Normal, normalScrollUv.zw);
    68.                
    69.                     vec3 nrml = (packednormal1.xyz * 2 - 1) + (packednormal2.xyz * 2 - 1);
    70.                    
    71.                     nrml.xy *= 0.05;
    72.                    
    73.                     vec4 rtRefl = texture2D(_ReflectionTex, (screen.xy / screen.w) + nrml.xy);
    74.                     rtRefl += texture2D (_FakeReflect, fakeRefl + nrml.xy * 2.0);
    75.                    
    76.                     vec4 tex = tex2D (_MainTex, uv.xy + nrml.xy * 0.05);
    77.                    
    78.                     tex  = tex + tex.a * rtRefl;
    79.                    
    80.                     gl_FragColor = tex;
    81.                 }
    82.                 #endif
    83.                    
    84.                 ENDGLSL
    85.             }
    86.         }
    87.     }
    88.