Omnidirectional fisheye for dome projectionPaul BourkeDecember 2025
The following presents a solution for omnidirectional stereoscopic projection into a horizontal (planetarium style) hemispherical dome. It is similar to the more traditional omnidirectional stereoscopic projection for cylindrical displays which allow multiple viewers to occupy the space, all potentially looking in different directions. Stereoscopic dome projection is highly problematic. Confounding attempts to create stereoscopic content for all domes is the realisation that not all dome configurations are the same. For example, a planetarium dome can be horizontal or tilted, the seating can be directional or circular. Many planetariums that offer stereoscopic content assume the seating is directional, that is, the bulk of the audience is facing in one direction (roughly). In this case the stereoscopic fisheye content can be created as if the viewer is in a single position, known in the industry as the "sweet spot". Every other position gets a distorted view of the content, including incorrect depth cues and often incurring high visual stress and increased discomfort. Omnidirectional stereoscopic image pairs are designed to allow multiple audience members to be each looking in different directions. In the context of an hemispherical dome, omnidirectional stereoscopic content works best in exactly the situations where directional stereoscopic content works the worst, namely, in a horizontal dome with circular seating.
The most elegant solution for generating omnidirectional stereoscopic fisheye images is with rendering engines that support cameras where each position on the image plane can be defined by a ray position and direction. The implementation presented below gives the basic mathematics, it is in a similar vein to the case for an omnidirectional cylindrical panorama. As with most stereoscopic rendering, it is usually advisable to create the virtual model in real world coordinates. This allows one to more readily create correct stereo pairs, for example, where zero parallax is at the screen depth and objects at infinity are separated by the eye separation (no divergence). For correct stereoscopic rendering, a different render is required when the viewing system geometry changes, of course this is strictly speaking true for all rendering of a virtual scene, stereoscopic or otherwise. For example, in the following the dome radius is defined and if virtual objects are placed at that distance in the scene then they will appear to reside at the correct distance, that is, at the screen depth. If the virtual objects are dimensioned correctly with respect to the physical dome then they will appear to be of the correct scale when viewed within the dome. There is one problem and that occurs at the top of the dome (center of the fisheye). In that region it is obviously not possible for the stereo pairs of a virtual object to be correct for all viewers. In the example here there are no virtual objects at that location, another solution that can be applied in some scenes is to drop the eye separation to zero towards the north pole. The following is the code for a "user_defined" camera in Povray. The image plane in x and y has coordinates ranging from -0.5 to 0.5. The solution allows precise positioning of the zero parallax distance.
#version 3.8;
/*
Experiment with ODSP (fisheye) using PovRay user_defined camera in version 3.8
Designed for a horizontal dome, viewers near center looking towards the rim.
Deals with left and right eye separately, suggest ffmpeg to create left/right arrangement
Handles positioning of zero parallax, typically at screen distance or infinity
Supports both a left and right handed coordinate system
The camera rig can be panned in order to set where "front"
(y axis) corresponds to in the panorama
All length units in meters
*/
#declare DOMERADIUS = 4.5/2;
// View position
#declare ODSP_camerax = 0;
#declare ODSP_cameray = 0;
#declare ODSP_cameraz = 0;
#declare ODSP_eyesep = 0.065/2; // Half the eye separation
#declare ODSP_handed = -1; // -1 for left, 1 for right
#declare ODSP_whicheye = -1; // -1 for left, 1 for right, 0 for center
#declare ODSP_eyefactor = 1; // How much to reduce the eye separation at poles
// Value of 1 will retain things at zero parallax
// Value of 0.001 will do no adjustment
#declare ODSP_parallax = DOMERADIUS; // Distance to zero parallax
// Set to very large number for infinity
#declare ODSP_panangle = 0; // Pan the camera rig
// Mostly to control where "front" is
// ODSP fisheye
#declare ODSP_parangle = atan(ODSP_eyesep/ODSP_parallax);
camera {
user_defined
location {
function {
ODSP_camerax -
ODSP_whicheye * ODSP_eyesep * cos(atan2(y,x) + ODSP_panangle) *
pow(sin(2*sqrt(x*x+y*y)*pi/2),ODSP_eyefactor) * ODSP_handed
}
function {
ODSP_cameray +
ODSP_whicheye * ODSP_eyesep * sin(atan2(y,x) + ODSP_panangle) *
pow(sin(2*sqrt(x*x+y*y)*pi/2),ODSP_eyefactor)
}
function {
ODSP_cameraz
}
}
direction {
function {
-sin(atan2(y,x) -
ODSP_whicheye * ODSP_parangle + ODSP_panangle) *
sin(2*sqrt(x*x+y*y)*pi/2) * ODSP_handed
}
function {
-cos(atan2(y,x) -
ODSP_whicheye * ODSP_parangle + ODSP_panangle) *
sin(2*sqrt(x*x+y*y)*pi/2)
}
function {
cos(2*sqrt(x*x+y*y)*pi/2)
}
}
}
|