全景视图iOS

//  PanoramaUtil.c
//  VRPanoramaKit
//
//  Created by 小发工作室 on 2017/9/22.
//  Copyright © 2017年 小发工作室. All rights reserved.
//

#include "PanoramaUtil.h"
#include <stdlib.h>
#include <math.h>

#define ES_PI  (3.14159265f)


/**
 根据numSlices和radius的大小取索引,顶点,纹理的值
 
 @param numSlices 球面切面数量
 @param radius 半径
 @param vertices 3维顶点坐标
 @param texCoords 纹理
 @param indices 索引
 @param vertices_count 3维顶点坐标数量
 @return 索引数量
 */
int esGenSphere(int numSlices,
                float radius,
                float **vertices,
                float **texCoords,
                uint32_t **indices,
                int *vertices_count) {
    
    
    int numParallels = numSlices / 2;
    int numVertices  = (numParallels + 1) * (numSlices + 1);
    int numIndices   = numParallels * numSlices * 6;
    
    float angleStep  = (2.0f * ES_PI) / ((float) numSlices);
    
    if (vertices != NULL) {
        *vertices = malloc(sizeof(float) * 3 * numVertices);
    }
    
    if (texCoords != NULL) {
        *texCoords = malloc(sizeof(float) * 2 * numVertices);
    }
    
    if (indices != NULL) {
        *indices = malloc(sizeof(uint32_t) * numIndices);
    }
    
    for (int i = 0; i < numParallels + 1; i++) {
        for (int j = 0; j < numSlices + 1; j++) {
            int vertex = (i * (numSlices + 1) + j) * 3;
            
            if (vertices) {
                (*vertices)[vertex + 0] = radius * sinf(angleStep * (float)i) * sinf(angleStep * (float)j);
                (*vertices)[vertex + 1] = radius * cosf(angleStep * (float)i);
                (*vertices)[vertex + 2] = radius * sinf(angleStep * (float)i) * cosf(angleStep * (float)j);
            }
            
            if (texCoords) {
                int texIndex = (i * (numSlices + 1) + j) * 2;
                (*texCoords)[texIndex + 0] = (float)j / (float)numSlices;
                (*texCoords)[texIndex + 1] = 1.0f - ((float)i / (float)numParallels);
            }
        }
    }
    
    // Generate the indices
    if (indices != NULL) {
        uint32_t *indexBuf = (*indices);
        for (int i = 0; i < numParallels ; i++) {
            for (int j = 0; j < numSlices; j++) {
                *indexBuf++ = i * (numSlices + 1) + j;
                *indexBuf++ = (i + 1) * (numSlices + 1) + j;
                *indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
                
                *indexBuf++ = i * (numSlices + 1) + j;
                *indexBuf++ = (i + 1) * (numSlices + 1) + (j + 1);
                *indexBuf++ = i * (numSlices + 1) + (j + 1);
            }
        }
    }
    
    if (vertices_count) {
        *vertices_count = numVertices;
    }
    
    return numIndices;
}
//  PanoramaUtil.h
//  VRPanoramaKit
//
//  Created by 小发工作室 on 2017/9/22.
//  Copyright © 2017年 小发工作室. All rights reserved.
//

#ifndef PanoramaUtil_h
#define PanoramaUtil_h

#include <stdio.h>

/**
 根据numSlices和radius的大小取索引,顶点,纹理的值
 
 @param numSlices 球面切面数量
 @param radius 半径
 @param vertices 3维顶点坐标
 @param texCoords 纹理
 @param indices 索引
 @param vertices_count 3维顶点坐标数量
 @return 索引数量
 */
int esGenSphere(int numSlices,
                float radius,
                float **vertices,
                float **texCoords,
                uint32_t **indices,
                int *vertices_count);

#endif /* PanoramaUtil_h */
//
//  PanoramaController.h
//  VRPanoramaKit
//
//  Created by 小发工作室 on 2017/9/21.
//  Copyright © 2017年 小发工作室. All rights reserved.
//

#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import <CoreMotion/CoreMotion.h>

@interface PanoramaController : GLKViewController

@property (nonatomic, copy  ) NSString        *imageName;

@property (nonatomic, copy  ) NSString        *imageNameType;

@property (nonatomic, strong) CMMotionManager *motionManager;

@property (nonatomic, strong) GLKView         *panoramaView;


/**
 初始化全景控制器

 @param imageName 全景图名字
 @param type 全景图类型,默认是jpg
 @return PanoramaController
 */
- (instancetype)initWithImageName:(NSString *)imageName type:(NSString *)type;


/**
 启动全景图
 */
