IOS个人开发平时生活和工作中的iOS直播

OpenGL ES_手把手教你打造VR全景播放器

2016-08-27  本文已影响9482人  酷走天涯

OpenGL ES _ 入门_01
OpenGL ES _ 入门_02
OpenGL ES _ 入门_03
OpenGL ES _ 入门_04
OpenGL ES _ 入门_05
OpenGL ES _ 入门练习_01
OpenGL ES _ 入门练习_02
OpenGL ES _ 入门练习_03
OpenGL ES _ 入门练习_04
OpenGL ES _ 入门练习_05
OpenGL ES _ 入门练习_06
OpenGL ES _ 着色器 _ 介绍
OpenGL ES _ 着色器 _ 程序
OpenGL ES _ 着色器 _ 语法
OpenGL ES_着色器_纹理图像
OpenGL ES_着色器_预处理
OpenGL ES_着色器_顶点着色器详解
OpenGL ES_着色器_片断着色器详解
OpenGL ES_着色器_实战01
OpenGL ES_着色器_实战02
OpenGL ES_着色器_实战03

让学习成为一种习惯
实战2中,详细介绍了多屏显示的原理和实现过程,今天我们继续我们的OpenGL 旅程!技术再牛逼也要学习! 学习是一件开心的额事情

学习目标

打造全景视频,以及VR 眼镜专用的双屏显示框架!

你应该知道的

网络截图

准备工作

找一个全景视频,添加到项目中去。

实现了那些功能

核心代码讲解

如果你想要和我一样,能够从零开始把代码敲出来,请确保自己有OpenGL ES 2.0 的基础知识 和 GLSL 的简单基本知识,如果你不具备这方面的知识,没关系,我已经写好了OpenGL学习教程GLSL教程,请移步开始学习。下面开始我们的内容讲解.

<p>工程中的两个文件 XJVRPlayerViewController.h和XJVRPlayerController.m主要负责视频数据采集,界面布局在XJVRPlayerViewController中可以更改,主要使用AVFoundation框架这部分内容今天咱不讲解,后面我会写关于视频采集的教程</p>

以上函数的具体用法,在之前的教程中都讲过,这里就不赘述了。

b.片段着色器

  precision mediump float;//设置float精度
  varying  vec2 texCoordVarying;
  uniform sampler2D sam2DY;  // 纹理采样器Y
  uniform sampler2D sam2DUV;// 纹理采样器UV
  void main(){
  mediump vec3 yuv;
  lowp vec3 rob;
  // YUV  转RGB 的转换矩阵 
  mediump mat3 convert = mat3(1.164,  1.164, 1.164,
                            0.0, -0.213, 2.112,
                            1.793, -0.533,   0.0);
  yuv.x = texture2D(sam2DY,texCoordVarying).r - (16.0/255.0);
  yuv.yz = texture2D(sam2DUV,texCoordVarying).rg - vec2(0.5, 0.5);
  rgb = convert*yuv;
  gl_FragColor = vec4(rgb,1);
  }

如果想要了解更多关于着色器语言的知识,请猛戳我

// 上面的OSShaderManager 这个类,我把着色器程序编译链接的一些方法简单的封装了一下,具体的方向看下面

    /**
     *  编译shader程序
     *  @param shader shader名称
     *  @param type   shader 类型
     *  @param URL    shader 本地路径
     *  @return 是否编译成功
   */
    - (BOOL)compileShader:(GLuint *)shader type:(GLenum)type URL:(NSURL *)URL;
   /**
   *  连接程序
   *  @return 连接程序是否成功
   */
  - (BOOL)linkProgram;
  /**
   *  验证程序是否成功
   *  @param prog 程序标示
   *  @return 返回是否成功标志
   */
  - (BOOL)validateProgram;
  /**
   *  绑定着色器的属性
   *  @param index 属性在shader 程序的索引位置
   *  @param name  属性名称
   */
  - (void)bindAttribLocation:(GLuint)index andAttribName:  (GLchar*)name;
  /**
   *  删除shader
   */
  - (void)deleteShader:(GLuint*)shader;
  /**
   *  获取属性值索引位置
   *  @param name 属性名称
   *  @return 返回索引位置
   */
  - (GLint)getUniformLocation:(const GLchar*) name;
  /**
   * 释放, 删除shader
   *  @param shader 着色器名称
   */
  -(void)detachAndDeleteShader:(GLuint*)shader;
  /**
   *  使用程序
   */
  -(void)useProgram;

方法的具体实现请阅读工程文件

