camerathree.jsfisheye

Three.js - Fisheye effect


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()?


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