- (void)startPanoramViewMotion;


/**
 关闭全景图
 */
- (void)stopPanoramViewMotion;

@end
//
//  PanoramaController.m
//  VRPanoramaKit
//
//  Created by 小发工作室 on 2017/9/21.
//  Copyright © 2017年 小发工作室. All rights reserved.
//

#import "PanoramaController.h"
#import "PanoramaUtil.h"

#define ES_PI  (3.14159265f)

#define MAX_VIEW_DEGREE 110.0f  //最大视角
#define MIN_VIEW_DEGREE 50.0f   //最小视角

#define FRAME_PER_SENCOND 60.0  //帧数

//#define SCREEN_WIDTH [UIScreen mainScreen].bounds.size.width
//#define SCREEN_HEIGHT [UIScreen mainScreen].bounds.size.height

@interface PanoramaController ()<GLKViewControllerDelegate,GLKViewDelegate>

// 相机的广角角度
@property (nonatomic, assign) CGFloat        overture;

// 索引数
@property (nonatomic, assign) GLsizei        numIndices;

// 顶点索引缓存指针
@property (nonatomic, assign) GLuint         vertexIndicesBuffer;

// 顶点缓存指针
@property (nonatomic, assign) GLuint         vertexBuffer;

// 纹理缓存指针
@property (nonatomic, assign) GLuint         vertexTexCoord;

// 着色器
@property (nonatomic, strong) GLKBaseEffect  *effect;

// 图片的纹理信息
@property (nonatomic, strong) GLKTextureInfo *textureInfo;

// 模型坐标系
@property (nonatomic, assign) GLKMatrix4     modelViewMatrix;

// 手势平移距离
@property (nonatomic, assign) CGFloat        panX;
@property (nonatomic, assign) CGFloat        panY;

//两指缩放大小
@property (nonatomic, assign) CGFloat        scale;

//是否双击
@property (nonatomic, assign) BOOL           isTapScale;

//是否根据陀螺仪旋转
@property (nonatomic, assign) BOOL           isMotion;

//测试按钮
@property (nonatomic, strong) UIButton       *startButton;
@property (nonatomic, strong) UIButton       *endButton;

@end

@implementation PanoramaController


- (CMMotionManager *)motionManager {
    if (_motionManager == nil) {
        
        _motionManager = [[CMMotionManager alloc] init];
        
        _motionManager.deviceMotionUpdateInterval = 1/FRAME_PER_SENCOND;
        _motionManager.showsDeviceMovementDisplay = YES;

    }
    return _motionManager;
}

- (instancetype)init {
    
    self = [super init];
    
    if (self) {
        
        [self createPanoramView];
    }
    return self;
}

- (instancetype)initWithImageName:(NSString *)imageName type:(NSString *)type{
    

    self = [super init];
    
    if (self) {

        self.imageName     = imageName;
        self.imageNameType = type;
        
        if (type.length == 0) {
            
            type = @"jpg";
        }
        

        
        [self createPanoramView];
        
        
        //实际开发中移除测试按钮
        [self startPanoramViewMotion];
        
     }
    return self;
}

- (void)startPanoramViewMotion{
    
    self.isMotion = YES;

    self.delegate                         = self;
    self.preferredFramesPerSecond         = FRAME_PER_SENCOND;

    [self setupOpenGL];

    
    [self startDeviceMotion];

}
#pragma -Private

- (void)createPanoramView{
    
    if (self.imageName == nil) {
        NSAssert(self.imageName.length != 0, @"image name is nil,please check image name of PanoramView");
        return;
    }
    
    EAGLContext *context                  = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES3];
    self.panoramaView                     = (GLKView *)self.view;
    self.panoramaView.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
    self.panoramaView.drawableDepthFormat = GLKViewDrawableDepthFormat24;

    self.panoramaView.context             = context;
    self.panoramaView.delegate            = self;
    [EAGLContext setCurrentContext:context];
    
    [self addGesture];

    [self startPanoramViewMotion];

    self.isMotion = NO;

    
}

#pragma mark set device Motion
- (void)startDeviceMotion {

    [self.motionManager startDeviceMotionUpdatesUsingReferenceFrame:CMAttitudeReferenceFrameXArbitraryCorrectedZVertical];
    
    
    _modelViewMatrix = GLKMatrix4Identity;
    
}

- (void)stopDeviceMotion {
    
    [self.motionManager stopDeviceMotionUpdates];
    [self.motionManager stopAccelerometerUpdates];
    
}

#pragma mark setup OpenGL

