360° spherical panorama view
Features
- OpenGL powered
- orientation sensors to look around
- touch interactive
- pan to look around
- pinch to zoom
- a split screen mode for VR headsets
- helper functions to orient direction of camera and touches
Equirectangular projections
OpenGL has strict texture size requirements
acceptable image sizes:
- 4096 × 2048
- 2048 × 1024
- 1024 × 512
- 512 × 256
- 256 × 128
- ... (any smaller power of 2)
4096 supported on iPhone 4s / iPad2 and newer
Methods
image
-(void) setImage:(UIImage*)image
-(void) setImageWithName:(NSString*)fileName // path or bundle. will check at both
orientation
// auto-update (usually only one of these at a time is recommended)
-(void) setOrientToDevice:(BOOL) // activate motion sensors
-(void) setTouchToPan:(BOOL) // activate UIPanGesture
// aligns z-axis (into screen)
-(void) orientToVector:(GLKVector3)
-(void) orientToAzimuth:(float) Altitude:(float)
// rotate cardinal north around the image horizon. in degrees
-(void) setCardinalOffset:(float)
field of view
-(void) setFieldOfView:(float) // in degrees
-(void) setPinchToZoom:(BOOL) // activate UIPinchGesture
touches
-(void) setShowTouches:(BOOL) // overlay latitude longitude intersects
-(BOOL) touchInRect:(CGRect) // hotspot detection in world coordinates
2D - 3D conversion
-(CGPoint) screenLocationFromVector:(GLKVector3) // 2D screen point from a 3D point
-(GLKVector3) vectorFromScreenLocation:(CGPoint) // 3D point from 2D screen point
-(CGPoint) imagePixelAtScreenLocation:(CGPoint) // 3D point from 2D screen point
// except this 3D point is expressed as 2D pixel unit in the panorama image
VR Split screen
-(void) setVRMode:(BOOL)
This activates a split screen that works inside of VR headsets like Google Cardboard. TBD if more VR best practices are needed, such as a barrel shader.
- Illusion of varying depth is not available. The two screens are rendered using the same image with no difference between camera IPD.
Installation
copy PanoramaView.h/.m into your project or use CocoaPods
- use a
GLKViewController
instead ofUIViewController
- initialize your panoramaView and set it as
self.view
- implement glkView:drawInRect:
@interface ViewController (){
PanoramaView *panoramaView;
}
@end
@implementation ViewController
- (void)viewDidLoad{
[super viewDidLoad];
panoramaView = [[PanoramaView alloc] init];
[panoramaView setImageWithName:@"image.jpg"];
[self setView:panoramaView];
}
-(void) glkView:(GLKView *)view drawInRect:(CGRect)rect{
[panoramaView draw];
}
@end
Swift
- installation is easiest with CocoaPods. add
use_frameworks!
to your podfile - or, create a bridging header, copy in PanoramaView.h/.m
import PanoramaView
class ViewController: GLKViewController {
let panoramaView:PanoramaView
required init?(coder aDecoder: NSCoder) {
panoramaView = PanoramaView()
super.init(coder: aDecoder)
}
override func viewDidLoad() {
super.viewDidLoad()
panoramaView.setImageWithName("image.jpg")
self.view = panoramaView
}
override func glkView(_ view: GLKView, drawIn rect: CGRect) {
panoramaView.draw()
}
}
make sure
- no device landscape/portrait auto-rotation
- any of the 4 device orientations works, use only 1.
Orientation
- azimuth and altitude
- look direction, the Z vector pointing through the center of the screen
The program begins by facing the center column of the image, or azimuth 0°
About equirectangular
equirectangular images mapped to the inside of a celestial sphere come out looking like the original scene, and the math is relatively simple http://en.wikipedia.org/wiki/Equirectangular_projection
License
MIT