鱼眼图片转化android,iOS简单实现全景图小行星和鱼眼模式

基于SceneKit,先导入SceneKit.framework

首先说一下本人对全景图的理解,所谓全景图,就是一个球体,在球体表面贴上图片,在不同位置看就会产生不同的效果。

比如如果把摄像机放在球心,这时看球面上的图片就是全景图(鱼眼图)的效果,如果放在球外边,看到的就是一个完整的球,放在球面上,看到的就是小行星的效果。

其中的翻转图片代码可根据需要使用,比如要在球内外切换的情况。

话不多说,直接上代码。

iOS全景图

.h文件:

#import#import@interface ViewController : UIViewController

/** 图片模型 */

@property (nonatomic,strong) XLPhotoModel *pModel;

/** 3D视图 */

@property (weak, nonatomic) IBOutlet SCNView *sceneView;

/** 自动播放动画 */

@property (strong, nonatomic) IBOutlet UIButton *autoPlayBtn;

@end

.m文件

/** 全景模式 */

typedef enum {

fisheye, //鱼眼

asteroid, //小行星

ball    //球

}panoramaModel;

@interface XLPicDetailVC ()

{

/** 球 */

SCNSphere *_sphere;

/** 相机 */

SCNNode *_cameraNode;

/** 球节点 */

SCNNode *_sphereNode;

/** 重力感应 */

CMMotionManager *_motionManager;

/** 原始图片 */

UIImage *_originalImage;

/** 翻转后的图片 */

UIImage *_reversalImage;

/** 是否上边界 */

BOOL _isUpBoundary;

/** 是否下边界 */

BOOL _isDownBoundary;

}

/** 全景模式 */

@property (nonatomic,assign) panoramaModel panoramaModel;

@end

@implementation XLPicDetailVC

- (void)viewDidLoad {

[super viewDidLoad];

[self layoutNavi];

//    [self layoutView];

}

- (void)didReceiveMemoryWarning {

[super didReceiveMemoryWarning];

// Dispose of any resources that can be recreated.

}

- (void)setupSceneView:(NSString *)filePath

{

filePath = [[NSBundle mainBundle] pathForResource:@"diqiu" ofType:@"jpg"];

//    filePath = [[NSBundle mainBundle] pathForResource:@"YaJunWei1" ofType:@"jpeg"];

_originalImage = [UIImage imageWithContentsOfFile:filePath];

//压缩图片

_originalImage = [UIImage imageWithData:[_originalImage compressImageWithScale:1.0 width:1000]];

//水平翻转图片

_reversalImage = [UIImage reversalImage:_originalImage];

// Set the scene

self.sceneView.scene = [[SCNScene alloc]init];

self.sceneView.showsStatistics = NO;

self.sceneView.allowsCameraControl = YES;

//修改手势

NSArray *array = self.sceneView.gestureRecognizers;

for (UIGestureRecognizer *gesture in array) {

if ([gesture isKindOfClass:[UIPanGestureRecognizer class]]) {

//修改最大手指数,防止拖动模型

((UIPanGestureRecognizer *)gesture).maximumNumberOfTouches = 1;

[self.sceneView removeGestureRecognizer:gesture];

}

}

//自定义拖动手势,实现图片上下左右界限

UIPanGestureRecognizer *panGesture = [[UIPanGestureRecognizer alloc]init];

panGesture.maximumNumberOfTouches = 1;

[panGesture addTarget:self action:@selector(panGesture:)];

[self.sceneView addGestureRecognizer:panGesture];

if (@available(iOS 11, *)) {

//        self.sceneView.defaultCameraController.maximumHorizontalAngle = M_PI * 2;

}

//Create node, containing a sphere, using the panoramic image as a texture

_sphere = [SCNSphere sphereWithRadius:20.0];

_sphere.firstMaterial.doubleSided = YES;

_sphere.firstMaterial.diffuse.contents = _reversalImage;

_sphereNode = [SCNNode nodeWithGeometry:_sphere];

_sphereNode.position = SCNVector3Make(0,0,0);

[self.sceneView.scene.rootNode addChildNode:_sphereNode];

// Camera, ...

_cameraNode = [[SCNNode alloc]init];

_cameraNode.camera = [[SCNCamera alloc]init];

[self.sceneView.scene.rootNode addChildNode:_cameraNode];

//约束

SCNTransformConstraint *constraint = [SCNTransformConstraint transformConstraintInWorldSpace:YES withBlock:^SCNMatrix4(SCNNode * _Nonnull node, SCNMatrix4 transform) {

//        transform = SCNMatrix4MakeRotation(0, -M_PI_2, 0, 0);

return transform;

}];

_sphereNode.constraints = @[constraint];

//    [self.sceneView.scene.rootNode addObserver:self forKeyPath:@"eulerAngles" options:NSKeyValueObservingOptionNew context:nil];

//重力感应

//    _motionManager = [[CMMotionManager alloc]init];

//

//    if (_motionManager.isDeviceMotionAvailable) {

//

//        _motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;

//        [_motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {

//

//            CMAttitude *attitude = motion.attitude;

//

//            _cameraNode.eulerAngles = SCNVector3Make(attitude.roll - M_PI/2.0, attitude.yaw, attitude.pitch);

//        }];

//    }

}