在这里我有必要提醒你,这两个方法,一定要放在着色器程序链接成功之后,不然你调用这个两个方法,没有效果。

我们从上面的日志输出找到了下面的东西

    <CVPixelBuffer 0x7fa27962c9c0 width=2048     height=1024 pixelFormat=420v iosurface=0x0 planes=2>
    <Plane 0 width=2048 height=1024 bytesPerRow=2048>
    <Plane 1 width=1024 height=512 bytesPerRow=2048>

我们能得到的信息是:
像素格式: 420v
数据通道: 2 个
通道1: width=2048 height=1024
通道2: width=1024 height=512

从上面信息可以得出我们数据的排列方式为YY....YY....UV.....UV,
2048\1024 个Y 数据,1024\512 从 bytesPerRow 可以看出每个Y、U、V 各占一个字节.
接下来就是如何将数据加载到我们的纹理缓冲区去了

CVReturn CVOpenGLESTextureCacheCreateTextureFromImage(
CFAllocatorRef CV_NULLABLE allocator,
CVOpenGLESTextureCacheRef CV_NONNULL textureCache,
CVImageBufferRef CV_NONNULL sourceImage,
CFDictionaryRef CV_NULLABLE textureAttributes,
GLenum target,
GLint internalFormat,
GLsizei width,
GLsizei height,
GLenum format,
GLenum type,
size_t planeIndex,
CV_RETURNS_RETAINED_PARAMETER CVOpenGLESTextureRef CV_NULLABLE * CV_NONNULL textureOut ) 

这个函数作用是: 通过CVImageBufferRef 创建一个纹理对象
allocator : 写默认值就可以了 kCFAllocatorDefault
textureCache:我们需要手动创建一个纹理缓冲对象,
sourceImage:传我们的CVImageBufferRef 数据
textureAttributes:纹理属性,可以为NULL
target:纹理的类型(GL_TEXTURE_2D 和GL_RENDERBUFFER)
internalFormat:数据格式,就是这个数据步伐的意思
width:纹理的高度
height : 纹理的长度
format: 像素数据的格式
type: 数据类型
planeIndex: 通道索引

接下来看我们的代码:

 // 启用纹理缓冲区0
glActiveTexture(GL_TEXTURE0);
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                   _videoTextureCache,
                                                   pixelBuffer,
                                                   NULL,
                                                   GL_TEXTURE_2D,
                                                   GL_RED_EXT,
                                                   width,
                                                   height,
                                                   GL_RED_EXT,
                                                   GL_UNSIGNED_BYTE,
                                                   0,
                                                   &_lumaTexture);
if (err) {
    NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
    
}

glBindTexture(CVOpenGLESTextureGetTarget(_lumaTexture), CVOpenGLESTextureGetName(_lumaTexture));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

// UV-plane.
// 启用纹理缓冲区1
glActiveTexture(GL_TEXTURE1);
err = CVOpenGLESTextureCacheCreateTextureFromImage(kCFAllocatorDefault,
                                                   _videoTextureCache,
                                                   pixelBuffer,
                                                   NULL,
                                                   GL_TEXTURE_2D,
                                                   GL_RG_EXT,
                                                   width /2,
                                                   height /2,
                                                   GL_RG_EXT,
                                                   GL_UNSIGNED_BYTE,
                                                   1,
                                                   &_chromaTexture);
if (err) {
    NSLog(@"Error at CVOpenGLESTextureCacheCreateTextureFromImage %d", err);
}


glBindTexture(CVOpenGLESTextureGetTarget(_chromaTexture), CVOpenGLESTextureGetName(_chromaTexture));
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

GL_RED_EXT 代表 1位数据 GL_RG_EXT 代表2位数据 。UV 就是两位数据 所以我们选择GL_RG_EXT。

刚才说了,参数中需要一个纹理缓冲TextureCacha,接下来我们就自己创建一个.

  CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, self.eagContext, NULL, &_videoTextureCache);

以上基本的工作都做完了,接下来,我们就只剩下显示了

到这里,视频已经可以显示了。

操作矩阵这里,暂时不想讲,后面我会专门来讲矩阵变换和角度传感器的使用,因为这两个东西在游戏和VR,还是AR的世界,都太重要了。今天先说的这里,给几张展示图欣赏一下。

全景模式下
普通视频 普通视频双屏展示 全景视频VR模式 全景.gif

需要代码在这里这里

全景播放器-实现方案2

使用SceneKit 也可以实现全景播放器,需要了解的朋友请查看这里

加群了
上一篇 下一篇

猜你喜欢

热点阅读