- (void)setupOpenGL {
    
    glEnable(GL_DEPTH_TEST);
    
    // 顶点
    GLfloat *vVertices  = NULL;

    // 纹理
    GLfloat *vTextCoord = NULL;

    // 索引
    GLuint *indices     = NULL;

    int numVertices     = 0;

    _numIndices         = esGenSphere(200, 1.0, &vVertices, &vTextCoord, &indices, &numVertices);
    
    
    // 创建索引buffer并将indices的数据放入
    glGenBuffers(1, &_vertexIndicesBuffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vertexIndicesBuffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices*sizeof(GLuint), indices, GL_STATIC_DRAW);
    
    // 创建顶点buffer并将vVertices中的数据放入
    glGenBuffers(1, &_vertexBuffer);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexBuffer);
    glBufferData(GL_ARRAY_BUFFER, numVertices*3*sizeof(GLfloat), vVertices, GL_STATIC_DRAW);
    
    //设置顶点属性,对顶点的位置,颜色,坐标进行赋值
    glEnableVertexAttribArray(GLKVertexAttribPosition);
    glVertexAttribPointer(GLKVertexAttribPosition, 3, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*3, NULL);
    
    // 创建纹理buffer并将vTextCoord数据放入
    glGenBuffers(1, &_vertexTexCoord);
    glBindBuffer(GL_ARRAY_BUFFER, _vertexTexCoord);
    glBufferData(GL_ARRAY_BUFFER, numVertices*2*sizeof(GLfloat), vTextCoord, GL_DYNAMIC_DRAW);
    
    //设置纹理属性,对纹理的位置,颜色,坐标进行赋值
    glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
    glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat)*2, NULL);
    
    NSString *filePath = [[NSBundle mainBundle]pathForResource:self.imageName ofType:self.imageNameType];
    
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:@(1),GLKTextureLoaderOriginBottomLeft, nil];
    
    GLKTextureInfo *textureInfo = [GLKTextureLoader textureWithContentsOfFile:filePath
                                                                      options:options
                                                                        error:nil];
    
    _effect                    = [[GLKBaseEffect alloc]init];
    _effect.texture2d0.enabled = GL_TRUE;
    _effect.texture2d0.name    = textureInfo.name;
}

#pragma mark Gesture

- (void)addGesture {
    
    /// 平移手势
    UIPanGestureRecognizer *pan =[[UIPanGestureRecognizer alloc] initWithTarget:self
                                                                         action:@selector(panGestture:)];
    
    /// 捏合手势
    UIPinchGestureRecognizer *pinch = [[UIPinchGestureRecognizer alloc] initWithTarget:self
                                                                                action:@selector(pinchGesture:)];
    
    UITapGestureRecognizer *tap = [[UITapGestureRecognizer alloc] initWithTarget:self
                                                                          action:@selector(tapGesture:)];
    
    tap.numberOfTouchesRequired = 1;
    tap.numberOfTapsRequired    = 2;
    
    [self.view addGestureRecognizer:pinch];
    [self.view addGestureRecognizer:pan];
    [self.view addGestureRecognizer:tap];

    _scale = 1.0;
    
}

- (void)panGestture:(UIPanGestureRecognizer *)sender {
    
    CGPoint point = [sender translationInView:self.view];
    _panX         += point.x;
    _panY         += point.y;
    
    //转换之后归零
    [sender setTranslation:CGPointZero inView:self.view];
    
    
}

- (void)pinchGesture:(UIPinchGestureRecognizer *)sender {
    
    _scale       *= sender.scale;
    sender.scale = 1.0;

}

- (void)tapGesture:(UITapGestureRecognizer *)sender {
    
    if (!_isTapScale) {
        
        _isTapScale = YES;
        
        _scale = 1.5;
    }
    else
    {
        _scale = 1.0;
        _isTapScale = NO;
    }
    

        
}


#pragma mark -GLKViewDelegate

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
    
    /**清除颜色缓冲区内容时候: 使用白色填充*/
    glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
    /**清除颜色缓冲区与深度缓冲区内容*/
    glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
    [_effect prepareToDraw];
//    glDrawElements(GL_TRIANGLES, _numIndices, GL_UNSIGNED_SHORT, 0);
    glDrawElements(GL_TRIANGLES, _numIndices, GL_UNSIGNED_INT,0);

}

static float xx = 0;
static float yy = 0;
static float zz = 0;
static float ww = 0;


#pragma mark GLKViewControllerDelegate