- (IBAction)switchBtn:(UIButton *)btn

{

_panoramaModel = (_panoramaModel + 1) % 3;

//把球转回来

[_sphereNode runAction:[SCNAction rotateToX:0 y:0 z:0 duration:0]];

//移除自动旋转动画

[_sphereNode removeActionForKey:@"YaJunWei"];

_autoPlayBtn.selected = NO;

switch (_panoramaModel) {

case fisheye: //鱼眼

{

[btn setImage:[UIImage imageNamed:@"figlhg"] forState:UIControlStateNormal];

[btn setTitle:@"鱼眼" forState:UIControlStateNormal];

_cameraNode.position = SCNVector3Make(0, 0, 0);

//设置相机视角大小

[_cameraNode.camera setYFov:60];

//水平翻转图片

_sphere.firstMaterial.diffuse.contents = _reversalImage;

break;

}

case asteroid: //小行星

{

[btn setImage:[UIImage imageNamed:@"xiaoxing"] forState:UIControlStateNormal];

[btn setTitle:@"小行星" forState:UIControlStateNormal];

_cameraNode.position = SCNVector3Make(0, 0, 20);

//设置相机视角大小

[_cameraNode.camera setYFov:120];

NSLog(@"小行星");

//水平翻转图片

_sphere.firstMaterial.diffuse.contents = _reversalImage;

//把球沿x轴转-M_PI_2,使 “天” 朝上

SCNAction *rotationAction = [SCNAction rotateByX:-M_PI_2 y:0 z:0 duration:0.5];

[_sphereNode runAction:rotationAction];

break;

}

case ball: //球

{

[btn setImage:[UIImage imageNamed:@"figlhg"] forState:UIControlStateNormal];

[btn setTitle:@"球" forState:UIControlStateNormal];

_cameraNode.position = SCNVector3Make(0, 0, 45);

//设置相机视角大小

[_cameraNode.camera setYFov:60];

//水平翻转图片

_sphere.firstMaterial.diffuse.contents = _originalImage;

break;

}

default:

break;

}

self.sceneView.scene = [[SCNScene alloc]init];

[self.sceneView.scene.rootNode addChildNode:_sphereNode];

[self.sceneView.scene.rootNode addChildNode:_cameraNode];

}

#pragma mark - 导航栏

- (void)layoutNavi

