8

How do I get a distortion like what a fisheye lens does to a view with a SCNCamera in Scene Kit?

Something like this kind of "bowing" of the imagery:

enter image description here

// as Rickster pointed out, this kind of distortion is known as "Barrel Distortion".

From the docs, this is the part that got me intrigued by the possibility of doing this kind of distortion with the camera:

If you compute your own projection transform matrix, you can use this method to set it directly, overriding the transformation synthesized from the camera’s geometric properties.

Unfortunately I know nothing about the powers and possibilities of computing ones own projection transform matrix. I'm hoping it's possible to do this kind of distortion via it... but dunno, hence the question.

Any other means via a camera is ideal. Too. Wanting to avoid post processing trickery and get the more "organic" look of this kind of distortion when the camera rotates and moves through the scene.

See any skateboarding video for how this looks in real life.

Confused
  • 6,048
  • 6
  • 34
  • 75
  • Documentation says there are xFov and yFov (field of view) parameters that you can control. Setting them to high value (60 degrees is default value) will result in distortion. – Archie Jul 27 '15 at 15:03
  • They're not independent, nor does putting them at high values create this kind of distortion. It simply widens the view via perspective. – Confused Jul 27 '15 at 17:58
  • To be more precise, adjusting x or y FoV means the other is automatically adjusted to match the current window's aspect ratio. – Confused Jul 27 '15 at 18:14
  • 1
    High fov just leads to rectilinear distortion, not barrel distortion. – rickster Jul 27 '15 at 19:09

1 Answers1

9

What you are looking for is called Barrel Distrortion.

There are a few ways of doing this, all of them using GLSL shaders.

You can either use classic OpenGL code, such as this example for the Occulus Rift (you will need to change the shader a little bit), or my personal favorite: SCNTechnique.

Create a technique containing a Barrel Fragment Shader (.fsh), and set its draw parameter to DRAW_QUAD. Then, simply apply the technique to your camera.

You can find an example of Barrel Distortion shader here : http://www.geeks3d.com/20140213/glsl-shader-library-fish-eye-and-dome-and-barrel-distortion-post-processing-filters/2/


EDIT: here's a sample code:

barrel.json (this should go in your scnassets bundle)

{
  "passes" : {
    "barrel" : {
      "outputs" : {
        "color" : "COLOR"
      },
      "inputs" : {
        "colorSampler" : "COLOR",
        "noiseSampler" : "noiseSymbol",
        "a_position" : "a_position-symbol"
      },
      "program" : "art.scnassets/barrel",
      "draw" : "DRAW_QUAD"
    }
  },
  "sequence" : [
    "barrel"
  ],
  "symbols" : {
    "a_position-symbol" : {
      "semantic" : "vertex"
    },
    "noiseSymbol" : {
      "image" : "noise.png",
      "type" : "sampler2D"
    },
    "barrelPower" : {
      "type" : "float"
    }
  }
}

barrel.vsh

attribute vec4 a_position;
varying vec2 uv;

void main() {
    gl_Position = a_position;
    uv = a_position.xy;
}

barrel.fsh

// Adapted from :
// http://www.geeks3d.com/20140213/glsl-shader-library-fish-eye-and-dome-and-barrel-distortion-post-processing-filters/2/

uniform sampler2D colorSampler;
const float PI = 3.1415926535;
uniform float barrelPower;

varying vec2 uv;


vec2 Distort(vec2 p)
{
    float theta  = atan(p.y, p.x);
    float radius = length(p);
    radius = pow(radius, barrelPower);
    p.x = radius * cos(theta);
    p.y = radius * sin(theta);
    return 0.5 * (p + 1.0);
}


void main() {

    vec2 rg = 2.0 * uv.xy - 1.0;
    vec2 uv2;
    float d = length(xy);
    if (d < 1.0){
        uv2 = Distort(xy);
    }else{
        uv2 = uv.xy;
    }

    gl_FragColor = texture2D(colorSampler, uv2);
}

