7
$\begingroup$

I would like to plot a Graphics3D object using a fisheye view like, for example, the equidistant fisheye described here, where the distance R of a point from the centre of the image is proportional to the angles θ between the points in the real 3-dim scene: R = f×θ.

For illustration, let's take the Graphics3D object produced by the following Mathematica code

paraboloid[a_, u_, nu_] := {a Sqrt[u] Cos[nu], a Sqrt[u] Sin[nu], u}
a = 10;
h = 20;
gr1 = ParametricPlot3D[paraboloid[a, u, nu], {nu, 0, Pi}, {u, 0, h}];
gr2 = Graphics3D[Table[
        Rotate[Cuboid[## - {1, 1, 0}, ## + {1, 1, 2}], nu, {0, 0, 1}, ##]&
            [paraboloid[a, u, nu]], 
      {nu, 0, Pi, Pi/20}, {u, 2, h, 2}]];
Show[gr1, gr2, ViewPoint -> {0, -0.5, 0}]

which produces some sort of paraboloid amphitheatre in usual perspective as follows

enter image description here

How can I view the same scene using a fisheye view instead of the usual perspective?

For reference, here is an example of fisheye perspective from MathWorld

enter image description here

$\endgroup$
8
  • $\begingroup$ Just make the ViewPoint really close to the object. $\endgroup$ Jan 7, 2019 at 17:48
  • 2
    $\begingroup$ Changing ViewPoint does not change the perspective transformation. While ViewMatrix only allows for homogeneous transformations. In both cases, straight lines remain straight, which is not the case for the transformation required in fisheye view. $\endgroup$
    – divenex
    Jan 7, 2019 at 18:35
  • 1
    $\begingroup$ Related: How to remap a fisheye image? $\endgroup$
    – C. E.
    Jan 8, 2019 at 5:05
  • 2
    $\begingroup$ I do not have time to implement this, nor is it ideal, but you can give it a try: create a standard rectilinear rendering with a known field of view, then transform the pixels of the image to a fisheye projection (ImageTransformation). You will need to derive the equations for the transform. It'll take some time but it's not a difficult one. Why is this not ideal? Because in practice the field of view of a rectilinear projection is limited, so you won't be able to get a 180-degree (or greater) view from a single rendering. $\endgroup$
    – Szabolcs
    Jan 8, 2019 at 13:14
  • 1
    $\begingroup$ To work around that, you can create multiple rectilinear projections, e.g. looking in 5 directions (out of 6 corresponding to the 6 sides of a cube). First map the pixels from each onto a sphere (spherical coordinates), then map each one to the same fisheye projection and obtain a full 180-degree view. Hopefully Mathematica's renderings are accurate and the 5 images will match seamlessly. $\endgroup$
    – Szabolcs
    Jan 8, 2019 at 13:16

2 Answers 2

6
$\begingroup$

I follow the approach suggested by Alex, which was taken from the Mathematica documentation of ImageForwardTransformation. But the required explanation and changes to produce an actual fisheye view are too extensive to be included in a comment, so I write them here.

As mentioned in my comment to Alex's answer, the key is to use as the radial coordinate of the ImageForwardTransformation the angles between the objects in the scene, as seen from the observer. This would produce the ideal and most common fisheye view, which is the equidistant, or equiangle, fisheye. But other fisheye formulas can be used by simply replacing the angle with a function of angles.

The key is then to obtain angles to the centre of the view for the pixels in a Mathematica image. Mathematica uses standard perspective for which is easy to show (see figure below) that the angles are given by theta = ArcTan[R/d].

enter image description here

But to obtain proper angles from the image pixel units one has to consider the definition of ViewPoint and the bounding box of the Graphics3D object:

ViewPoint -> {x,y,z} gives the position of the view point relative to the center of the three‐dimensional box that contains the objects.

The view point is given in a special scaled coordinate system in which the longest side of the bounding box has length 1. The centre of the bounding box is taken to have coordinates {0,0,0}.

Other key steps for the fisheye view are (i) to place the ViewPoint orthogonal to the bounding box (two ViewPoint coordinates have to be zero), (ii) as close as possible (but at a finite distance) to the nearest side of the box and (iii) with ViewAngle -> All. This will produce an extremely distorted view, for a field of view close to 180 degrees, which can then be remapped into a fisheye view.

The image has to be rasterized at high pixels resolution, to account for the fact that the central part of the distorted image will appear much smaller than the outer parts, due to the large field of view. This, of course, results in a very inefficient procedure. An alternative would be to obtain different views, as Szabolcs suggested in his comments. But this would significantly complicate the procedure.

Following the above steps here is the proposed solution.

I take for illustration the amphitheatre proposed in my original question.

paraboloid[a_, u_, nu_] := {a Sqrt[u] Cos[nu], a Sqrt[u] Sin[nu], u}

a = 5;
h = 20;
gr1 = ParametricPlot3D[paraboloid[a, u, nu], {nu, 0, Pi}, {u, 0, h}];
gr2 = Graphics3D[
   Table[Rotate[Cuboid[## - {1, 1, 0}, ## + {1, 1, 2}], nu, {0, 0, 1}, ##]     
       &[paraboloid[a, u, nu]], 
    {nu, 0, Pi, Pi/10}, {u, 2, h, 2}]];

Which appears as follow with default viewing parameters

Show[gr1, gr2]

enter image description here

and I show it from a very close distance to the bounding box side and using the full field of view

dist = 0.3;
gr = Show[gr1, gr2, ViewPoint -> {0, -dist, 0}, ViewAngle -> All];
img = Rasterize[gr, RasterSize -> 1000]

This produces the original very distorted (non fisheye) perspective, covering, in this case, a field of view of 169 degrees

Standard Mathematica perspective

This image can be remapped into an equidistant fisheye view, using ImageForwardTransformation as suggested by Alex, but with a proper fisheye transformation, as follow

f[point_, center_, edgeTan_] := With[
  {r = Norm[point - center], ang = ArcTan @@ (point - center), radius = Max[center]},
  rnew = radius*ArcTan[edgeTan*r/radius]/ArcTan[edgeTan];
  center + rnew*{Cos[ang], Sin[ang]}]

rat = BoxRatios /. AbsoluteOptions[gr, BoxRatios];
ratios = rat/Max @@ rat;
center = ImageDimensions[img]/2;
edgeTan = (0.5 Max @@ ratios[[{1, 3}]])/(dist - 0.5 ratios[[2]]); (* Tan of edge *)
ImageForwardTransformation[img, f[#, center, edgeTan] &, DataRange -> Full]
fov = 2 ArcTan[edgeTan]/Degree  (* Image field of view for information *)

This produces the desired equidistant fisheye view, where I show in black the region outside the field of view of the original Mathematica projection

enter image description here

$\endgroup$
2
$\begingroup$

The author wants to see the same scene but in a different perspective, which is called a fisheye. This standard example is in the section ImageForwardTransformation[]. Reproduced with minor corrections

paraboloid[a_, u_, nu_] := {a Sqrt[u] Cos[nu], a Sqrt[u] Sin[nu], u}
a = 10;
h = 20;
gr1 = ParametricPlot3D[paraboloid[a, u, nu], {nu, 0, Pi}, {u, 0, h}, 
   Axes -> False, Boxed -> False, ImageSize -> 400];

gr2 = Graphics3D[
   Table[Rotate[Cuboid[## - {1, 1, 0}, ## + {1, 1, 2}], 
       nu, {0, 0, 1}, ##] &[paraboloid[a, u, nu]], {nu, 0, Pi, 
     Pi/20}, {u, 2, h, 2}], Boxed -> False];
im = Show[gr1, gr2, ViewPoint -> {0, -0.5, 0}]
f[x_, y_, c_] := 
 With[{r = N@Sqrt[(x - c[[1]])^2 + (y - c[[2]])^2], 
   t = ArcTan[x - c[[1]], y - c[[2]]], R = Min[c]}, rn = Sqrt[r*R];
  {rn*Cos[t] + c[[1]], rn*Sin[t] + c[[2]]}]
ImageForwardTransformation[im, f[#[[1]], #[[2]], {400, 400}/2] &, 
 DataRange -> Full, Background -> None]

fig1

$\endgroup$
6
  • 1
    $\begingroup$ I do not see how this would help answering my question... $\endgroup$
    – divenex
    Jan 8, 2019 at 14:10
  • 1
    $\begingroup$ As I understand it, you have no question. You're just experimenting with 3D graphics. $\endgroup$ Jan 8, 2019 at 14:25
  • $\begingroup$ You misunderstood. Feel free to suggest how to make it clearer so others do not do repeat your mistake. $\endgroup$
    – divenex
    Jan 8, 2019 at 14:40
  • $\begingroup$ Oh, now I see that you want to see the same scene but in a different perspective. $\endgroup$ Jan 8, 2019 at 15:12
  • $\begingroup$ Thanks. Your current version uses the right approach but is not a true fisheye view. This is why the ImageForwardTransformation documentation (reference.wolfram.com/language/ref/…) calls it "fisheye effect". It only produces a spherical distortion in the centre. However, this could be corrected by redefining rn to give actual angles (for an equidistant fisheye), which are proportional to ArcTan[k*r]. $\endgroup$
    – divenex
    Jan 9, 2019 at 15:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.