AR/VR -iOSSceneKitUnity技术VR/AR分享

SceneKIt+ AVFoundation 打造VR播放器(2

2017-06-23  本文已影响276人  hhjdk

下面是我写的播放器

SceneKIt+ AVFoundation 打造VR播放器(1)
支持VR,全景,视频缩放,本地,网络视频播放,实时获取视频帧,获取播放时间,获取缓存时间,播放,暂停

2017-06-22 17_47_06.gif

要想完成一个Vr播放器,需要完成两个功能

1、写一个可以实时获取视频帧的播放器
2、写一个可以渲染每一帧图片为全景图片的view

SCN3DPlayerView 图片渲染

用于渲染每一帧图片为全景图片
使用的是 <UIKit/UIKit.h><SceneKit/SceneKit.h> <GLKit/GLKit.h>

下面是SCN3DPlayerView的一些方法

//
//  SCN3DPlayerView.h
//  SCN3DPlayer
//
//  Created by 俞涛涛 on 16/11/11.
//  Copyright © 2016年 俞涛涛. All rights reserved.
//

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


/////////////////////////////////////////////////////////////////////////////////////////////////////////

/**
 触摸,重力,触摸重力

 - SCN3DInteractive_Touch: 触摸
 - SCN3DInteractive_Motion: 重力
 - SCN3DInteractive_MotionAndTouch: 触摸和重力
 */
typedef NS_ENUM(NSInteger, SCN3DInteractive_) {
    SCN3DInteractive_Touch,
    SCN3DInteractive_Motion,
    SCN3DInteractive_MotionAndTouch,
};

/**
 形状模式枚举

 - SCN3DDisplayMode_Plane_Normal: 普通模式
 - SCN3DDisplayMode_Plane_Slide: 平面180模式
 - SCN3DDisplayMode_Tube: 圆柱模式
 - SCN3DDisplayMode_Sphere: 球模式
 - SCN3DDisplayMode_VR360: 全景模式
 - SCN3DDisplayMode_VRGlass: VR双屏模式
 */
typedef NS_ENUM(NSUInteger, SCN3DDisplayMode_) {
    SCN3DDisplayMode_Plane_Normal = 0,
    SCN3DDisplayMode_Plane_Slide,
    SCN3DDisplayMode_Tube,
    SCN3DDisplayMode_Sphere,
    SCN3DDisplayMode_VR360,
    SCN3DDisplayMode_VRGlass,
};

/////////////////////////////////////////////////////////////////////////////////////////////////////////

@interface SCN3DPlayerView : UIView

@property (nonatomic, strong) SCNScene *scene;
@property (nonatomic, strong) SCNView  *scViewLeft;
@property (nonatomic, strong) SCNView  *scViewRight;
@property (nonatomic, strong) SCNNode  *shapeNode;
@property (nonatomic, strong) SCNNode  *cameraNode;

//_________________________________________________________________________________________________

/**
 设置使用重力,还是手指触摸,或者两者都持

 @param interactive SCN3DInteractive_ 枚举参数
 */
- (void)setInteractiveMode:(SCN3DInteractive_)interactive;

/**
 设置sceneKit 渲染模式,有Vr ,全景,等等模式

 @param displayMode SCN3DDisplayMode_ 枚举参数
 */
- (void)setVideoDisplayMode:(SCN3DDisplayMode_)displayMode;

/**
 是否,水平启用,垂直启用

 @param horEnabled Yes or no
 @param verEnabled yes or no
 */
- (void)setHorizontalEnabled:(BOOL)horEnabled verticalEnabled:(BOOL)verEnabled;

//是否开启重力传感器
- (void)setGSensorMotionEnabled:(BOOL)GSensorEnabled;
//是否开启缩放功能
- (void)setPinchScaleEnabled:(BOOL)pinchEnabled;
//设置缩放范围
- (void)setMinScale:(float)minScale maxScale:(float)maxScale;
//设置旋转范围
- (void)setVerticalMinRotate:(float)minRotate maxRotate:(float)maxRotate;
//设置纹理坐标平移
- (void)setTextureOffsetX:(float)x offsetY:(float)y;
//设置纹理坐标缩放
- (void)setTextureScaleX:(float)x ScaleY:(float)y;

//_________________________________________________________________________________________________
//设置宽高比
- (void)setVideoAspectRatio:(float)aspectRatio;
//设置当前横屏或者竖屏
- (void)setCurrentOrientation:(UIInterfaceOrientation)orientation;
//设置当前缩放比例
- (void)setCurrentScale:(float)curScale;
//设置当前旋转角度
- (void)setCurrentRotateX:(float)rotateX rotateY:(float)rotateY;

//_________________________________________________________________________________________________
//设置帧视频图像
- (void)setFramesPerVideoImage:(UIImage *)image;

@end


下面是一些重要的方法

下面是一些枚举的定义

/**
 触摸,重力,触摸重力

 - SCN3DInteractive_Touch: 触摸
 - SCN3DInteractive_Motion: 重力
 - SCN3DInteractive_MotionAndTouch: 触摸和重力
 */
typedef NS_ENUM(NSInteger, SCN3DInteractive_) {
    SCN3DInteractive_Touch,
    SCN3DInteractive_Motion,
    SCN3DInteractive_MotionAndTouch,
};

/**
 形状模式枚举

 - SCN3DDisplayMode_Plane_Normal: 普通模式
 - SCN3DDisplayMode_Plane_Slide: 平面180模式
 - SCN3DDisplayMode_Tube: 圆柱模式
 - SCN3DDisplayMode_Sphere: 球模式
 - SCN3DDisplayMode_VR360: 全景模式
 - SCN3DDisplayMode_VRGlass: VR双屏模式
 */
typedef NS_ENUM(NSUInteger, SCN3DDisplayMode_) {
    SCN3DDisplayMode_Plane_Normal = 0,
    SCN3DDisplayMode_Plane_Slide,
    SCN3DDisplayMode_Tube,
    SCN3DDisplayMode_Sphere,
    SCN3DDisplayMode_VR360,
    SCN3DDisplayMode_VRGlass,
};

初始化SCNScene,SCNView

- (void)initScene {
    self.scene  = [SCNScene scene];
    self.scViewLeft = [[SCNView alloc] initWithFrame:CGRectMake(0, 0, self.frame.size.width, self.frame.size.height) options:nil];
    self.scViewLeft.backgroundColor = [UIColor blackColor];
    self.scViewLeft.scene = self.scene;
    self.scViewLeft.antialiasingMode = SCNAntialiasingModeMultisampling4X;
    [self addSubview:self.scViewLeft];
    
    self.cameraNode = [SCNNode node];
    self.cameraNode.camera = [SCNCamera camera];
    [self.scene.rootNode addChildNode:self.cameraNode];
    
    UIPinchGestureRecognizer *pinchGestureRecognizer = [[UIPinchGestureRecognizer alloc] initWithTarget:self action:@selector(handlePinchScale:)];
    [self addGestureRecognizer:pinchGestureRecognizer];
}

根据不同的模式,设置相应的参数

- (void)initSceneNodeWithMode:(SCN3DDisplayMode_)displayMode {
    [self.shapeNode   removeFromParentNode];
    [self.scViewRight removeFromSuperview];
    [self initParameterSetting];
    switch (displayMode) {
        case SCN3DDisplayMode_Plane_Normal: {
            float width  = 2.0;
            float height = width / self.videoAspRatio;
            self.shapeNode = [SCNNode nodeWithGeometry:[SCNPlane planeWithWidth:width height:height]];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = YES;
        }
            break;
        case SCN3DDisplayMode_Plane_Slide: {
            float width  = 2.0;
            float height = width / self.videoAspRatio;
            self.shapeNode = [SCNNode nodeWithGeometry:[SCNPlane planeWithWidth:width height:height]];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeRepeat;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeRepeat;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = YES;
            
        }
            break;
        case SCN3DDisplayMode_Tube: {
            SCNTube *tube = [SCNTube tubeWithInnerRadius:1.0 outerRadius:1.0 height:1.0];
            tube.radialSegmentCount = 96;
            self.shapeNode = [SCNNode nodeWithGeometry:tube];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;
            
            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = YES;
        }
            break;
        case SCN3DDisplayMode_Sphere: {
            
            SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
            sphere.segmentCount = 96;
            self.shapeNode = [SCNNode nodeWithGeometry:sphere];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullBack;// 设置剔除内表面
            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = YES;
        }
            break;
        case SCN3DDisplayMode_VR360: {
  
            SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
            sphere.segmentCount = 96;
            self.shapeNode = [SCNNode nodeWithGeometry:sphere];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullFront;
            self.shapeNode.geometry.firstMaterial.doubleSided = false; // 设置只渲染一个表面
            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = NO;
            
//            SCNMatrix4 contentsTransform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
////            SCNMatrix4 contentsTransform2 = self.shapeNode.transform;
//       
//            contentsTransform =SCNMatrix4Rotate(contentsTransform, 0, M_PI, 0, 0);
//            self.shapeNode.transform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
     
            
 
        }
            break;
        case SCN3DDisplayMode_VRGlass: {
            SCNSphere *sphere = [SCNSphere sphereWithRadius:1.0];
            sphere.segmentCount = 96;
            self.shapeNode = [SCNNode nodeWithGeometry:sphere];
            self.shapeNode.geometry.firstMaterial.diffuse.wrapT = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.diffuse.wrapS = SCNWrapModeClamp;
            self.shapeNode.geometry.firstMaterial.cullMode = SCNCullFront;// 设置剔除外表面
            self.shapeNode.geometry.firstMaterial.doubleSided = false; // 设置只渲染一个表面
//            self.cameraNode.position = SCNVector3Make(0, 0, 1.5);
            self.cameraNode.camera.usesOrthographicProjection = NO;
            
            self.scViewLeft.frame  = CGRectMake(0, 0, self.frame.size.width / 2, self.frame.size.height);
            self.scViewRight = [[SCNView alloc] initWithFrame:CGRectMake(self.frame.size.width / 2, 0, self.frame.size.width / 2, self.frame.size.height) options:nil];
            self.scViewRight.backgroundColor = [UIColor blackColor];
            self.scViewRight.scene = self.scene;
            self.scViewRight.antialiasingMode = SCNAntialiasingModeMultisampling4X;
            [self addSubview:self.scViewRight];
        }
            break;
            
        default:
            break;
    }
    
    self.cameraNode.camera.zNear = 0.01f;
    self.cameraNode.camera.zFar  = 100.0f;
    self.shapeNode.castsShadow = NO;
    self.shapeNode.position = SCNVector3Make(0, 0, 0);
    [self.scene.rootNode addChildNode:self.shapeNode];
    
    self.modelMatrix = self.shapeNode.transform;
    [self setCurrentScale:1.0];
    [self setFramesPerVideoImage:nil];
}

手指滑动,捏合的相关处理

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
    UITouch *touch = [touches anyObject];
    CGPoint currLoc = [touch locationInView:self];
    CGPoint lastLoc = [touch previousLocationInView:self];
    CGPoint moveDiff = CGPointMake(currLoc.x - lastLoc.x, currLoc.y - lastLoc.y);
    
    float rotX = -1 * GLKMathDegreesToRadians(moveDiff.y / 5.0);
    float rotY = -1 * GLKMathDegreesToRadians(moveDiff.x / 5.0);
    
    rotX = self.verticalEnabled   ? rotX : 0;
    rotY = self.horizontalEnabled ? rotY : 0;
    [self changeNodeTransformWithRotateX:rotX / self.curScale rotateY:rotY / self.curScale];
}

