Simple Fish-Eye Post Process Shader

I recently needed a simple fish-eye effect for a Unity game which I was working on. So as is typical for this type of problem, I first turned to Google to try and find somebody who’d already done the hard work for me.

However (much to my dismay) I really struggled to find something that just worked. I found quite a few examples of fish-eye and other distortion effect shaders, but nothing which bundled it all up into a usable set.

So I had to bite the bullet and do it myself. It didn’t turn out to be too difficult, especially since a lot of the harder work was already online, I just had to piece it together. After doing all that I decided to document it here.

Camera.OnRenderImage()

The first step was to implement the Camera.OnRenderImage method. You can read more about this in the unity documentation here.

I basically just copied the example in the docs by creating a new script in my project called FishEyeEffect and added this code to it

using UnityEngine;

public class FishEyeEffect : MonoBehaviour
{
    public Material mat;
    void OnRenderImage(RenderTexture src, RenderTexture dest)
    {
        Graphics.Blit(src, dest, mat);
    }
}

I added this component to the MainCamera object in my Unity scene.

Creating the Shader

Next was the shader itself. I created a new shader file in my project called FishEye. I searched the internet for a good fish eye shader example and found this post which was very useful:

http://www.francois-tarlier.com/blog/cubic-lens-distortion-shader

Although the code was for a different rendering system, it was pretty easy to convert to Unity CG. Here is my shader code:

Shader "Custom/FishEye" 
{
    Properties
    {
        _MainTex("", 2D) = "white" {}
        _Distortion("Distortion", Range(-3, 3)) = -1
    }

    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct v2f 
            {
                float4 pos : POSITION;
                half2 uv : TEXCOORD0;
            };

            //Our Vertex Shader 
            v2f vert(appdata_img v) 
            {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = MultiplyUV(UNITY_MATRIX_TEXTURE0, v.texcoord.xy);
                return o;
            }

            sampler2D _MainTex;
            float _Distortion;

            //Our Fragment Shader
            float4 frag(v2f i) : COLOR
            {
                // lens distortion coefficient
                float k = -0.15;

                float r2 = (i.uv.x - 0.5) * (i.uv.x - 0.5) + (i.uv.y - 0.5) * (i.uv.y - 0.5);
                float f = 0;

                //only compute the cubic distortion if necessary
                if (_Distortion == 0.0)
                {
                    f = 1 + r2 * k;
                }
                else 
                {
                    f = 1 + r2 * (k + _Distortion * sqrt(r2));
                };

                // get the right pixel for the current position
                float x = f*(i.uv.x - 0.5) + 0.5;
                float y = f*(i.uv.y - 0.5) + 0.5;
                float3 inputDistord = tex2D(_MainTex,float2(x,y));

                return float4(inputDistord.r,inputDistord.g,inputDistord.b,1);
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

For those of you familiar with shader language, you can see its actually pretty simple.

I also added a property slider to the shader to allow the distortion amount to be updated in the editor. This means you can set the distortion amount by eye while running the game which makes tuning it super easy. You could also set this value through code if you wanted the amount to change on the fly.

Putting it all together

The final step was to create a new material in my project and hook it up to my new shader. This material was then hooked up to the FishEyeEffect component which I had placed on my MainCamera object earlier.

And that’s it! Just playing the game shows the new fish eye effect.

You may also like...