Three.js - Fisheye effect

前端 未结 5 1420
渐次进展
渐次进展 2021-02-03 15:12

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?

相关标签:
5条回答
  • 2021-02-03 15:29

    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

    0 讨论(0)
  • 2021-02-03 15:31

    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.

    0 讨论(0)
  • 2021-02-03 15:47

    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:

    0 讨论(0)
  • 2021-02-03 15:52

    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 );
    
    0 讨论(0)
  • 2021-02-03 15:53

    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.

    0 讨论(0)
提交回复
热议问题