Fisheye Lens Shader

I always wanted to write some shaders and I always procrastinated it. But now I did it. Finally. I got a good source for a fisheye shader and I adapted it for the jMonkeyEngine which was not too hard.

Fisheye.j3md is the thing which holds everything together. This is jMonkeEngine specific and may look different for your game engine you use. This just holds the two shaders files together and it’s the place where you can specify the parameters which you need if you want to handover values from the code the shader pipeline.

MaterialDef Toon {
 MaterialParameters {
     Int NumSamples
     Int NumSamplesDepth
     Texture2D Texture
     Color Color
   }
   Technique {
     VertexShader GLSL100:   MatDefs/Post/Fisheye.vert
     FragmentShader GLSL100: MatDefs/Post/Fisheye.frag
     WorldParameters {     }
     Defines {
       RESOLVE_MS : NumSamples
     } 
   }
 }

Fisheye.vert is the vertex file and here I had to adjust the fact that jMonkeyEngine hands in the position as inPosition and as well the texture coordinate as inTexCoord. The example I got was for another game engine where the input UV was a vec4. To avoid having to change too much I packed the vec2 inTextCoord (which is basically the same thing as the vec4 UV) into a vec4 even tho in the end the fragment file will only use xy means the vec2.

#import "Common/ShaderLib/GLSLCompat.glsllib"
attribute vec4 inPosition;
varying vec4 Vertex_UV;
attribute vec2 inTexCoord;
void main() {
     vec2 pos = inPosition.xy * 2.0 - 1.0;
     gl_Position = vec4(pos, 0.0, 1.0);    
     Vertex_UV = vec4(inTexCoord, 0.0, 0.0);
}

That’s it the rest I took over untouched from here.

Fisheye.frag

import "Common/ShaderLib/GLSLCompat.glsllib"
import "Common/ShaderLib/MultiSample.glsllib"

uniform sampler2D tex0;
varying vec4 Vertex_UV;
const float PI = 3.1415926535;

void main() {
  float aperture = 178.0;
  float apertureHalf = 0.5 * aperture * (PI / 180.0);
  float maxFactor = sin(apertureHalf);
  vec2 uv;
  vec2 xy = 2.0 * Vertex_UV.xy - 1.0;
  float d = length(xy);
  if (d < (2.0-maxFactor)) {
    d = length(xy * maxFactor);
    float z = sqrt(1.0 - d * d);
    float r = atan(d, z) / PI;
    float phi = atan(xy.y, xy.x);
    uv.x = r * cos(phi) + 0.5; uv.y = r * sin(    phi) + 0.5
  }  else  { 
    uv = Vertex_UV.xy;
  }
  vec4 c = texture2D(tex0, uv);
  gl_FragColor = c;
}

There is a little bit of Java code involved to be able to add the filter as post filter. This is most likely different for your game engine.

package ch.artificials.bubble.system.mvc.view.post.filter;

import com.jme3.asset.AssetManager;
import com.jme3.material.Material;
import com.jme3.post.Filter;
import com.jme3.renderer.RenderManager;
import com.jme3.renderer.ViewPort;

public class FisheyeFilter extends Filter {
    public FisheyeFilter() {
        super(FisheyeFilter.class.getSimpleName());
    }

    @Override
    protected Material getMaterial() {
        return material;
    }

    @Override
    protected void initFilter(AssetManager manager, RenderManager renderManager, ViewPort vp, int w, int h) {
        material = new Material(manager, "MatDefs/Post/Fisheye.j3md");
    }
}

And this is the effect. Not too nice but a good starting point to play with this shader.


I get a small commissions for purchases made through the following links, I only have books in this section which I bought myself and which I love. No bullshit.