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): Shader "RealtimeReflectionWaterFlow_GLSL" { Properties { _MainTex ("Base", 2D) = "white" {} _Normal("Normal", 2D) = "bump" {} _ReflectionTex("_ReflectionTex", 2D) = "black" {} _FakeReflect("Fake reflection", 2D) = "black" {} _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2) _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0) } SubShader { Tags {"Queue" = "Transparent"} Pass { GLSLPROGRAM uniform vec4 _Time; uniform vec3 _WorldSpaceCameraPos; uniform mat4 _Object2World; varying mediump vec2 uv; varying highp vec4 normalScrollUv; varying mediump vec4 screen; varying mediump vec2 fakeRefl; uniform mediump vec4 _DirectionUv; uniform mediump vec4 _TexAtlasTiling; uniform mediump sampler2D _MainTex; uniform mediump sampler2D _Normal; uniform mediump sampler2D _ReflectionTex; uniform mediump sampler2D _FakeReflect; varying highp vec4 position_in_world_space; #ifdef VERTEX void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; uv = gl_MultiTexCoord0.xy; normalScrollUv.xyzw = gl_MultiTexCoord0.xyxy * _TexAtlasTiling + fract(_Time.xxxx * _DirectionUv); position_in_world_space = _Object2World * gl_Vertex; vec3 worldSpace = position_in_world_space.xyz; worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07; fakeRefl = worldSpace.xy; vec4 ScreenPos = gl_Position; ScreenPos.x = 0.5 * (ScreenPos.w + ScreenPos.x); ScreenPos.y = 0.5 * (ScreenPos.w + ScreenPos.y); ScreenPos.z = ScreenPos.w; screen = ScreenPos; } #endif #ifdef FRAGMENT void main() { vec4 nrml = texture2D(_Normal, normalScrollUv.xy); nrml += texture2D(_Normal, normalScrollUv.zw); nrml.xy *= 0.05; vec4 rtRefl = texture2D(_ReflectionTex, (screen.xy / screen.w) + nrml.xy); rtRefl += texture2D (_FakeReflect, fakeRefl + nrml.xy * 2.0); vec4 tex = tex2D (_MainTex, uv.xy + nrml.xy * 0.05); vec4 texalpha = tex2D (_MainTex, uv); tex = tex + tex.a * rtRefl; gl_FragColor = tex; } #endif ENDGLSL } } } 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.
I think it would be easier to find the problem when you would post the correctly working(!) Cg shader.
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): Shader "AngryBots/RealtimeReflectionInWaterFlow" { Properties { _MainTex ("Base", 2D) = "white" {} _Normal("Normal", 2D) = "bump" {} _ReflectionTex("_ReflectionTex", 2D) = "black" {} _FakeReflect("Fake reflection", 2D) = "black" {} _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2) _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0) } CGINCLUDE struct v2f_full { half4 pos : SV_POSITION; half2 uv : TEXCOORD0; half4 normalScrollUv : TEXCOORD1; half4 screen : TEXCOORD2; half2 fakeRefl : TEXCOORD3; #ifdef LIGHTMAP_ON half2 uvLM : TEXCOORD4; #endif }; #include "AngryInclude.cginc" half4 _DirectionUv; half4 _TexAtlasTiling; sampler2D _MainTex; sampler2D _Normal; sampler2D _ReflectionTex; sampler2D _FakeReflect; ENDCG SubShader { Tags { "RenderType"="Opaque" } LOD 300 Pass { CGPROGRAM float4 _MainTex_ST; float4 unity_LightmapST; sampler2D unity_Lightmap; v2f_full vert (appdata_full v) { v2f_full o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv.xy = TRANSFORM_TEX(v.texcoord,_MainTex); #ifdef LIGHTMAP_ON o.uvLM = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw; #endif o.normalScrollUv.xyzw = v.texcoord.xyxy * _TexAtlasTiling + _Time.xxxx * _DirectionUv; o.fakeRefl = EthansFakeReflection(v.vertex); o.screen = ComputeScreenPos(o.pos); return o; } fixed4 frag (v2f_full i) : COLOR0 { half3 nrml = UnpackNormal(tex2D(_Normal, i.normalScrollUv.xy)); nrml += UnpackNormal(tex2D(_Normal, i.normalScrollUv.zw)); nrml.xy *= 0.025; fixed4 rtRefl = tex2D (_ReflectionTex, (i.screen.xy / i.screen.w) + nrml.xy); rtRefl += tex2D (_FakeReflect, i.fakeRefl + nrml.xy * 2.0); fixed4 tex = tex2D (_MainTex, i.uv.xy + nrml.xy * 0.05); #ifdef LIGHTMAP_ON fixed3 lm = ( DecodeLightmap (tex2D(unity_Lightmap, i.uvLM))); tex.rgb *= lm; #endif tex = tex + tex.a * rtRefl; return tex; } #pragma vertex vert #pragma fragment frag #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON #pragma fragmentoption ARB_precision_hint_fastest ENDCG } } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass { CGPROGRAM float4 _MainTex_ST; float4 unity_LightmapST; sampler2D unity_Lightmap; v2f_full vert (appdata_full v) { v2f_full o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); #ifdef LIGHTMAP_ON o.uvLM = v.texcoord1.xy * unity_LightmapST.xy + unity_LightmapST.zw; #endif o.normalScrollUv.xyzw = v.texcoord.xyxy * _TexAtlasTiling + _Time.xxxx * _DirectionUv; o.fakeRefl = EthansFakeReflection(v.vertex); o.screen = ComputeScreenPos(o.pos); return o; } fixed4 frag (v2f_full i) : COLOR0 { // assuming this is on mobile, so no texture unpacking needed fixed4 nrml = tex2D(_Normal, i.normalScrollUv.xy); nrml = (nrml - 0.5) * 0.1; fixed4 rtRefl = tex2D (_ReflectionTex, (i.screen.xy / i.screen.w) + nrml.xy); // needed optimization for now //rtRefl += tex2D (_FakeReflect, i.fakeRefl + nrml.xy); fixed4 tex = tex2D (_MainTex, i.uv); #ifdef LIGHTMAP_ON fixed3 lm = ( DecodeLightmap (tex2D(unity_Lightmap, i.uvLM))); tex.rgb *= lm; #endif tex = tex + tex.a * rtRefl; return tex; } #pragma vertex vert #pragma fragment frag #pragma multi_compile LIGHTMAP_OFF LIGHTMAP_ON #pragma fragmentoption ARB_precision_hint_fastest ENDCG } } FallBack "AngryBots/Fallback" } 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): // Projected screen position helpers #define V2F_SCREEN_TYPE float4 inline float4 ComputeScreenPos (float4 pos) { float4 o = pos * 0.5f; #if defined(UNITY_HALF_TEXEL_OFFSET) o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w * _ScreenParams.zw; #else o.xy = float2(o.x, o.y*_ProjectionParams.x) + o.w; #endif #if defined(SHADER_API_FLASH) o.xy *= unity_NPOTScale.xy; #endif o.zw = pos.zw; return o; } And from "AngryInclude.cginc": Code (csharp): half2 EthansFakeReflection (half4 vtx) { half3 worldSpace = mul(_Object2World, vtx).xyz; worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07; return worldSpace.xz; } Thanks for your help
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;
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): vec4 packednormal = texture2D(_Normal, normalScrollUv.xy); packednormal += texture2D(_Normal, normalScrollUv.zw); vec3 nrml = packednormal.xyz * 2 - 1; 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): vec4 packednormal1 = texture2D(_Normal, normalScrollUv.xy); vec4 packednormal2 = texture2D(_Normal, normalScrollUv.zw); vec3 nrml = (packednormal1.xyz * 2 - 1) + (packednormal2.xyz * 2 - 1); And here's what i get: The offset is gone So here's the final shader (for people who may be interested): Code (csharp): Shader "RealtimeReflectionWaterFlow_GLSL" { Properties { _MainTex ("Base", 2D) = "white" {} _Normal("Normal", 2D) = "bump" {} _ReflectionTex("_ReflectionTex", 2D) = "black" {} _FakeReflect("Fake reflection", 2D) = "black" {} _DirectionUv("Wet scroll direction (2 samples)", Vector) = (1.0,1.0, -0.2,-0.2) _TexAtlasTiling("Tex atlas tiling", Vector) = (8.0,8.0, 4.0,4.0) } SubShader { Tags {"Queue" = "Geometry"} Pass { GLSLPROGRAM uniform vec4 _Time; uniform vec3 _WorldSpaceCameraPos; uniform mat4 _Object2World; varying mediump vec2 uv; varying highp vec4 normalScrollUv; varying mediump vec4 screen; varying mediump vec2 fakeRefl; uniform mediump vec4 _DirectionUv; uniform mediump vec4 _TexAtlasTiling; uniform mediump sampler2D _MainTex; uniform mediump sampler2D _Normal; uniform mediump sampler2D _ReflectionTex; uniform mediump sampler2D _FakeReflect; varying highp vec4 position_in_world_space; #ifdef VERTEX void main() { gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; uv = gl_MultiTexCoord0.xy; normalScrollUv.xyzw = gl_MultiTexCoord0.xyxy * _TexAtlasTiling + fract(_Time.xxxx * _DirectionUv); position_in_world_space = _Object2World * gl_Vertex; vec3 worldSpace = position_in_world_space.xyz; worldSpace = (-_WorldSpaceCameraPos * 0.6 + worldSpace) * 0.07; fakeRefl = worldSpace.xz; vec4 ScreenPos = gl_Position; ScreenPos.x = 0.5 * (ScreenPos.w + ScreenPos.x); ScreenPos.y = 0.5 * (ScreenPos.w + ScreenPos.y); ScreenPos.z = ScreenPos.w; screen = ScreenPos; } #endif #ifdef FRAGMENT void main() { vec4 packednormal1 = texture2D(_Normal, normalScrollUv.xy); vec4 packednormal2 = texture2D(_Normal, normalScrollUv.zw); vec3 nrml = (packednormal1.xyz * 2 - 1) + (packednormal2.xyz * 2 - 1); nrml.xy *= 0.05; vec4 rtRefl = texture2D(_ReflectionTex, (screen.xy / screen.w) + nrml.xy); rtRefl += texture2D (_FakeReflect, fakeRefl + nrml.xy * 2.0); vec4 tex = tex2D (_MainTex, uv.xy + nrml.xy * 0.05); tex = tex + tex.a * rtRefl; gl_FragColor = tex; } #endif ENDGLSL } } }