Unity 3D Indie Realtime Water Reflection

Using the following two-pass shader I was able to get realtime water reflections in Indie/Free Edition of Unity 3D:

Shader "Custom/water_mirror" {
    Properties {
        _DiffuseTex ("Diffuse (RGB)", 2D) = "white" {}
        _NormalTex ("Normal (RGB)", 2D) = "bump" {}
        _SpecularTex ("Specular (R) Gloss (G)", 2D) = "gray" {}
        _WaterLevel ("Water Level", float) = 0
    }

    SubShader {

        Pass {
            Name "ContentBase"
            Tags {"LightMode" = "ForwardBase"}

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile_fwdbase
                #pragma fragmentoption ARB_precision_hint_fastest

                #include "UnityCG.cginc"
                #include "AutoLight.cginc"

                struct v2f
                {
                    float4  pos : SV_POSITION;
                    float2  uv : TEXCOORD0;
                    float3  lightDirT : TEXCOORD1;
                    float3  viewDirT : TEXCOORD2;
                    LIGHTING_COORDS(3,4)
                };

                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = v.texcoord.xy;
                    TANGENT_SPACE_ROTATION;
                    o.lightDirT = mul(rotation, ObjSpaceLightDir(v.vertex));
                    o.viewDirT = mul(rotation, ObjSpaceViewDir(v.vertex));
                    TRANSFER_VERTEX_TO_FRAGMENT(o);
                    return o;
                }

                sampler2D _DiffuseTex;
                sampler2D _SpecularTex;
                sampler2D _NormalTex;

                float4 _LightColor0;

                float4 frag(v2f i) : COLOR
                {
                    float3 normal = normalize(tex2D(_NormalTex, i.uv).xyz * 2 - 1);
                    float NdotL = dot(normal, i.lightDirT);
                    float3 halfAngle = normalize(i.lightDirT + i.viewDirT);
                    float atten = LIGHT_ATTENUATION(i);
                    float3 specularity = (pow(saturate(dot(normal, halfAngle)), tex2D(_SpecularTex, i.uv).g * 200) * tex2D(_SpecularTex, i.uv).r)  * _LightColor0;

                    float4 result;
                    result.rgb = tex2D(_DiffuseTex, i.uv).rgb * NdotL * atten * _LightColor0 + specularity;
                    result.a = 0;
                    return result;
                }
            ENDCG
        }

        Pass {
            Name "ContentBase"
            Tags {"LightMode" = "ForwardBase"}
            Cull Front

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag
                #pragma multi_compile_fwdbase
                #pragma fragmentoption ARB_precision_hint_fastest

                #include "UnityCG.cginc"
                #include "AutoLight.cginc"

                struct v2f
                {
                    float4  pos : SV_POSITION;
                    float2  uv : TEXCOORD0;
                    float3  lightDirT : TEXCOORD1;
                    float3  viewDirT : TEXCOORD2;
                    LIGHTING_COORDS(3,4)
                };

                float _WaterLevel;

                v2f vert (appdata_tan v)
                {
                    v2f o;
                    o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                    o.uv = v.texcoord.xy;
                    float4 p = mul(_Object2World, v.vertex);
                    p = float4(p[0], min(_WaterLevel, 2*_WaterLevel-p[1]), p[2], p[3]);
                    p = mul(_World2Object, p);
                    o.pos = mul(UNITY_MATRIX_MVP, p);
                    TANGENT_SPACE_ROTATION;
                    o.lightDirT = mul(rotation, ObjSpaceLightDir(v.vertex));
                    o.viewDirT = mul(rotation, ObjSpaceViewDir(v.vertex));
                    TRANSFER_VERTEX_TO_FRAGMENT(o);
                    return o;
                }

                sampler2D _DiffuseTex;
                sampler2D _SpecularTex;
                sampler2D _NormalTex;

                float4 _LightColor0;

                float4 frag(v2f i) : COLOR
                {
                    float3 normal = normalize(tex2D(_NormalTex, i.uv).xyz * 2 - 1);
                    float NdotL = dot(normal, i.lightDirT);
                    float3 halfAngle = normalize(i.lightDirT + i.viewDirT);
                    float atten = LIGHT_ATTENUATION(i);
                    float3 specularity = (pow(saturate(dot(normal, halfAngle)), tex2D(_SpecularTex, i.uv).g * 200) * tex2D(_SpecularTex, i.uv).r)  * _LightColor0;

                    float4 result;
                    result.rgb = tex2D(_DiffuseTex, i.uv).rgb * NdotL * atten * _LightColor0 + specularity;
                    result.a = 0;
                    return result;
                }
            ENDCG
        }
    }
}

The shader was based on some other shader demonstrating light computations in Unity3D. I added the second pass, made a mirror reflection of geometry against water and switched face culling to front. Hope somebody finds it useful 😉

9 Comments

  • Hi there,
    I’m very interested in being able to use reflections in the Indie version of Unity, so I tried to use this script. Only I got an error:

    Assets/Scripts/Reflection.cs(1,28): error CS8025: Parsing error

    Note that I’m a total n00b when it comes to scripting, perhaps I made a mistake implementing this.

    Would you be able to help me out?
    Thanks in advance!

    -f

    • Oh wait I’m sorry, I got it now, haha. As said, I’m a total noob with this kind of stuff. Thanks for the script!!!

    • Sorry I don’t offer any support for my code and I’m very busy with other projects at the moment. Anyway – if you want reflections in your game, better buy Unity Pro. This solution is clumsy at best.

  • esse efeito pode ser aplicado na versão grátis da Unity? se sim pode dizer como realizar (e se possível disponibilizar o código) não intendo muito de shaders e esse seria um bom para esstudar e usá-lo^^.

  • Very nice! I used it in my project and got a very nice effect in water although I don’t know why I cannot get the mirror effect. I put a cubemap of my scene but cannot see the other things reflected in water. Is there something that I’m missing? I use Unity free.

    Thanks.

    Regards.


Leave a Reply

Your email address will not be published. Required fields are marked *