iOS VR視頻開發

Demo的Github倉庫地址:https://github.com/filelife/FLVRPlayer html

Demo效果圖.gif

爲了讓你們更好的閱讀Demo代碼,如下簡單介紹相關知識及實現思路。

Step 0.

0.1 渲染VR場景視頻的大體思路:

image
逐幀讀取VR視頻的逐幀數據,以後經過着色器渲染到球體模型上,獲取球體中心的做爲視覺取鏡點,經過重力感應接受頭部移動向量方向,更新手機的映射取鏡點。

image

Step 1.

1.1 獲取模型

首先,須要建立一個球體。在OpenGL中建立模型,須要頂點和紋理座標,經過3D MAX等工具製做的obj模型在iOS中識別不了,因此須要進行轉換爲OpenGL使用的頂點數組。

有位大神寫了一個模型轉換爲頂點數組的工具:https://github.com/HBehrens/obj2opengl

產物是一套球體模型的頂點數據+頂點索引;

1.2 模型數據描述

爲了更好理解頂點數據和定點索引的旨意,咱們來分析一個正方形模型的建模數據。

GLfloat squareVertexData[] =
{
    0.5, -0.5, 0.0f,    1.0f, 0.0f, //右下
    -0.5, 0.5, 0.0f,    0.0f, 1.0f, //左上
    -0.5, -0.5, 0.0f,   0.0f, 0.0f, //左下
    0.5, 0.5, -0.0f,    1.0f, 1.0f, //右上
};
複製代碼
這些點咱們本意想要描述的是一個正方形,可是因爲手機屏幕座標系內XY軸比例並不同,因此最終會產出如下形狀:

image

對於初學者還有個容易忽視的技術點,那就是 ==在OpenGL ES只能繪製三角形==,不能繪製多邊形,可是在OpenGL中確實能夠直接繪製多邊形.
//頂點索引
GLuint indices[] ={ 
  0, 1, 2,
  1, 3, 0
};
複製代碼

image

咱們還能夠經過如下頂點+索引,繪製一個3D的正方體。
//頂點數據,前三個是頂點座標, 中間三個是頂點顏色,    最後兩個是紋理座標
GLfloat attrArr[] =
{
    -0.5f, 0.5f, 0.0f,       0.0f, 0.0f, 0.5f,       0.0f, 1.0f,//左上
    0.5f, 0.5f, 0.0f,        0.0f, 0.5f, 0.0f,       1.0f, 1.0f,//右上
    -0.5f, -0.5f, 0.0f,      0.5f, 0.0f, 1.0f,       0.0f, 0.0f,//左下
    0.5f, -0.5f, 0.0f,       0.0f, 0.0f, 0.5f,       1.0f, 0.0f,//右下
    -0.5f, 0.5f, 1.0f,       0.0f, 0.0f, 0.5f,       0.0f, 1.0f,//後方左上
    0.5f, 0.5f, 1.0f,        0.0f, 0.5f, 0.0f,       1.0f, 1.0f,//後方右上
    -0.5f, -0.5f, 1.0f,      0.5f, 0.0f, 1.0f,       0.0f, 0.0f,//後方左下
    0.5f, -0.5f, 1.0f,       0.0f, 0.0f, 0.5f,       1.0f, 0.0f,//後方右下
};

複製代碼
//頂點索引
    GLuint indices[] =
    {
        0, 3, 2,
        0, 1, 3,
        0, 4, 1,
        5, 4, 1,
        1, 5, 3,
        7, 5, 3,
        2, 6, 3,
        6, 7, 3,
        0, 6, 2,
        0, 4, 6,
        4, 5, 7,
        4, 6, 7,
    };

複製代碼

Step 2.

2.1渲染基礎 - 緩存

CPU的運算能力(每秒億次級別)遠高於其對內存的讀寫能力(每秒百萬次級別),爲了優化效率,避免產生數據飢餓,在OpenGL ES中,可讓程序從CPU的內存中複製數據到GPU控制的連續RAM緩存中,GPU在取得一個緩存數據以後,便會獨佔該緩存,從而儘量有效的讀寫內存數據,緩存數據包含:幾何數據、顏色、燈光效果等等。

緩存的生命週期:

2.1.1. 生成(Generate):請求OpenGLES爲GPU控制的緩存生成一個獨一無二的標誌符。

對應函數:glGenBuffers()

2.1.2. 綁定(Bind)告訴OpenGLES爲接下來的運算使用一個緩存。

對應函數:glBindBuffer()

