日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

利用FFmpeg和OpenGL ES 实现 3D 全景播放器

發(fā)布時(shí)間:2024/3/26 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 利用FFmpeg和OpenGL ES 实现 3D 全景播放器 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

前言

我們已經(jīng)利用 FFmpeg + OpenGLES + OpenSLES 實(shí)現(xiàn)了一個(gè)多媒體播放器,本文將基于此播放器實(shí)現(xiàn)一個(gè)酷炫的 3D 全景播放器。

全景播放器原理

全景視頻是由多臺(tái)攝像機(jī)在一個(gè)位置同時(shí)向四面八方拍攝,最后經(jīng)過(guò)后期拼接處理生成的。

用普通的多媒體播放器播放全景視頻,畫面會(huì)呈現(xiàn)出嚴(yán)重的拉伸和扭曲變形。

全景播放器將視頻畫面渲染到球面上,相當(dāng)于從球心去觀察內(nèi)部球面,觀察到的畫面 360 度無(wú)死角,這也就是市面上大多數(shù)“ VR 盒子”的實(shí)現(xiàn)原理。

構(gòu)建球面網(wǎng)格

全景播放器原理與普通播放器的本質(zhì)區(qū)別在渲染圖像部分,普通播放器只需將視頻畫面渲染到一個(gè)矩形平面上,而全景播放器需要將視頻畫面渲染到球面。

為實(shí)現(xiàn)全景播放器,我們只需要利用 OpenGL 構(gòu)建一個(gè)球體,然后將 FFmpeg 解碼的視頻畫面渲染到這個(gè)球體表面即可。

OpenGL ES 中所有 3D 物體均是由三角形構(gòu)成的,構(gòu)建一個(gè)球體只需要利用球坐標(biāo)系中的經(jīng)度角、維度角以及半徑計(jì)算出球面點(diǎn)的三維坐標(biāo),最后這些坐標(biāo)點(diǎn)構(gòu)成一個(gè)個(gè)小矩形,每個(gè)矩形就可以分成 2 個(gè)三角形。

球坐標(biāo)系

在球坐標(biāo)系中,利用經(jīng)度角、維度角和半徑計(jì)算出球面點(diǎn)坐標(biāo)公式如下:

x=rsinsβcosθ
y=rsinsθcosβ
z=rcosβ

根據(jù)上述公式計(jì)算球面頂點(diǎn)坐標(biāo)的代碼實(shí)現(xiàn), 其中 ANGLE_SPAN 為步長(zhǎng),RADIUS 為半徑,RADIAN 用于弧度轉(zhuǎn)換 。

//構(gòu)建頂點(diǎn)坐標(biāo) for (float vAngle = 90; vAngle > -90; vAngle = vAngle - ANGLE_SPAN) {//垂直方向每隔 ANGLE_SPAN 度計(jì)算一次for (float hAngle = 360; hAngle > 0; hAngle = hAngle - ANGLE_SPAN) {//水平方向每隔 ANGLE_SPAN 度計(jì)算一次double xozLength = RADIUS * cos(RADIAN(vAngle));float x1 = (float) (xozLength * cos(RADIAN(hAngle)));float z1 = (float) (xozLength * sin(RADIAN(hAngle)));float y1 = (float) (RADIUS * sin(RADIAN(vAngle)));xozLength = RADIUS * cos(RADIAN(vAngle - ANGLE_SPAN));float x2 = (float) (xozLength * cos(RADIAN(hAngle)));float z2 = (float) (xozLength * sin(RADIAN(hAngle)));float y2 = (float) (RADIUS * sin(RADIAN(vAngle - ANGLE_SPAN)));xozLength = RADIUS * cos(RADIAN(vAngle - ANGLE_SPAN));float x3 = (float) (xozLength * cos(RADIAN(hAngle - ANGLE_SPAN)));float z3 = (float) (xozLength * sin(RADIAN(hAngle - ANGLE_SPAN)));float y3 = (float) (RADIUS * sin(RADIAN(vAngle - ANGLE_SPAN)));xozLength = RADIUS * cos(RADIAN(vAngle));float x4 = (float) (xozLength * cos(RADIAN(hAngle - ANGLE_SPAN)));float z4 = (float) (xozLength * sin(RADIAN(hAngle - ANGLE_SPAN)));float y4 = (float) (RADIUS * sin(RADIAN(vAngle)));//球面小矩形的四個(gè)點(diǎn)vec3 v1(x1, y1, z1);vec3 v2(x2, y2, z2);vec3 v3(x3, y3, z3);vec3 v4(x4, y4, z4);//構(gòu)建第一個(gè)三角形m_VertexCoords.push_back(v1);m_VertexCoords.push_back(v2);m_VertexCoords.push_back(v4);//構(gòu)建第二個(gè)三角形m_VertexCoords.push_back(v4);m_VertexCoords.push_back(v2);m_VertexCoords.push_back(v3);} }

