// PanoramaUtil.c
// VRPanoramaKit
//
// Created by 小发工作室 on 2017/9/22.
// Copyright © 2017年 小发工作室. All rights reserved.
//
#include "PanoramaUtil.h"
#include
#include
#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
/**
根据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
#import
#import
@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 ()
// 相机的广角角度
@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
该篇文章是小发工作室所发,摘抄过来方便自己查阅