学习opengl的时候,看别人做了一个全景视频。因此感兴趣,所以学习下。
原理
视频播放
在播放全景视频时,和普通视频一样,也是播放器从视频源中一帧一帧地取画面,但全景视频播放器会将取出来的画面帖在一个球体的表面:
比如视频中的一帧画面如下:
将其帖到一个球体表面后如下:
将画面帖到球体表面后,为什么人能够看到整个画面的各个方面呢?是因为观影点刚好在这个球体的中心,观众可以通过转动头部来控制观察的视线方向:
用opengl 绘制怎么展示这个效果呢?
1。我们知道,图像需要绘制在一个球的上面。所以,我们需要用opengl 绘制一个球体
2. 图像附着在球体上面,因此,我们需要将球体的表面纹理绘制成这张照片
3. 而我们应该在球体的中心观察。摄像机的位置位于球体的中心。
这里我们只是单纯的对一张图片做下全景解析。会解析一张图。那么也会解析视频了,因为视频也是有一张张图来的。
代码如下
#import "GLTestView.h"#import "OPenGLManger.h"#import "sphere.h"
enum{ ATTRIB_VERTEX,
ATTRIB_TEXCOORD,
NUM_ATTRIBUTES};
enum{ UNIFORM_TEXTURE,
UNIFORM_PROJECTION_MARTRIX,
UNIFORM_MODELVIEW_MARTRIX,
UNIFORM_ROTATE, NUM_UNIFORMS};
GLint uniforms[NUM_UNIFORMS];
@interface GLTestView(){
EAGLContext *_context;
UILabel* horizontalLabel;
float horizontalDegree;
UILabel* verticalLabel;
float verticalDegree;
}
@property (nonatomic ,strong) ShaderManager * shadermanager;
@property (nonatomic, strong) AGLKVertexAttribArrayBuffer * vertexBuffer;
@property (nonatomic ,strong) AGLKVertexAttribArrayBuffer * vertexTextureBuffer;
@property (nonatomic ,strong) FrameBufferManger * frameManager;
@property (nonatomic , strong) CADisplayLink *mDisplayLink;
@end
@implementation GLTestView
+ (Class)layerClass{ return [CAEAGLLayer class];}
- (instancetype)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.mDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(displayLinkCallback:)];
self.mDisplayLink.frameInterval = 2;//FPS=30
[[self mDisplayLink] addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[[self mDisplayLink] setPaused:NO];
[self setupView];
self.contentScaleFactor = [[UIScreen mainScreen] scale];
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = TRUE;
eaglLayer.drawableProperties = @{ kEAGLDrawablePropertyRetainedBacking :[NSNumber numberWithBool:NO], kEAGLDrawablePropertyColorFormat : kEAGLColorFormatRGBA8};
_context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
if (!_context || ![EAGLContext setCurrentContext:_context] || ![self loadShaders]) { return nil; }
[EAGLContext setCurrentContext:_context];
[self initVertex];
[self frameBuffer];
[self setShader];
[self initImage];
[self render];
}
return self;}
///初始化顶点-(void)initVertex{
glDisable(GL_DEPTH_TEST);
self.vertexBuffer = [[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:sizeof(GLfloat)*3 numberOfVertices:sphereNumVerts bytes:sphereVerts usage:GL_STATIC_DRAW];
self.vertexTextureBuffer=[[AGLKVertexAttribArrayBuffer alloc]initWithAttribStride:sizeof(GLfloat)*2 numberOfVertices:sphereNumVerts bytes:sphereTexCoords usage:GL_STATIC_DRAW];}
-(void)initImage{ //地球纹理 [self setupTexture:@"Earth512x256.jpg"];}
- (GLuint)setupTexture:(NSString *)fileName { // 1获取图片的CGImageRef CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage; if (!spriteImage) { NSLog(@"Failed to load image %@", fileName); exit(1); } // 2 读取图片的大小 size_t width = CGImageGetWidth(spriteImage); size_t height = CGImageGetHeight(spriteImage); GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte)); //rgba共4个byte CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4, CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast); // 3在CGContextRef上绘图 CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage); CGContextRelease(spriteContext); // 4绑定纹理到默认的纹理ID(这里只有一张图片,故而相当于默认于片元着色器里面的colorMap,如果有多张图不可以这么做) glBindTexture(GL_TEXTURE_2D, 0); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); float fw = width, fh = height; glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, fw, fh, 0, GL_RGBA, GL_UNSIGNED_BYTE, spriteData); glBindTexture(GL_TEXTURE_2D, 0); free(spriteData); return 0;}
-(void)frameBuffer{
self.frameManager = [[FrameBufferManger alloc]init]; self.frameManager.renderBufferStore = ^{ [_context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer]; };
[self.frameManager buildLayer];
}
- (BOOL)loadShaders{
self.shadermanager =[[ShaderManager alloc]init]; return [self.shadermanager CompileLinkSuccessShaderName:@"Shader" glBindAttribLocationBlock:^(GLuint program) { glBindAttribLocation(program, ATTRIB_VERTEX, "position"); } GetUniformLocationBlock:^(GLuint program) { uniforms[UNIFORM_TEXTURE] =glGetUniformLocation(program, "Sampler"); uniforms[UNIFORM_ROTATE] = glGetUniformLocation(program, "preferredRotation"); uniforms[UNIFORM_PROJECTION_MARTRIX] = glGetUniformLocation(program, "projectionMatrix"); uniforms[UNIFORM_MODELVIEW_MARTRIX] = glGetUniformLocation(program, "modelViewMatrix"); }];
}
-(void)setShader{ glUseProgram(self.shadermanager.program); glUniform1f(uniforms[UNIFORM_ROTATE], GLKMathDegreesToRadians(180)); GLKMatrix4 projectionMatrix = GLKMatrix4MakePerspective(90, CGRectGetWidth(self.bounds) * 1.0 / CGRectGetHeight(self.bounds), 0.01, 10); GLKMatrix4 modelViewMatrix = GLKMatrix4MakeLookAt(0, 0, 0, 1, 0, 0, 0, 1, 0); glUniform1i(uniforms[UNIFORM_TEXTURE], 0); glUniformMatrix4fv(uniforms[UNIFORM_PROJECTION_MARTRIX], 1, GL_FALSE, projectionMatrix.m); glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEW_MARTRIX], 1, GL_FALSE, modelViewMatrix.m);}-(void)render{ glClearColor(0.1f, 0.0f, 0.0f, 1.0f); glClear(GL_COLOR_BUFFER_BIT); glUseProgram(self.shadermanager.program); // 更新顶点数据 [self.vertexBuffer prepareToDrawWithAttrib:ATTRIB_VERTEX numberOfCoordinates:3 attribOffset:0 shouldEnable:YES]; [self.vertexTextureBuffer prepareToDrawWithAttrib:ATTRIB_TEXCOORD numberOfCoordinates:2 attribOffset:0 shouldEnable:YES]; [AGLKVertexAttribArrayBuffer drawPreparedArraysWithMode:GL_TRIANGLES startVertexIndex:0 numberOfVertices:sphereNumVerts]; [self.frameManager layerRender:^{ if ([EAGLContext currentContext] == _context) { [_context presentRenderbuffer:GL_RENDERBUFFER]; } }];}#define LY_ROTATE YES- (void)setupView{ horizontalLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 0, 200, 50)]; [self addSubview:horizontalLabel]; horizontalLabel.textColor = [UIColor redColor]; verticalLabel = [[UILabel alloc] initWithFrame:CGRectMake(100, 50, 200, 50)]; [self addSubview:verticalLabel]; verticalLabel.textColor = [UIColor redColor]; if (LY_ROTATE) { horizontalDegree = 0.0; verticalDegree = M_PI_2; horizontalLabel.text = [NSString stringWithFormat:@"绕X轴旋转角度为%.2f", GLKMathRadiansToDegrees(horizontalDegree)]; verticalLabel.text = [NSString stringWithFormat:@"绕Y轴旋转角度为%.2f", GLKMathRadiansToDegrees(verticalDegree)]; } else { horizontalDegree = M_PI_2; verticalDegree = 0.0; horizontalLabel.text = [NSString stringWithFormat:@"偏航角为%.2f", GLKMathRadiansToDegrees(horizontalDegree)]; verticalLabel.text = [NSString stringWithFormat:@"高度角为%.2f", GLKMathRadiansToDegrees(verticalDegree)]; }}- (void)roatateWithX:(float)x Y:(float)y { horizontalDegree -= x / 100; verticalDegree += y / 100; horizontalLabel.text = [NSString stringWithFormat:@"绕X轴旋转角度为%.2f", GLKMathRadiansToDegrees(horizontalDegree)]; verticalLabel.text = [NSString stringWithFormat:@"绕Y轴旋转角度为%.2f", GLKMathRadiansToDegrees(verticalDegree)]; GLKMatrix4 modelViewMatrix = GLKMatrix4Identity; modelViewMatrix = GLKMatrix4RotateX(modelViewMatrix, horizontalDegree); modelViewMatrix = GLKMatrix4RotateY(modelViewMatrix, verticalDegree); glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEW_MARTRIX], 1, GL_FALSE, modelViewMatrix.m);}- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch* touch = [touches anyObject];
CGPoint point = [touch locationInView:self];
CGPoint prePoint = [touch previousLocationInView:self];
if (LY_ROTATE) {
[self roatateWithX:point.y - prePoint.y Y:point.x - prePoint.x];
}
else {
[self changeModelViewWithHorizontal:point.x - prePoint.x Vertical:point.y - prePoint.y];
}
}
- (void)changeModelViewWithHorizontal:(float)h Vertical:(float)v {
horizontalDegree -= h / 100;
verticalDegree -= v / 100;
horizontalLabel.text = [NSString stringWithFormat:@"偏航角为%.2f", GLKMathRadiansToDegrees(horizontalDegree)];
verticalLabel.text = [NSString stringWithFormat:@"高度角为%.2f", GLKMathRadiansToDegrees(verticalDegree)];
GLKMatrix4 modelViewMatrix = GLKMatrix4MakeLookAt(0, 0, 0,
sin(horizontalDegree) * cos(verticalDegree),
sin(horizontalDegree) * sin(verticalDegree),
cos(horizontalDegree),
0, 1, 0);
glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEW_MARTRIX], 1, GL_FALSE, modelViewMatrix.m);
}
- (void)displayLinkCallback:(CADisplayLink *)sender
{
[self render];
}
@end
借鉴文章