something.m

NSURL *url = [[NSBundle mainBundle] URLForResource:@"art.scnassets/barrel" withExtension:@"json"];
NSDictionary *tecDic = [NSJSONSerialization JSONObjectWithData:[NSData dataWithContentsOfURL: url] options:nil error:nil];

SCNTechnique* technique = [SCNTechnique techniqueWithDictionary:tecDic];

[technique setValue: [NSNumber numberWithFloat:0.5]  forKey:@"barrelPower"];


cameraNode.technique = technique;
Moustach
  • 2,904
  • 1
  • 13
  • 23
  • Good call — `SCNTechnique` is for render post-processing, so doing it all in-shader with a technique lets you leave the rest of the pipeline alone and keep your code to a minimum (just a fragment shader and some stuff in the technique plist for configuring it). – rickster Jul 27 '15 at 19:12
  • That's great, but I'm specifically wondering if it's possible with the matrix transforms and other wonders available to SCNCamera, hence the title of the question. – Confused Jul 27 '15 at 21:09
  • 1
    Short answer: not yet. However thanks to ModelIO, [new functions](https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNCamera_Class/index.html#//apple_ref/occ/clm/SCNCamera/cameraWithMDLCamera:) and the new [MDLCamera](https://developer.apple.com/library/prerelease/ios/documentation/ModelIO/Reference/MDLCamera_Class/index.html#//apple_ref/occ/instp/MDLCamera/fisheyeDistortion), you should soon have access to Fish Eye effect without additional code. For now, what we have access to is not enough. – Moustach Jul 27 '15 at 22:30
  • WOW! THANK YOU!!! We are working in iOS 9, so can use this immediately. I never thought to look in Model I/O for a camera. And this thing is it.. has :: var barrelDistortion: Float :: and :: var fisheyeDistortion: Float !!! – Confused Jul 28 '15 at 02:39
  • I'll edit the question to include "via the camera" instead of SCNCamera specific, as I simply didn't know there was another Scene Kit type of camera, but this is exactly the desirable answer. Can you then post this MDLCamera as an answer and we can call it a day. THANK YOU!!! – Confused Jul 28 '15 at 02:41
  • please note that you cannot use an MDLCamera in SceneKit. You can build a SCNCamera form an MDLCamera, but not everything will be available (including barrel distortion) – mnuages Jul 28 '15 at 09:13
  • Hm, then my initial answer still stands... @mnuages: Does that mean all the PBR options of ModelIO are not available in SceneKit? – Moustach Jul 28 '15 at 13:49
  • Model I/O provides structures that can be useful to build a 3D pipeline. But that doesn't mean all it can express is available in SceneKit, MetalKit, or other frameworks. And no, SceneKit doesn't support PBR. – mnuages Jul 28 '15 at 13:53
  • 1
    @mnuages what's the point of a 3D pipeline if it doesn't do much? And why hype it (as the WWDC video does) as though it's fully incorporated and compatible... when it's not anywhere near that. And: where's a list of what it can do that's SceneKit compatible vs not. PBR not being part of SceneKit via Model I/O is a true WTF moment. Makes the entire WWDC video look like the kind of "marketing" that Unity does. // I'm assuming you work for Apple. – Confused Jul 28 '15 at 15:27
  • @Confused: Since the MDL option did not work, do you want me to add a SCNTechnique example? – Moustach Aug 02 '15 at 11:55
  • Yes, please! and THANK YOU! – Confused Aug 02 '15 at 23:38
  • 2
    @Confused I added a code sample. I wrote it here so it might need some debugging, but the overall workflow is there. Let me know if you have any question! – Moustach Aug 03 '15 at 13:31
  • @Moustach I'm getting an error about `Use of undeclared identifier 'xy'` I'm using OpenGL ES, not metal on ios9. Did something change and the xy and d vars are no longer global? – Will May 05 '16 at 00:39