美文网首页
iOS-利用OpenGL加载VR(本地/网络)图片

iOS-利用OpenGL加载VR(本地/网络)图片

作者: 香蕉你个菠萝 | 来源:发表于2020-03-19 14:55 被阅读0次

    有个GLKit框架可以简化基于OpenGL或者OpenGL ES的应用开发。这个框架功能介绍可以参考以下链接:

    https://www.jianshu.com/p/e19dd7a9c74c

    1、新建一个View继承自GLKView

    DXPanoramaPhotoView.h

    #import <UIKit/UIKit.h>
    #import <GLKit/GLKit.h>
    #import <CoreMotion/CoreMotion.h>
    
    @interface DXPanoramaPhotoView :GLKView
    ///  传过来的VR全景图片
    @property (nonatomic,copy)NSString *photoURL;
    
    @end
    

    .m

    
    #import "DXPanoramaPhotoView.h"
    #import "Sphere.h"
    
    @interface DXPanoramaPhotoView ()<GLKViewDelegate>
    /// 相机广角角度
    @property (nonatomic,assign)CGFloat overture;
    /// 索引数
    @property (nonatomic,assign)int numIndices;
    /// 顶点索引缓存指针
    @property (nonatomic,assign)GLuint vertexIndicesBufferID;
    /// 顶点缓存指针
    @property (nonatomic,assign)GLuint vertexBufferID;
    /// 纹理缓存指针
    @property (nonatomic,assign)GLuint vertexTexCoordID;
    /// 着色器
    @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 sphereSliceNum;
    ///  球体半径
    @property (nonatomic,assign)CGFloat sphereRadius;
    @property (nonatomic,assign)CGFloat sphereScale;
    
    @property (nonatomic,strong)CMMotionManager *motionManager;
    @property (nonatomic,strong)UIPanGestureRecognizer *pan;
    
    @end
    
    @implementation DXPanoramaPhotoView
    
    -(CMMotionManager *)motionManager
    {
        if (!_motionManager) {
            _motionManager = [[CMMotionManager alloc]init];
            _motionManager.deviceMotionUpdateInterval = 1.0 / 60.0;//更新间隔
            _motionManager.showsDeviceMovementDisplay = YES;
        }
        return _motionManager;
    }
    
    -(UIPanGestureRecognizer *)pan
    {
        if (!_pan) {
            _pan = [[UIPanGestureRecognizer alloc]initWithTarget:self action:@selector(panActionDidClick:)];
        }
        return _pan;
    }
    
    -(instancetype)initWithFrame:(CGRect)frame
    {
        if (self = [super initWithFrame:frame]) {
            self.modelViewMatrix = GLKMatrix4Identity;
            self.sphereSliceNum = 200;
            self.sphereRadius = 1.0;
            self.sphereScale = 300;
            
            /// 初始化GLKView
            [self setupGLKView];
            /// 设置buffers
            [self setupBuffer];
            /// 检测屏幕位置(加速器与陀螺仪)
            [self startDeviceMotion];
            /// 添加拖拽手势
            [self addPanGestureRecognizer];
            
            [self addDisplayLink];
            
        }
        return self;
    }
    
    
    -(void)setPhotoURL:(NSString *)photoURL
    {
        _photoURL = photoURL;
        
        [self runningTexture:photoURL];
    }
    
    -(void)glkView:(GLKView *)view drawInRect:(CGRect)rect
    {
        // 清除缓冲区的内容
        glClearColor(0, 0, 0, 1);
        // 清除颜色缓冲区与深度缓冲区内容
        glClear((GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
        // 渲染着色器
        [_effect prepareToDraw];
    
        glDrawElements((GL_TRIANGLES), (_numIndices),(GL_UNSIGNED_SHORT), nil);
        
        [self update];
    
    }
    
    - (void)update
    {
        float aspect = fabs(self.bounds.size.width / self.bounds.size.height);
        GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(GLKMathDegreesToRadians(85.0), aspect, 0.1, 400.0);
        projectionMatrix = GLKMatrix4Scale(projectionMatrix, -1.0, 1.0, 1.0);
        
        if (_motionManager.deviceMotion != nil){
            float w = _motionManager.deviceMotion.attitude.quaternion.w;
            float x = _motionManager.deviceMotion.attitude.quaternion.x;
            float y = _motionManager.deviceMotion.attitude.quaternion.y;
            float z = _motionManager.deviceMotion.attitude.quaternion.z;
            
            projectionMatrix = GLKMatrix4RotateX(projectionMatrix, -(0.005 * _panY));
            
            GLKQuaternion quaternion = GLKQuaternionMake(-x, y, z, w);
            GLKMatrix4 rotation = GLKMatrix4MakeWithQuaternion(quaternion);
            projectionMatrix = GLKMatrix4Multiply(projectionMatrix, rotation);
            
            /// 为了保证在水平放置手机的时候, 是从下往上看, 因此首先坐标系沿着x轴旋转90度
            projectionMatrix = GLKMatrix4RotateX(projectionMatrix, -(M_PI_2));
            _effect.transform.projectionMatrix = projectionMatrix;
        
            GLKMatrix4 modelViewMatrix = GLKMatrix4Identity;
            modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix,(0.005 * _panX));
            _effect.transform.modelviewMatrix = modelViewMatrix;
        }
    }
    
    - (void)setupGLKView
    {
        /// 设置颜色格式和深度格式
        self.drawableColorFormat = GLKViewDrawableColorFormatRGBA8888;
        self.drawableDepthFormat = GLKViewDrawableDepthFormat24;
        self.delegate = self;
        self.context = [[EAGLContext alloc]initWithAPI:kEAGLRenderingAPIOpenGLES2];
        //将此“EAGLContext”实例设置为OpenGL的“当前激活”的“Context”
        [EAGLContext setCurrentContext:self.context];
        /// 注意: 激活深度检测,设置深度检测一定要放在设置上一句的下面, 要不然context还没有激活
        glEnable(GL_DEPTH_TEST);
        
    }
    
    - (void)setupBuffer
    {
        float *vertices = 0;// 顶点
        float *texCoord = 0;// 纹理
        uint16_t *indices  = 0;// 索引
        int32_t numVertices = 0;
    
        /// 编译C文件 获取顶点/纹理/索引
        self.numIndices = initSphere(_sphereSliceNum, _sphereRadius, &vertices, &texCoord, &indices,  &numVertices);
        
        
        /// 加载顶点索引数据
        glGenBuffers(1, &_vertexIndicesBufferID); // 申请内存
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _vertexIndicesBufferID);// 将命名的缓冲对象绑定到指定的类型上去
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, _numIndices * sizeof(GLushort),indices, GL_STATIC_DRAW);
        
        /// 加载顶点坐标数据
        glGenBuffers(1, &_vertexBufferID);
        glBindBuffer((GL_ARRAY_BUFFER), _vertexBufferID);
        glBufferData((GL_ARRAY_BUFFER), (numVertices) * 3 * sizeof(GLfloat), vertices, (GL_STATIC_DRAW));
        
        /// 激活顶点位置属性
        glEnableVertexAttribArray(GLKVertexAttribPosition);
    
        glVertexAttribPointer(GLKVertexAttribPosition, 3, (GL_FLOAT), GL_FALSE, sizeof(GLfloat) * 3, nil);
        
        // 纹理
        glGenBuffers(1, &_vertexTexCoordID);
        glBindBuffer((GL_ARRAY_BUFFER), _vertexTexCoordID);
        glBufferData((GL_ARRAY_BUFFER),(numVertices) * 2 * sizeof(GLfloat), texCoord, (GL_DYNAMIC_DRAW));
        glEnableVertexAttribArray(GLKVertexAttribTexCoord0);
        glVertexAttribPointer(GLKVertexAttribTexCoord0, 2, GL_FLOAT, GL_FALSE, sizeof(GLfloat) * 2, nil);
        free(vertices);
        free(texCoord);
        free(indices);
    
    }
    
    - (void)startDeviceMotion
    {
        /**设置初始坐标系, 并开始监控
         CMAttitudeReferenceFrameXArbitraryCorrectedZVertical: 描述的参考系默认设备平放(垂直于Z轴),在X轴上取任意值。实际上当你开始刚开始对设备进行motion更新的时候X轴就被固定了。不过这里还使用了罗盘来对陀螺仪的测量数据做了误差修正
         使用pull形式获取数据
         */
        [self.motionManager startDeviceMotionUpdates];
        
        _modelViewMatrix = GLKMatrix4Identity;
    
        
    }
    
    - (void)addPanGestureRecognizer
    {
        [self addGestureRecognizer:self.pan];
        
    }
    
    - (void)addDisplayLink
    {
        CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayAction)];
        
    
        [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSRunLoopCommonModes];
    }
    
    - (void)runningTexture:(NSString *)photoUrl
    {
        
        // 获取图片纹理信息
        NSDictionary * options = [NSDictionary dictionaryWithObjectsAndKeys:
                                  [NSNumber numberWithBool:YES],
                                  GLKTextureLoaderOriginBottomLeft,
                                  nil];
        //本地图片
    //    self.textureInfo = [GLKTextureLoader textureWithContentsOfFile:photoUrl options:options error:nil];
        
        //网络图片
        self.textureInfo = [GLKTextureLoader textureWithContentsOfURL:[NSURL URLWithString:photoUrl] options:options error:nil];
        
        _effect = [GLKBaseEffect new];
        _effect.texture2d0.enabled = GL_TRUE;
        _effect.texture2d0.name = _textureInfo.name;
    
    }
    
    #pragma mark - 监听方法
    - (void)panActionDidClick:(UIPanGestureRecognizer *)rec
    {
        CGPoint point = [rec translationInView:rec.view];
        _panX += point.x;
        _panY += point.y;
        // 变换完后设置0
        [rec setTranslation:CGPointZero inView:rec.view];
    }
    
    - (void)displayAction
    {
        [self display];
    }
    @end
    
    ****** Sphere类来自https://github.com/cleven1/VRPanorama

    2、加载网络VR

    //网络
        _dxPhotoView = [[DXPanoramaPhotoView alloc] initWithFrame:CGRectMake(0, 0, self.view.bounds.size.width, 400)];
        _dxPhotoView.photoURL = @"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1584505024908&di=226b499afd97abe97d10cc1fb92c4a69&imgtype=0&src=http%3A%2F%2Fpic121.huitu.com%2Fres%2F20190525%2F1075369_20190525154106080050_1.jpg";
        
        [self.view addSubview:_dxPhotoView];
    

    如果加载本地的,需要改以下方法:

    [GLKTextureLoader textureWithContentsOfFile:photoUrl options:options error:nil];
    

    GitHub上有个小伙伴写的类似的View,也是可以改写成加载网络VR图片。链接如下

    https://github.com/bestswifter/BSPanoramaView

    相关文章

      网友评论

          本文标题:iOS-利用OpenGL加载VR(本地/网络)图片

          本文链接:https://www.haomeiwen.com/subject/pwmkyhtx.html