{

self.navigationController.navigationBar.tintColor = [UIColor clearColor];

self.navigationItem.hidesBackButton = YES;

UIView *naviView = [[UIView alloc] initWithFrame:CGRectMake(0, 0, UI_SCREEN_WIDTH, 32)];

self.navigationItem.titleView = naviView;

UIImageView *theImageView = [[UIImageView alloc] init];

theImageView.frame = CGRectMake(-8, -20, UI_SCREEN_WIDTH + 16, 52);

theImageView.backgroundColor = defaultColor;

[naviView addSubview:theImageView];

//返回按钮

UIButton *backBtn = [UIButton buttonWithType:UIButtonTypeCustom];

[backBtn setFrame:CGRectMake(0, 0, 40, 32)];

//    [backBtn setBackgroundImage:[UIImage imageNamed:@"a_return"] forState:UIControlStateNormal];

//    [backBtn setBackgroundImage:[UIImage imageNamed:@"a_return"] forState:UIControlStateHighlighted];

backBtn.titleLabel.font = [UIFont systemFontOfSize:15];

[backBtn setTitle:@"返回" forState:UIControlStateNormal];

[backBtn setTitleColor:TITLE_COLOR forState:UIControlStateNormal];

[backBtn addTarget:self action:@selector(backBtnClick:) forControlEvents:UIControlEventTouchUpInside];

[naviView addSubview:backBtn];

//titleLabel

UILabel *titleLabel = [[UILabel alloc]initWithFrame:CGRectMake(0, 6, 300, 20)];

titleLabel.centerX = naviView.centerX - 11;

titleLabel.font = [UIFont systemFontOfSize:15];

titleLabel.textColor = TITLE_COLOR;

titleLabel.text = self.title;

titleLabel.textAlignment = NSTextAlignmentCenter;

[naviView addSubview:titleLabel];

//右上角按钮

UIButton *rightBtn = [UIButton buttonWithType:UIButtonTypeCustom];

[rightBtn setFrame:CGRectMake(UI_SCREEN_WIDTH - 60, 6, 40, 20)];

[rightBtn setImage:[UIImage imageNamed:@"3"] forState:UIControlStateNormal];

[rightBtn setTitleColor:TITLE_COLOR forState:UIControlStateNormal];

rightBtn.titleLabel.font = [UIFont systemFontOfSize:15];

[rightBtn addTarget:self action:@selector(shareBtnClick:) forControlEvents:UIControlEventTouchUpInside];

[naviView addSubview:rightBtn];

}

- (void)backBtnClick:(UIButton *)btn

{

[self dismissViewControllerAnimated:YES completion:nil];

}

- (void)shareBtnClick:(UIButton *)btn

{

}

- (void)setPModel:(XLPhotoModel *)pModel

{

_pModel = pModel;

[MBProgressHUD showMessage:@"加载中..." toView:self.view];

NSString *url = [NSString stringWithFormat:@"%@/%@",SERVICE_DOMAIN,pModel.panoramaPic];

[XLHttpTool downloadTaskWithURL:url progress:^(NSProgress *downloadProgress) {

NSLog(@"%f",1.0 * downloadProgress.completedUnitCount / downloadProgress.totalUnitCount);

} destination:^NSURL *(NSURL *targetPath, NSURLResponse *response) {

NSString *documentPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];

NSString *path = [documentPath stringByAppendingPathComponent:@"panorama"];

NSFileManager *fileMgr = [NSFileManager defaultManager];

BOOL isDirectory = NO;

if ([fileMgr fileExistsAtPath:path isDirectory:&isDirectory]) {

if (!isDirectory) {

[fileMgr removeItemAtPath:path error:nil];

[fileMgr createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];

}

}else{

[fileMgr createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];

}

path = [path stringByAppendingPathComponent:response.suggestedFilename];

return [NSURL fileURLWithPath:path];

} completionHandler:^(NSURLResponse *response, NSURL * _Nullable filePath, NSError * _Nullable error) {

NSLog(@"下载完成");

[MBProgressHUD hideHUDForView:self.view];

[self setupSceneView:[filePath path]];

}];

}

//自动旋转

- (IBAction)autoPlay:(UIButton *)btn

{

btn.selected = !btn.selected;

if (btn.selected) {

NSLog(@"添加动画");

switch (_panoramaModel) {

case fisheye: //鱼眼

{

[_sphereNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:M_PI z:0 duration:ACTION_DURATION]] forKey:@"YaJunWei"];

break;

}

case asteroid: //小行星

{

[_sphereNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:0 z:M_PI duration:ACTION_DURATION]] forKey:@"YaJunWei"];

