OpenGL ES實踐教程(四)VR全景視頻播放

教程

OpenGL ES實踐教程1-Demo01-AVPlayer
OpenGL ES實踐教程2-Demo02-攝像頭采集數(shù)據(jù)和渲染
OpenGL ES實踐教程3-Demo03-Mirror
其他教程請移步OpenGL ES文集,這一篇介紹以下知識點:

  • AVFoundation——加載視頻;
  • CoreVideo——配置紋理;
  • OpenGL ES——渲染視頻;
  • 3D數(shù)學(xué)——球體以及3維變換;

核心思路

通過AVFoundation加載視頻源,讀取到每一幀的CMSampleBuffer之后,用CoreVideo創(chuàng)建OpenGL ES紋理緩存并上傳GPU;OpenGL ES按照球體的模型來渲染視頻;用移動攝像機(jī)朝向或者旋轉(zhuǎn)球體的方式來響應(yīng)手指的移動達(dá)到移動鏡頭的效果。

效果展示

具體細(xì)節(jié)

1、配置OpenGL ES;

  • loadShaders加載著色器和compileShader編譯著色器的內(nèi)容前面的教程已經(jīng)介紹過都次,不再贅述;
  • setupBuffers配置緩存信息,并且創(chuàng)建頂點數(shù)據(jù)緩存,把球體的頂點和紋理數(shù)據(jù)先上傳GPU;

因為模型的頂點數(shù)據(jù)不會變化,故而可以預(yù)先上傳,使用時只需通過glBindBuffer即可使用頂點數(shù)據(jù);
如果想每幀都上傳頂點數(shù)據(jù)亦可以。(不推薦)

  • glUniform常量賦值在編譯鏈接完成頂點著色器后,可以設(shè)置著色器里面用到常量;

2、加載視頻;

  • loadAsset創(chuàng)建視頻源,并用loadValuesAsynchronouslyForKeys加載軌道信息;
  • createAssetReader創(chuàng)建Reader,并設(shè)置讀取的格式與軌道目標(biāo);
  • processAsset開始Reader,并啟動CADisplayLink開始讀取視頻幀;

通過mReaderVideoTrackOutput的copyNextSampleBuffer可以獲取到下一幀的視頻信息;
通過CMSampleBufferGetImageBuffer可以獲取到CMSampleBuffer里面的像素緩存pixelBuffer,將其傳給GLView用于配置紋理;

    CMSampleBufferRef sampleBuffer = [self.mReaderVideoTrackOutput copyNextSampleBuffer];
    CVPixelBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);

3、配置紋理;

  • 通過CVBufferGetAttachment獲取pixelBuffer的顏色空間格式,決定使用的顏色轉(zhuǎn)換矩陣,用于下一步的YUV到RGB顏色空間的轉(zhuǎn)換;
  • 通過glActiveTexture啟用紋理,用
    CVOpenGLESTextureCacheCreateTextureFromImage創(chuàng)建紋理,再glBindTexture綁定紋理,設(shè)置好紋理格式;

CVOpenGLESTextureCacheCreateTextureFromImage創(chuàng)建的亮度紋理為frameHeight * frameWidth,格式為GL_LUMINANCE
CVOpenGLESTextureCacheCreateTextureFromImage創(chuàng)建的亮度紋理為frameHeight/2 * frameWidth/2,格式為GL_LUMINANCE_ALPHA;

思考0:為何要使用CV?是否可以不使用CV直接讀取紋理信息?

4、YUV到RGB顏色空間的轉(zhuǎn)換;

YUV顏色空間由亮度+色度組成,GPU支持的RGB的顏色空間,故而需要進(jìn)行一次轉(zhuǎn)換。
下面是demo用到的顏色轉(zhuǎn)換矩陣:

const GLfloat kColorConversion601FullRange[] = {
    1.0,    1.0,    1.0,
    0.0,    -0.343, 1.765,
    1.4,    -0.711, 0.0,
};

注意:顏色轉(zhuǎn)換和頂點變換都是通過矩陣來計算。

5、球體渲染

簡單介紹下全景視頻的原理:
通過多個攝像機(jī)錄制多方向的視頻,通過投影計算,存儲到一個視頻中;
將視頻渲染到球面上,通過攝像機(jī)的位置與朝向,計算每次能顯示的內(nèi)容并繪制到屏幕。如下圖:


這就涉及到兩個問題:

  • 將全景的視頻信息存儲在二維的視頻里面;
  • 將二維的視頻還原成全景的視頻信息。
    (攝像機(jī)的位置和朝向計算看下面)

思考1:全景視頻顯示效果與普通視頻有何區(qū)別?為什么?

球面到2D視頻的展開


假設(shè)地球被圍在一中空的圓柱里,其基準(zhǔn)緯線與圓柱相切(赤道)接觸,然后再假想地球中心有一盞燈,把球面上的圖形投影到圓柱體上,再把圓柱體展開,得到投影。

越靠近畫面的TOP和BOTTOM,圖像的扭曲效果就越嚴(yán)重。上圖還看不太出來,看看下圖。


思考2:是否存在沒有扭曲效果的全景顯示?

2D視頻到球面的顯示
之前的教程有介紹過,點這里
下圖是一張展開了的地球圖像


下圖是按照球體的頂點數(shù)據(jù)進(jìn)行渲染

6、視角變化

球的圓心在原點,攝像機(jī)的所在也是原點,如下圖。



球坐標(biāo)系(r,θ,φ)與直角坐標(biāo)系(x,y,z)的轉(zhuǎn)換關(guān)系:
x=rsinθcosφ
y=rsinθsinφ
z=rcosθ


思考

思考0:視頻的紋理創(chuàng)建、銷毀非常頻繁,并且紋理普遍較大,CV對紋理的創(chuàng)建和緩存有針對的優(yōu)化,故而在處理視頻幀的時候推薦通過CV來處理紋理(圖像不行)。
如果不是用CV創(chuàng)建紋理,可以通過CVPixelBufferGetBaseAddress,拿到pixelBuffer的像素指針p;p的起始是亮度紋理,p加上亮度紋理的偏移量(frameWidth * frameHeight)之后是色度紋理。

思考1:全景視頻帶有明顯的扭曲效果。因為是把2D平面的紋理渲染到球面上,故而帶有扭曲效果。

思考2:存在。天空盒可以做到。天空盒

擴(kuò)展

1、投影方式

Equisolid投影
Mercator投影

2、錄制難點

同步、角度、分屏(雙倍設(shè)備)

和VR的區(qū)別。全景+雙屏。

總結(jié)

demo的起因是群里和徐杰聊天的時候說到最近看到一個全景視頻直播,想起以前自己曾想過做一個全景圖像,結(jié)果因為不懂CV和AVFoundation、沒有球體的頂點數(shù)據(jù)而放棄。
剛好他有一個視頻源,就要過來再試試。
結(jié)果這次的demo只花一天的時間就做完了,第二天的時間都是微調(diào)手指觸摸的體驗。
實現(xiàn)過程中遇到一些坑,但是在分析完數(shù)據(jù)之后也馬上解決,一次很好的實踐體驗。
篇幅有限,代碼在這里,歡迎star、fork。疑問請留言。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容