Selective Distortion

I’m using a distortion shader to create a lens effect. The only problem I have is that it distorts everything above and below it. Is there a way to only distort below and not above?

Here is the lens normal map:

// Per pixel bumped refraction.
// Uses a normal map to distort the image behind, and
// an additional texture to tint the color.

Shader "Distortion" {
Properties {
    _BumpAmt  ("Distortion", range (0,128)) = 10
    _MainTex ("Tint Color (RGB)", 2D) = "white" {}
    _BumpMap ("Normalmap", 2D) = "bump" {}
}

CGINCLUDE
#pragma fragmentoption ARB_precision_hint_fastest
#pragma fragmentoption ARB_fog_exp2
#include "UnityCG.cginc"

sampler2D _GrabTexture : register(s0);
float4 _GrabTexture_TexelSize;
sampler2D _BumpMap : register(s1);
sampler2D _MainTex : register(s2);

struct v2f {
    float4 vertex : POSITION;
    float4 uvgrab : TEXCOORD0;
    float2 uvbump : TEXCOORD1;
    float2 uvmain : TEXCOORD2;
};

uniform float _BumpAmt;


half4 frag( v2f i ) : COLOR
{
    // calculate perturbed coordinates
    half2 bump = UnpackNormal(tex2D( _BumpMap, i.uvbump )).rg; // we could optimize this by just reading the x & y without reconstructing the Z
    float2 offset = bump * _BumpAmt * _GrabTexture_TexelSize.xy;
    i.uvgrab.xy = offset * i.uvgrab.z + i.uvgrab.xy;
   
    half4 col = tex2Dproj( _GrabTexture, i.uvgrab.xyw );
    half4 tint = tex2D( _MainTex, i.uvmain );
    return col * tint;
}
ENDCG

Category {

    // We must be transparent, so other objects are drawn before this one.
    Tags { "Queue"="Transparent" "RenderType"="Opaque" }


    SubShader {

        // This pass grabs the screen behind the object into a texture.
        // We can access the result in the next pass as _GrabTexture
        GrabPass {                           
            Name "BASE"
            Tags { "LightMode" = "Always" }
         }
        
         // Main pass: Take the texture grabbed above and use the bumpmap to perturb it
         // on to the screen
        Pass {
            Name "BASE"
            Tags { "LightMode" = "Always" }
           
CGPROGRAM
#pragma vertex vert
#pragma fragment frag

struct appdata_t {
    float4 vertex : POSITION;
    float2 texcoord: TEXCOORD0;
};

v2f vert (appdata_t v)
{
    v2f o;
    o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
    #if UNITY_UV_STARTS_AT_TOP
    float scale = -1.0;
    #else
    float scale = 1.0;
    #endif
    o.uvgrab.xy = (float2(o.vertex.x, o.vertex.y*scale) + o.vertex.w) * 0.5;
    o.uvgrab.zw = o.vertex.zw;
    o.uvbump = MultiplyUV( UNITY_MATRIX_TEXTURE1, v.texcoord );
    o.uvmain = MultiplyUV( UNITY_MATRIX_TEXTURE2, v.texcoord );
    return o;
}
ENDCG
        }
    }

    // ------------------------------------------------------------------
    // Fallback for older cards and Unity non-Pro
   
    SubShader {
        Blend DstColor Zero
        Pass {
            Name "BASE"
            SetTexture [_MainTex] {    combine texture }
        }
    }
}

}
2 Likes

Well, minutes after posting this, I found out that I have a sprite with a layer order that was making it render before the lens and get the distortion. So nevermind then, but here is a lens distortion and normal map if anyone needs it lol

Hmm, it’s not working for me. Were you applying this on a quad or a sprite?

The distortion material was on a quad rendered between sprite layers.

Thanks so much.

It works wonderfully in Unity 4.6

And not at all in Unity 5… crap…

DanSuperGP, it works in Unity 5, the fix is pretty simply:
replace

tex2Dproj( _GrabTexture, uvgrab.xyw);

with

tex2Dproj( _GrabTexture, uvgrab.xyww);

So awesome, thanks!