Barrel Distortion Lens

After the fisheye lens shader turned out to be too extreme for All Fucked Up I looked for other lens shaders and found barrel distortion shaders which makes a similar effect like a fisheye but less extreme. It just widens up the lens. Looks totally cool. I got the best and simplest shader for barrel distortion here on github. It’s in a different format than I need it for jMonkeyEngine but was quite simple to adapt it.

I always start with the part I need to embed the shader in the code and as I do not make anything fancy or parametrized it’s fairly straight forward

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 BarrelDistortionFilter extends Filter {
     public BarrelDistortionFilter() {
         super(BarrelDistortionFilter.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/BarrelDistortion.j3md");
     }
}

The referenced MatDefs/Post/BarrelDistortion.j3md holds the references to the shader code and also defines possible parameters which we want to hand over from code to the shader code.

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

The vertex shader code is very simple and just hands over the tex coordinate and the position

import "Common/ShaderLib/GLSLCompat.glsllib"
attribute vec4 inPosition;
varying vec2 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 = inTexCoord;
}

The barrel distortion logic is in the fragment shader code and looks like this

import "Common/ShaderLib/GLSLCompat.glsllib"
import "Common/ShaderLib/MultiSample.glsllib"
uniform sampler2D tex0;
varying vec2 Vertex_UV;
uniform sampler2D Texture;
const float BARREL_DISTORTION = 0.25;
const float rescale = 1.0 - (0.25 * BARREL_DISTORTION);
void main()
{
    vec2 tex0 = Vertex_UV;
    vec2 texcoord = tex0 - vec2(0.5);
    float rsq = texcoord.x * texcoord.x + texcoord.y * texcoord.y;
    texcoord = texcoord + (texcoord * (BARREL_DISTORTION * rsq));
    texcoord *= rescale;
    if (abs(texcoord.x) > 0.5 || abs(texcoord.y) > 0.5)
         gl_FragColor = vec4(0.0);
    else
    {
         texcoord += vec2(0.5);
         vec3 colour = texture2D(Texture, texcoord).rgb;
         gl_FragColor = vec4(colour,1.0);
    }
}

And the lens effect in action looks like this


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.

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.