- (void)glkViewControllerUpdate:(GLKViewController *)controller {
    
    
    CGSize size    = self.view.bounds.size;
    float aspect   = fabs(size.width / size.height);
    
    CGFloat radius = [self rotateFromFocalLengh];
    
    /**GLKMatrix4MakePerspective 配置透视图
     第一个参数, 类似于相机的焦距, 比如10表示窄角度, 100表示广角 一般65-75;
     第二个参数: 表示时屏幕的纵横比
     第三个, 第四参数: 是为了实现透视效果, 近大远处小, 要确保模型位于远近平面之间
     */
    GLKMatrix4 projectionMatrix        = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(radius),
                                                                   aspect,
                                                                   0.1f,
                                                                   1);
    
    GLKQuaternion quaternion;

    
    if(self.isMotion){
        

        
        projectionMatrix                   = GLKMatrix4Scale(projectionMatrix, -1.0f, 1.0f, 1.0f);
        
        CMDeviceMotion *deviceMotion       = self.motionManager.deviceMotion;
        
        double w                           = deviceMotion.attitude.quaternion.w;
        double wx                          = deviceMotion.attitude.quaternion.x;
        double wy                          = deviceMotion.attitude.quaternion.y;
        double wz                          = deviceMotion.attitude.quaternion.z;
        
        quaternion = GLKQuaternionMake(-wx,  wy, wz, w);


        NSLog(@"%f + %f + %f + %f",xx,yy,zz,ww);
        NSLog(@"%f,%f,%f,%f",wx,wy,wz,w);

        
    }
    else{
    
        projectionMatrix = GLKMatrix4Scale(projectionMatrix, -1.0f, 1.0f, 1.0f);

        quaternion       = GLKQuaternionMake(0, 0.7, 0.7, 0);
        _panY            = 0;
        _panX            = 0;
        xx = 0;
        yy = 0;
        zz = 0;
        ww = 0;
    }
    


    GLKMatrix4 rotation                = GLKMatrix4MakeWithQuaternion(quaternion);
    

    //上下滑动,绕X轴旋转
    projectionMatrix                   = GLKMatrix4RotateX(projectionMatrix, -0.005 * _panY);
    projectionMatrix                   = GLKMatrix4Multiply(projectionMatrix, rotation);
    // 为了保证在水平放置手机的时候, 是从下往上看, 因此首先坐标系沿着x轴旋转90度
    projectionMatrix                   = GLKMatrix4RotateX(projectionMatrix, M_PI_2);
    
    
    _effect.transform.projectionMatrix = projectionMatrix;
    GLKMatrix4 modelViewMatrix         = GLKMatrix4Identity;
    //左右滑动绕Y轴旋转
    modelViewMatrix                    = GLKMatrix4RotateY(modelViewMatrix, 0.005 * _panX);
    _effect.transform.modelviewMatrix  = modelViewMatrix;
 
}

- (void)glkViewController:(GLKViewController *)controller willPause:(BOOL)pause{
    NSLog(@"pause:%d", pause);
}

- (void)drawPanoramView{
}

- (CGFloat)rotateFromFocalLengh{
    
    CGFloat radius = 100 / _scale;
    
    // radius不小于50, 不大于110;
    if (radius < MIN_VIEW_DEGREE) {
        
        radius = MIN_VIEW_DEGREE;
        _scale = 1 / (MIN_VIEW_DEGREE / 100);
        
    }
    if (radius > MAX_VIEW_DEGREE) {
        
        radius = MAX_VIEW_DEGREE;
        _scale = 1 / (MAX_VIEW_DEGREE / 100);
    }
    
    return radius;
}

- (void)dealloc{
    
}


@end

该篇文章是小发工作室所发,摘抄过来方便自己查阅

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,548评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,497评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 167,990评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,618评论 1 296
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,618评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,246评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,819评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,725评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,268评论 1 320
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,356评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,488评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,181评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,862评论 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,331评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,445评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,897评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,500评论 2 359

推荐阅读更多精彩内容

  • 我一直怀念那年,那个教室,那群可爱的人儿…… 总有那么一群人,给过你最多的感动,你们一起哭过,笑过,疯过,后来才知...
    槿誓阅读 119评论 0 0
  • 一切商业的原则皆是低买高卖。 那么买的是什么,卖的又是什么呢?通证经济体系下这种买卖会有什么变化? 1 商业模式的...
    BobiGo阅读 185评论 0 0
  • 从前。有一个女巫,她的容貌永远停留在22岁。她会七十二变,神通广大。她买来针和药,脱光孩子们的衣服,然后摇身一变,...
    一棵花白阅读 1,939评论 21 59
  • 今天是广州今年最冷的一天,虽然今年没过几年。最近跟人聊天也常常忘记已经到了2018年。最近的生活,普通、无味又总有...
    安灵子阅读 126评论 0 0