- (void)changeNodeTransformWithRotateX:(float)rotX rotateY:(float)rotY {
    switch (self.displayMode) {
        case SCN3DDisplayMode_Plane_Normal: {}
            break;
        case SCN3DDisplayMode_Plane_Slide: {
            SCNMatrix4 contentsTransform = self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform;
            contentsTransform = SCNMatrix4Translate(contentsTransform, rotY, 0, 0);
            contentsTransform = SCNMatrix4Translate(contentsTransform, 0, rotX, 0);
            self.shapeNode.geometry.firstMaterial.diffuse.contentsTransform = contentsTransform;
        }
            break;
        case SCN3DDisplayMode_Tube: {
            self.rotateX += rotX;
            self.rotateY += rotY;
            float minRotate = self.minRotateX / self.curScale;
            float maxRotate = self.maxRotateX / self.curScale;
            
            minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
            maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
            self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
            self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
            
            SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
            modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
            self.shapeNode.transform = modelViewMatrix;
        }
            break;
        case SCN3DDisplayMode_Sphere: {
            self.rotateX += rotX;
            self.rotateY += rotY;
            float minRotate = self.minRotateX / self.curScale;
            float maxRotate = self.maxRotateX / self.curScale;
            
            minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
            maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
            self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
            self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
            
            SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
            modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
            self.shapeNode.transform = modelViewMatrix;
        }
            break;
            
        default: {
            self.rotateX += -rotX;
            self.rotateY += -rotY;
            float minRotate = self.minRotateX / self.curScale;
            float maxRotate = self.maxRotateX / self.curScale;
            
            minRotate = minRotate < (- M_PI / 2) ? (- M_PI / 2) : minRotate;
            maxRotate = maxRotate > (+ M_PI / 2) ? (+ M_PI / 2) : maxRotate;
            self.rotateX = self.rotateX < minRotate ? minRotate : self.rotateX;
            self.rotateX = self.rotateX > maxRotate ? maxRotate : self.rotateX;
//            NSLog(@"self.rotateX = %f, %f", self.rotateX, GLKMathRadiansToDegrees(self.rotateX));
            
            SCNMatrix4 modelViewMatrix = SCNMatrix4Identity;
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateY, 0, 1, 0);
            modelViewMatrix = SCNMatrix4Rotate(modelViewMatrix, -self.rotateX, 1, 0, 0);
            modelViewMatrix = SCNMatrix4Scale(modelViewMatrix, self.curScale, self.curScale, self.curScale);
            self.shapeNode.transform = modelViewMatrix;
        }
            break;
    }
}

