Question

So, I've messed around with three.js, works out great. The only thing I can't figure out, is how to make a camera with a real fisheye effect.

How is that possible? camera.setLens()?

Was it helpful?

Solution

The fish eye effect can be achieved using Giliam de Carpentier's shader for lens distortion.

Shader code:

function getDistortionShaderDefinition()
{
    return {

        uniforms: {
            "tDiffuse":         { type: "t", value: null },
            "strength":         { type: "f", value: 0 },
            "height":           { type: "f", value: 1 },
            "aspectRatio":      { type: "f", value: 1 },
            "cylindricalRatio": { type: "f", value: 1 }
        },

        vertexShader: [
            "uniform float strength;",          // s: 0 = perspective, 1 = stereographic
            "uniform float height;",            // h: tan(verticalFOVInRadians / 2)
            "uniform float aspectRatio;",       // a: screenWidth / screenHeight
            "uniform float cylindricalRatio;",  // c: cylindrical distortion ratio. 1 = spherical

            "varying vec3 vUV;",                // output to interpolate over screen
            "varying vec2 vUVDot;",             // output to interpolate over screen

            "void main() {",
                "gl_Position = projectionMatrix * (modelViewMatrix * vec4(position, 1.0));",

                "float scaledHeight = strength * height;",
                "float cylAspectRatio = aspectRatio * cylindricalRatio;",
                "float aspectDiagSq = aspectRatio * aspectRatio + 1.0;",
                "float diagSq = scaledHeight * scaledHeight * aspectDiagSq;",
                "vec2 signedUV = (2.0 * uv + vec2(-1.0, -1.0));",

                "float z = 0.5 * sqrt(diagSq + 1.0) + 0.5;",
                "float ny = (z - 1.0) / (cylAspectRatio * cylAspectRatio + 1.0);",

                "vUVDot = sqrt(ny) * vec2(cylAspectRatio, 1.0) * signedUV;",
                "vUV = vec3(0.5, 0.5, 1.0) * z + vec3(-0.5, -0.5, 0.0);",
                "vUV.xy += uv;",
            "}"
        ].join("\n"),

        fragmentShader: [
            "uniform sampler2D tDiffuse;",      // sampler of rendered scene?s render target
            "varying vec3 vUV;",                // interpolated vertex output data
            "varying vec2 vUVDot;",             // interpolated vertex output data

            "void main() {",
                "vec3 uv = dot(vUVDot, vUVDot) * vec3(-0.5, -0.5, -1.0) + vUV;",
                "gl_FragColor = texture2DProj(tDiffuse, uv);",
            "}"
        ].join("\n")

    };
}

One way to setup the effect using effect composer (assuming scene and renderer have been been created):

// Create camera
camera = new THREE.PerspectiveCamera( 100, window.innerWidth / window.innerHeight, 1, 1000000 );
camera.position.z = 800;

// Create effect composer
composer = new THREE.EffectComposer( renderer );
composer.addPass( new THREE.RenderPass( scene, camera ) );

// Add distortion effect to effect composer
var effect = new THREE.ShaderPass( getDistortionShaderDefinition() );
composer.addPass( effect );
effect.renderToScreen = true;

// Setup distortion effect
var horizontalFOV = 140;
var strength = 0.5;
var cylindricalRatio = 2;
var height = Math.tan(THREE.Math.degToRad(horizontalFOV) / 2) / camera.aspect;

camera.fov = Math.atan(height) * 2 * 180 / 3.1415926535;
camera.updateProjectionMatrix();

effect.uniforms[ "strength" ].value = strength;
effect.uniforms[ "height" ].value = height;
effect.uniforms[ "aspectRatio" ].value = camera.aspect;
effect.uniforms[ "cylindricalRatio" ].value = cylindricalRatio;

Following script are needed and they can be found for example from three.js GitHub page:

<script src="examples/js/postprocessing/EffectComposer.js"></script>
<script src="examples/js/postprocessing/RenderPass.js"></script>
<script src="examples/js/postprocessing/MaskPass.js"></script>
<script src="examples/js/postprocessing/ShaderPass.js"></script>
<script src="examples/js/shaders/CopyShader.js"></script>

Link to Giliam's example: http://www.decarpentier.nl/downloads/lensdistortion-webgl/lensdistortion-webgl.html

Link to Giliam's article about lens distortion: http://www.decarpentier.nl/lens-distortion

Image of my test where lens distortion effect is used:

Image

OTHER TIPS

Put a camera inside a reflective sphere. Make sure the sphere is double sided. Parent the camera and sphere together if you want to move it around your scene. Works like a charm:

http://tileableart.com/code/NOCosmos/test.html

borrowed from:

http://mrdoob.github.io/three.js/examples/webgl_materials_cubemap_dynamic2.html

cubeCamera = new THREE.CubeCamera( 1, 3000, 1024);
            cubeCamera.renderTarget.minFilter = THREE.LinearMipMapLinearFilter;
scene.add( cubeCamera );
camParent.add(cubeCamera);
var material = new THREE.MeshBasicMaterial( { envMap: cubeCamera.renderTarget } );
material.side = THREE.DoubleSide;
sphere = new THREE.Mesh( new THREE.SphereGeometry( 2, 60, 30 ), material );

It's possible to get the fisheye effect with a high Field of View.

            var fishCamera = new THREE.PerspectiveCamera( 110, window.innerWidth / window.innerHeight, 1, 1100 );
            var normalCamera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 1, 1100 );

or set

camera.fov = 110 
camera.updateProjectionMatrix();

Live Example here: http://mrdoob.github.com/three.js/examples/canvas_geometry_panorama_fisheye.html

One way is to set a large field of view on the camera:

new THREE.PerspectiveCamera(140, ... )

This will not technically be a fisheye effect, but it may be the effect you're looking for.

In a real camera lens, getting a large field of view without distorsion would likely make the lens pretty expensive, but in computer graphics, it's the easy way.

A real fisheye lens distorts the image so that straight line become curved, like in this image:

Fisheye example, CC by Ilveon at Wikipedia

If you want to create an actual fisheye effect with this kind of distorsion, you would have to modify the geometry, as in Three.js's fisheye example. In that example, the geometry is actually modified beforehand, but for a more advanced scene, you'd want to use a vertex shader to update the vertices on the fly.

A wide angle lens generally have a very low focus length.

To achieve an extreme wide angle we need to reduce focus length.

Note that fish eye lens is an extreme wide angle lens.

To reduce focus length(or to achieve extreme wide angles), one can just increase FOV (field of view), as FOV is inversely proportional to focus length.

example:

var camera_1 = new THREE.PerspectiveCamera( 45, width / height, 1, 1000 );

var camera_2 = new THREE.PerspectiveCamera( 80, width / height, 1, 1000 );

Here camera_2 is a wider angle setup.

Note

To achieve desired effect, one may have to adjust camera position.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top