## Fisheye Lens (Hyperbolic Lens) Vertex Shader in GLSL

Today I will write about my fisheye (hyperbolic) lens shader. It was part of a larger project to simulate a wide depth of field and imperfections in human eyesight. Anyway, I like the way it came out, so I will describe how I went about the process in GLSL. A fisheye lens allows an extremely wide field of view, nearly 180 degrees, but it also bends the light in order to fit it all on a square so you get an interesting effect where the edges become distorted and straight lines become curved. Since it’s just an approximation, and it works on vertices, the quality is limited to the amount of vertices on screen. Here are some pictures which describe how it works. (10,000 hours in ms paint)

First we project A which is the vector from the camera to the point we are looking at, unto B, which is the lookat point. This is the point that is in the center of the frustum, basically the Z axis in the current coordinate system. Then after projecting we get C. Then we subtract and get the C-A vector. Then we find a halfway point of that and calculate D by subtracting camera_pos – C-A/2.

Then we take the magnitude of A and multiply it by the direction of D effectively rotating point A and bringing it closer to the center of the camera.

Here is a scene with a regular 45 degree field of view.

And now what we’ve all been waiting for. Per pixel phong shading + fisheye lens shader.

``` 1: varying vec4 normal, light_dir, eye_vec, lookat;
2: const float PI =  3.14159265;
3:
4: void main()
5: {
6: 	vec4 ambient, diffuse, specular;
7: 	float NdotL, RdotV;
8:
9: 	normal = vec4(gl_NormalMatrix * gl_Normal, 0.0);
10: 	vec4 vVertex = gl_ModelViewMatrix * gl_Vertex;
11: 	light_dir = gl_LightSource[0].position - vVertex;
12: 	eye_vec = -vVertex;
13:
14: 	vec4 temp_pos = ftransform();
15:
16: 	float dist = length(eye_vec);
17: 	lookat = eye_vec - temp_pos;
18: 	vec4 dir = temp_pos - eye_vec;
19: 	vec4 center = normalize(-eye_vec);
20: 	vec4 proj = dot(temp_pos, normalize(-lookat)) * normalize(-lookat);
21:
22: 	vec4 c = temp_pos - proj;
23:
24: 	float magnitude = .01;//1-acos(dot(normalize(-eye_vec), normalize(temp_pos)));
25:
26: 	c = length(c) * magnitude * normalize(c);
27:
28: 	vec4 dir2 = normalize(c-lookat);
29:
30: 	dir2 = (dir2 * dist);
31:
32: 	gl_Position.xyz = dir2.xyz;
33: 	gl_Position.w = ftransform().w;
34:
35: }
```

``` 1: varying vec4 normal, light_dir, eye_vec;
2:
3: uniform vec4 camera_pos;
4:
5: void main()
6: {
7:
8: 	vec4 ambient, diffuse, specular;
9: 	float NdotL, RdotV;
10:
11: 	vec4 N = normalize(normal);
12: 	vec4 L = normalize(light_dir);
13: 	NdotL = dot(N, L);
14: 	//RdotV = max(dot(R, V), 0.0);
15: 	gl_FragColor = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
16:
17: 	if(NdotL > 0.0)
18: 	{
19: 		ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
20: 		diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
21: 		specular = gl_FrontMaterial.specular * gl_LightSource[0].specular;
22: 		vec4 E = normalize(eye_vec);
23: 		vec4 R = reflect(-L, N);
24:
25: 		gl_FragColor +=  (NdotL * diffuse) +
26: 				specular * pow(max(dot(R, E), 0.0), gl_FrontMaterial.shininess);
27: 	}
28: }```
• matFr
• March 19th, 2012

nice, thanks you, helped me!

1. This is awsome. I have been attempting this for so long but in python + opengl. Do you have the full code. Thats executable or how would some one be able to execute it.

Thanks.

2. I wrote this a while ago so I don’t have the original exe, but there’s an implementation here in an android library: http://codeanticode.wordpress.com/2010/08/22/glgraphics-0-9-4/

• BrunoFlyboy
• November 8th, 2013

Hi! This is great! I’ve been looking for clear explanations of this. Thank you!

I’ve tested it out but I’m getting a weird effect. I get the fisheye transformation but all my normals are reversed (I see the inside of objects)…!

Something like if the projection was made behind the camera.. Have you run into this during your development? And if not, do you have an idea of what I’m experimenting?

Thanks 🙂

• Check your state – set different cull mode like ccw or cw or maybe alter the winding order of your geometry.

• Wollen
• November 4th, 2014