- (void)handlePinchScale:(UIPinchGestureRecognizer *)paramSender {
    if (!self.pinchEnabled) return;
    if (paramSender.state != UIGestureRecognizerStateEnded && paramSender.state != UIGestureRecognizerStateFailed) {
        if (paramSender.scale != NAN && paramSender.scale != 0.0) {
            
            float scale = (paramSender.scale - 1) * 0.50;
            self.curScale = scale + self.prevScale;
            
            if (self.curScale < self.minScale) {
                self.curScale = self.minScale;
            }
            else if (self.curScale > self.maxScale) {
                self.curScale = self.maxScale;
            }
            
            [self setCurrentScale:self.curScale];
        }
    } else if(paramSender.state == UIGestureRecognizerStateEnded) {
        self.prevScale = self.curScale;
    }
}

重力感应相关设置

- (void)startGSENSORMotion {
    float gFPS = 30.0f;
    if (self.displayMode == SCN3DDisplayMode_VRGlass) {
        gFPS = 120.0f;
    }
    self.motionManager = [[CMMotionManager alloc] init];
    self.motionManager.deviceMotionUpdateInterval = 1.0f / gFPS;
    self.motionManager.gyroUpdateInterval = 1.0f / gFPS;
    self.motionManager.showsDeviceMovementDisplay = YES;
    self.rotateX = 0.0f;
    self.rotateY = 0.0f;
    
    NSOperationQueue* motionQueue = [[NSOperationQueue alloc] init];
    [self.motionManager startDeviceMotionUpdatesToQueue:motionQueue withHandler:^(CMDeviceMotion * _Nullable motion, NSError * _Nullable error) {
        float  damping = 30.0;
        double rotateX = -motion.rotationRate.y / damping;  // X 轴旋转
        double rotateY = -motion.rotationRate.x / damping;  // Y 轴旋转
        
        switch (self.orientation) {
            case UIDeviceOrientationLandscapeRight:
                rotateX = +motion.rotationRate.x / damping;
                rotateY = -motion.rotationRate.y / damping;
                break;
            case UIDeviceOrientationLandscapeLeft:
                rotateX = -motion.rotationRate.x / damping;
                rotateY = +motion.rotationRate.y / damping;
                break;
            case UIDeviceOrientationPortrait:
            default:
                break;
        }
        [self changeNodeTransformWithRotateX:(rotateY / 2) / self.curScale rotateY:rotateX / self.curScale];
    }];
}

- (void)stopGSENSORMotion {
    [self.motionManager stopDeviceMotionUpdates];
    self.motionManager = nil;
}

还有一些相关设置,这里就不介绍了,大家可以自己看,在.h文件里面都有相关注释,如有错误,请指正,谢谢
源代码
如果喜欢的话,就star一下

上一篇下一篇

猜你喜欢

热点阅读