計(jì)算對(duì)應(yīng)球面坐標(biāo)的紋理坐標(biāo),實(shí)際上就是計(jì)算固定行和列的網(wǎng)格點(diǎn)。

//構(gòu)建紋理坐標(biāo),球面展開后的矩形 int width = 360 / ANGLE_SPAN;//列數(shù) int height = 180 / ANGLE_SPAN;//行數(shù) float dw = 1.0f / width; float dh = 1.0f / height; for (int i = 0; i < height; i++) {for (int j = 0; j < width; j++) {//每一個(gè)小矩形,由兩個(gè)三角形構(gòu)成,共六個(gè)點(diǎn)float s = j * dw;float t = i * dh;vec2 v1(s, t);vec2 v2(s, t + dh);vec2 v3(s + dw, t + dh);vec2 v4(s + dw, t);//構(gòu)建第一個(gè)三角形m_TextureCoords.push_back(v1);m_TextureCoords.push_back(v2);m_TextureCoords.push_back(v4);//構(gòu)建第二個(gè)三角形m_TextureCoords.push_back(v4);m_TextureCoords.push_back(v2);m_TextureCoords.push_back(v3);} }

用 OpenGL 劃線渲染球狀網(wǎng)格,測(cè)試構(gòu)建的球體是否準(zhǔn)確。

渲染全景視頻

計(jì)算好頂點(diǎn)坐標(biāo)和紋理坐標(biāo)后,剩下的就是簡(jiǎn)單的紋理映射(紋理貼圖),不了解紋理映射的同學(xué)可以查看這篇文章紋理映射,篇幅有限,這里不展開講述。

頂點(diǎn)坐標(biāo)和紋理坐標(biāo)初始化 VAO 。

// Generate VBO Ids and load the VBOs with data glGenBuffers(2, m_VboIds); glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]); glBufferData(GL_ARRAY_BUFFER, sizeof(vec3) * m_VertexCoords.size(), &m_VertexCoords[0], GL_STATIC_DRAW);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]); glBufferData(GL_ARRAY_BUFFER, sizeof(vec2) * m_TextureCoords.size(), &m_TextureCoords[0], GL_STATIC_DRAW);// Generate VAO Id glGenVertexArrays(1, &m_VaoId); glBindVertexArray(m_VaoId);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[0]); glEnableVertexAttribArray(0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(vec3), (const void *)0); glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindBuffer(GL_ARRAY_BUFFER, m_VboIds[1]); glEnableVertexAttribArray(1); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(vec2), (const void *)0); glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);glBindVertexArray(GL_NONE);

繪制視頻畫面。

// Use the program object glUseProgram (m_ProgramObj);glBindVertexArray(m_VaoId);GLUtils::setMat4(m_ProgramObj, "u_MVPMatrix", m_MVPMatrix);//綁定紋理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, m_TextureId); GLUtils::setFloat(m_ProgramObj, "s_TextureMap", 0);glDrawArrays(GL_TRIANGLES, 0, m_VertexCoords.size());

先繪制普通視頻,看看是啥樣兒。

最后繪制全景視頻。

編譯好的FFmpeg下載地址

FFmpeg_4.3.2支持Android的音視頻處理庫(kù)-Android文檔類資源-CSDN文庫(kù)

總結(jié)

以上是生活随笔為你收集整理的利用FFmpeg和OpenGL ES 实现 3D 全景播放器的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。