2.1.3. 啓用(Enable)或者禁止(Disable):告訴OpenGLES再接下來的渲染中,是否使用緩存數據。

對應函數:glEnableVertexAttribArray()

2.1.4. 設置指針(Set Points):告訴OpenGLES在緩存中的數據存儲的類型和所須要訪問的數據的內存指針偏移。

對應函數:geVertexAttribPointer()

2.1.5. 繪圖(Draw):告訴OpenGLES使用當前綁定並啓用的緩存中的數據渲染整個場景或者某個場景的一部分。

對應函數:glDrawArrays()

2.1.6. 刪除(Delete):告訴OpenGLES刪除之前生成的緩存並釋放相關的資源。

對應函數:glDeleteBuffers()

2.2 一個3D場景的幾何數據

2.2.1. 座標系(笛卡爾座標系):OpeGLES座標系沒有單位,點{1,0,0}到點{2,0,0}的距離就是沿着X軸的的1單位。 2.2.2. 矢量:矢量是理解現代GPU的關鍵,由於圖形處理器就是大規模矢量處理器。 2.2.3. 點、線、三角形:OpenGLES只渲染頂點、線段和三角形。下圖就是一種渲染案例,經過點、線、三角形渲染環形體。git

2.3 Core Animation層:

因爲iOS操做系統不會讓應用直接向前幀緩存或者後幀緩存繪圖,也不會讓應用直接控制前幀緩存和後幀緩存之間的切換。因此iOS使用Core Animation合成器去控制全部繪製層的合成。(例如:StatusBar層+開發者提供的渲染層 = 屏幕最終顯示像素)

Core Animation合成器使用OpenGLES來儘量高效地控制GPU、混合層和切換幀緩存,所以Core Animation合成器合成圖像造成一個合成結果的過程,都和OpenGLES有關。

2.4 GLKView/GLKViewController:

GLKView/GLKViewController都是GLKit框架的一部分。GLKView是UIView的子類,GLKView簡化了 經過CoreAnimation對於:建立幀緩存、管理幀緩存、繪製幀緩存的所須要做的工做。於GLKView相關的GLKViewController是視圖的委託,並接收當視圖須要重繪時的消息。

Step 3.

理解球體內的取景視角

image

球座標系(r,θ,φ)與直角座標系(x,y,z)的轉換關係:
x=rsinθcosφ
y=rsinθsinφ
z=rcosθ

image

Step 4.

代碼淺析。

1. GLKBaseEffecty: 隱藏了iOS設備支持的多格OpenGLES版本之間的差別。在應用中使用BaseEffect可以減小代碼量,例如簡化避免咱們去使用OpenGLES2中的Shading Lauguage。
EAGL:Embedded Apple GL
2. EAGLContext: Context上下文是爲了多任務在互不干擾狀況下,共享圖像的硬件設備而存在的, EAGLContext則提供並行工做的函數方法,並控制GPU去執行渲染運算。
3. EAGLSharegroup: 管理Context中的OpenGL ES對象,Context須要經過EAGLSharegroup對象建立和管理的。

[圖片上傳失敗...(image-9f3553-1529478576825)]github

4. glTexParameter :函數用於制定紋理的應用方式。[濾波模式(第14頁)]
紋理放大與縮小以下圖
/* TextureParameterName */
//提供紋理放大縮小濾波
#define GL_TEXTURE_MAG_FILTER 0x2800
#define GL_TEXTURE_MIN_FILTER 0x2801
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
@ glTexParameteri (GLenum target, GLenum pname, GLint param);
//glTexParameterf vs glTexParameteri:不一樣點在於 integerfloat類型入參。
//In the case where the pname (second) parameter is GL_TEXTURE_WRAP_S where you are passing an enum you should use glTexParameteri but for other possible values such as GL_TEXTURE_MIN_LOD and GL_TEXTURE_MAX_LOD it makes sense to pass a float parameter using glTexParameterf. 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
複製代碼
5. 紋理空間映射到對象空間上產生了Wrap,關於S方向和T方向上的延伸方案:
#define GL_TEXTURE_WRAP_S 0x2802
#define GL_TEXTURE_WRAP_T 0x2803
複製代碼
6. 紋理在作Mipmap(紋理映射)變化中產生的縮小方案:

採樣方案:

點採樣方法:最近紋素的紋理值。
線性濾波:最近領域(2x2)紋素加權平均值。
複製代碼

補充兩點:

1. Shader語法介紹
2. 齊次記法
相關文章
相關標籤/搜索