break;

}

case ball: //球

{

[_sphereNode runAction:[SCNAction repeatActionForever:[SCNAction rotateByX:0 y:M_PI z:0 duration:ACTION_DURATION]] forKey:@"YaJunWei"];

break;

}

default:

break;

}

}else{

NSLog(@"移除动画");

[_sphereNode removeActionForKey:@"YaJunWei"];

}

}

#pragma mark - 自定义手势,实现图片上下左右界限

- (void)panGesture:(UIPanGestureRecognizer *)panGesture

{

CGPoint pt = [panGesture translationInView:self.sceneView];

NSLog(@"pt.x = %f,pt.y = %f",pt.x,pt.y);

//旋转

//距离

//    CGFloat distance = MAX(fabs(pt.x), fabs(pt.y));

CGFloat distance = fabs(pt.x) > fabs(pt.y) ? pt.x : pt.y;

//已旋转角度

SCNVector4 vector = _sphereNode.rotation;

//    NSLog(@"vector:x:%f y:%f z:%f w:%f",vector.x,vector.y,vector.z,vector.w);

//判断滑动方向

if (fabs(pt.x) >= fabs(pt.y)) {

//水平滑动

switch (_panoramaModel) {

case fisheye: //鱼眼

{

//算出对应旋转角度

[_sphereNode runAction:[SCNAction rotateByX:0 y:distance / self.sceneView.width * 2 * M_PI  z:0 duration:0.01]];

break;

}

case asteroid: //小行星

{

//算出对应旋转角度

[_sphereNode runAction:[SCNAction rotateByX:0 y:0 z:distance / self.sceneView.width * 2 * M_PI duration:0.01]];

break;

}

case ball: //球

{

//算出对应旋转角度

[_sphereNode runAction:[SCNAction rotateByX:0 y:distance / self.sceneView.width * 2 * M_PI  z:0 duration:0.01]];

break;

}

default:

break;

}

}else{

//垂直移动

switch (_panoramaModel) {

case fisheye: //鱼眼

{

/*

if (panGesture.state == UIGestureRecognizerStateChanged)

{

//即将超出边界

if (vector.w >= M_PI_2 && !_isUpBoundary && !_isDownBoundary)

{

if (pt.y < 0){

//到达或超出边界,转到边界

NSLog(@"到达上边界");

_isUpBoundary = YES;

[_sphereNode runAction:[SCNAction rotateToX:-M_PI_2 y:vector.y z:vector.z duration:0]];

}else if (pt.y > 0){

//到达或超出边界,转到边界

NSLog(@"到达下边界");

_isDownBoundary = YES;

[_sphereNode runAction:[SCNAction rotateToX:M_PI_2 y:vector.y z:vector.z duration:0]];

}

}else{

//算出对应旋转角度

if ((_isUpBoundary && pt.y < 0) || (_isDownBoundary && pt.y > 0)) {

}else{

[_sphereNode runAction:[SCNAction rotateByX:distance / self.sceneView.height * 2 * M_PI y:0 z:0 duration:0]];

if (vector.w < M_PI_2) {

_isUpBoundary = _isDownBoundary = NO;

}

}

}

}

*/

break;

}

case asteroid: //小行星

{

//算出对应旋转角度

[_sphereNode runAction:[SCNAction rotateByX:0 y:0 z:distance / self.sceneView.height * 2 * M_PI duration:0.01]];

break;

}

case ball: //球

{

//算出对应旋转角度

//                [_sphereNode runAction:[SCNAction rotateByX:0 y:distance / self.sceneView.height * 2 * M_PI  z:0 duration:0.01]];

break;

}

default:

break;

}

}

//每次移动完,将移动量置为0,否则下次移动会加上这次移动量

[panGesture setTranslation:CGPointMake(0, 0) inView:self.sceneView];

}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void *)context

{

NSLog(@"%@",change);

}

@end

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值