Android 仿抖音之使用OpenGL顯示攝像頭

前言

在上一篇博客中医吊,簡單介紹了一下有關(guān)于OpenGL的基礎(chǔ)內(nèi)容,沒看過的,可以看一下OpenGL ES基礎(chǔ)缘眶,如果對(duì)里面有很多內(nèi)容還是不懂的話嘱根,就百度一下吧,里面我都是簡單說了一下大概內(nèi)容巷懈,從這一篇開始该抒,用仿抖音的項(xiàng)目來一步步具體介紹怎么在Android中使用OpenGL。

首先抖音其實(shí)就是錄制前處理和錄制后特效的處理砸喻,今天先來第一步使用OpenGL顯示攝像頭柔逼,為后面的工作做準(zhǔn)備。

需求

使用OpenGL顯示攝像頭割岛,分析一下需求愉适,其實(shí)也就是兩步,第一步采集攝像頭數(shù)據(jù)癣漆,第二步將攝像頭數(shù)據(jù)顯示到屏幕上

采集攝像頭數(shù)據(jù)

使用Android的Camera就可以實(shí)現(xiàn)采集

將攝像頭數(shù)據(jù)顯示到屏幕上

這里顯示到屏幕上维咸,其實(shí)有很多方法,比如ANative_window等等惠爽,但是這個(gè)是使用OpenGL實(shí)現(xiàn)癌蓖,使用OpenGL怎么實(shí)現(xiàn)呢,在上一篇博客中說到了OpenGL的繪制流程婚肆,基本就是按照那個(gè)流程進(jìn)行實(shí)現(xiàn)的租副。

下面是項(xiàng)目結(jié)構(gòu)中一個(gè)不太規(guī)范的類圖,下面根據(jù)這種圖來實(shí)現(xiàn)具體的代碼


簡單點(diǎn)說较性,我們都知道SurfaceView實(shí)質(zhì)是將底層顯存Surface顯示到界面上用僧,而GLSurfaceView實(shí)際就是在這個(gè)基礎(chǔ)之上增加了OpenGL環(huán)境,DouyinView實(shí)際就相當(dāng)于一塊畫布赞咙,而ScreenFilter中是封裝了如何使用畫筆去畫當(dāng)前攝像頭采集到的內(nèi)容责循,而DouyinRender渲染器,就是將ScreenFilter中的畫渲染到畫布上攀操。

創(chuàng)建工程

創(chuàng)建一個(gè)JNI工程院仿,就是在一開始創(chuàng)建項(xiàng)目的時(shí)候,勾選上 Include C++ support,這樣就創(chuàng)建好了


配置文件 AndroidManifest.xml


OpenGL的使用還需要有設(shè)備制造商提供支持速和,以下是設(shè)備的支持情況歹垫,項(xiàng)目里都是用來2.0

OpenGL ES 1.0 和 1.1 :Android 1.0和更高的版本支持這個(gè)API規(guī)范。

OpenGL ES 2.0 :Android 2.2(API 8)和更高的版本支持這個(gè)API規(guī)范颠放。

OpenGL ES 3.0 :Android 4.3(API 18)和更高的版本支持這個(gè)API規(guī)范县钥。

OpenGL ES 3.1 : Android 5.0(API 21)和更高的版本支持這個(gè)API規(guī)范。

DouyinView.java

DouyinRender.java


這里使用的Render,是實(shí)現(xiàn)了OpenGL配置好EGL的渲染器慈迈,后面會(huì)自己來配置,這里先使用這個(gè),上面也有提到過痒留,OpenGL的操作都是在GLThread線程中完成谴麦,所以這里的onSurfaceCreated,onSurfaceChanged伸头,onDrawFrame都是在GLThread中實(shí)現(xiàn)的匾效,下面單獨(dú)看看每個(gè)方法的使用


CameraHelper是封裝了對(duì)Camera的一系列操作



創(chuàng)建著色器

AS里面有個(gè)插件可以支持GLSL的高亮顯示,在plugin里面搜索 GLSL Support

頂點(diǎn)著色器


片元著色器


這里我們需要畫的是矩形恤磷,也就是兩個(gè)三角形面哼,給了4個(gè)點(diǎn)的坐標(biāo),這4個(gè)點(diǎn)扫步,會(huì)執(zhí)行4次頂點(diǎn)著色器魔策,依次將頂點(diǎn)傳遞給gl_Position,當(dāng)4個(gè)點(diǎn)都傳遞完成之后河胎,會(huì)進(jìn)行光柵化闯袒,將一個(gè)矩形變換成一個(gè)個(gè)的片元,然后再去使用gl_FragColor去著色

因?yàn)镾urfaceTexture是Android中的游岳,并不是OpenGL的政敢,所以需要使用額外擴(kuò)展的采樣器samplerExternalOES,而不是普通的sample2D采樣器胚迫,在使用samplerExternalOES時(shí)候喷户,需要再添加一句:#extension GL_OES_EGL_image_external : require


ScreenFilter.java

public ScreenFilter(Context context) {

? ? ? ? //把camera_vertext內(nèi)容讀出來

? ? ? ? String vertexSource= OpenUtils.readRawTextFile(context, R.raw.camera_vertex);

? ? ? ? String fragSource = OpenUtils.readRawTextFile(context, R.raw.camera_frag);

? ? ? ? //通過字符串創(chuàng)建著色器程序

? ? ? ? //使用opengl

? ? ? ? //一.頂點(diǎn)著色器

? ? ? ? //1.1創(chuàng)建頂點(diǎn)著色器

? ? ? ? int vSharderId = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);

? ? ? ? //1.2綁定代碼到著色器中

? ? ? ? GLES20.glShaderSource(vSharderId,vertexSource);

? ? ? ? //1.3編譯著色器

? ? ? ? GLES20.glCompileShader(vSharderId);

? ? ? ? //1.4主動(dòng)獲取成功失敗

? ? ? ? int[] status=new int[1];

? ? ? ? GLES20.glGetShaderiv(vSharderId,GLES20.GL_COMPILE_STATUS,status,0);

? ? ? ? if (status[0] != GLES20.GL_TRUE){

? ? ? ? ? ? throw new IllegalStateException("ScreenFitler 頂點(diǎn)著色器配置失敗访锻!");

? ? ? ? }

? ? ? ? //二.創(chuàng)建片元著色器

? ? ? ? int fShaderId = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

? ? ? ? //綁定代碼到著色器中

? ? ? ? GLES20.glShaderSource(fShaderId,fragSource);

? ? ? ? //編譯

? ? ? ? GLES20.glCompileShader(fShaderId);

? ? ? ? GLES20.glGetShaderiv(fShaderId,GLES20.GL_COMPILE_STATUS,status,0);

? ? ? ? if (status[0] != GLES20.GL_TRUE){

? ? ? ? ? ? throw new IllegalStateException("ScreenFilter 片元著色器配置失敗");

? ? ? ? }

? ? ? ? //三褪尝、把著色器塞到程序里面去,GPU

? ? ? ? mProgram = GLES20.glCreateProgram();

? ? ? ? GLES20.glAttachShader(mProgram,vSharderId);

? ? ? ? GLES20.glAttachShader(mProgram,fShaderId);

? ? ? ? //連接著色器

? ? ? ? GLES20.glLinkProgram(mProgram);

? ? ? ? //獲取程序是否配置成功

? GLES20.glGetProgramiv(mProgram,GLES20.GL_LINK_STATUS,status,0);

? ? ? ? if (status[0] != GLES20.GL_TRUE){

? ? ? ? ? ? throw? new IllegalStateException("Screen Filter 著色器程序連接失敗");

? ? ? ? }

? ? ? ? //因?yàn)橹饕呀?jīng)塞到了GPU程序中朗若,所以可以刪除了

? ? ? ? GLES20.glDeleteShader(vSharderId);

? ? ? ? GLES20.glDeleteShader(fShaderId);

? ? ? ? //獲得著色器程序中的變量的索引恼五,通過這個(gè)索引對(duì)其進(jìn)行賦值

? ? ? ? vPosition = GLES20.glGetAttribLocation(mProgram,"vPosition");

? ? ? ? vCoord = GLES20.glGetAttribLocation(mProgram,"vCoord");

? ? ? ? vMatrix = GLES20.glGetUniformLocation(mProgram,"vMatrix");

? ? ? ? vTexture =? GLES20.glGetUniformLocation(mProgram,"vTexture");

? ? ? ? //創(chuàng)建一個(gè)數(shù)據(jù)緩沖區(qū)

? ? ? ? //OpenGL的中頂點(diǎn)的位置坐標(biāo)

? ? ? ? mVertextBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();

? ? ? ? mVertextBuffer.clear();

? ? ? ? float[] v = {-1.0f,-1.0f,

? ? ? ? ? ? ? ? 1.0f,-1.0f,

? ? ? ? ? ? ? ? -1.0f,1.0f,

? ? ? ? ? ? ? ? 1.0f,1.0f

? ? ? ? };

? ? ? ? mVertextBuffer.put(v);

? ? ? ? //采樣器采樣圖片的坐標(biāo)

? ? ? ? mTextureBuffer = ByteBuffer.allocateDirect(4 * 2 * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();

? ? ? ? mTextureBuffer.clear();

//? ? ? ? float[] f={0.0f,1.0f,

//? ? ? ? ? ? ? ? 1.0f,1.0f,

//? ? ? ? ? ? ? ? 0.0f,0.0f,

//? ? ? ? ? ? ? ? 1.0f,0.0f

//? ? ? ? };

//

? ? ? ? //順時(shí)針旋轉(zhuǎn)90度

//? ? ? ? float[] f={1.0f,1.0f,

//? ? ? ? ? ? ? ? 1,0,

//? ? ? ? ? ? ? ? 0,1,

//? ? ? ? ? ? ? ? 0,0

//

//

//? ? ? ? };

//? ? ? ? 鏡像

? ? ? ? float[] f={1.0f,0.0f,

? ? ? ? ? ? ? ? 1.0f,1.0f,

? ? ? ? ? ? ? ? 0.0f,0.0f,

? ? ? ? ? ? ? ? 0.0f,1.0f

? ? ? ? };

? ? ? ? mTextureBuffer.put(f);

? ? }

? ? /**

? ? * 使用著色器程序開始畫畫

? ? * @param texture

? ? * @param mtx

? ? */

? ? public void onDrawFrame(int texture,float[] mtx){

? ? ? ? //1.設(shè)置窗口大小

? ? ? ? GLES20.glViewport(0,0,mWidth,mHeight);

? ? ? ? //2.使用著色器程序

? ? ? ? GLES20.glUseProgram(mProgram);

? ? ? ? /**

? ? ? ? * 3. 畫頂點(diǎn)

? ? ? ? */

? ? ? ? //3.1傳入頂點(diǎn)數(shù)據(jù),確定形狀

? ? ? ? mVertextBuffer.position(0);

? ? ? ? //size:2表示xy兩個(gè)數(shù)據(jù)

? ? ? ? GLES20.glVertexAttribPointer(vPosition,2,GLES20.GL_FLOAT,false,0,mVertextBuffer);

? ? ? ? //3.2激活

? ? ? ? GLES20.glEnableVertexAttribArray(vPosition);

? ? ? ? //4哭懈,片元灾馒,紋理

? ? ? ? mTextureBuffer.position(0);

? ? ? ? GLES20.glVertexAttribPointer(vCoord,2,GLES20.GL_FLOAT,false,0,mTextureBuffer);

? ? ? ? GLES20.glEnableVertexAttribArray(vCoord);

? ? ? ? //5.變換矩陣

? ? ? ? GLES20.glUniformMatrix4fv(vMatrix,1,false,mtx,0);

? ? ? ? //片元

? ? ? ? //激活圖層

? ? ? ? GLES20.glActiveTexture(GLES20.GL_TEXTURE);

? ? ? ? //圖像數(shù)據(jù)

? ? ? ? GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,texture);

? ? ? ? //傳遞參數(shù)

? ? ? ? GLES20.glUniform1i(vTexture,0);

? ? ? ? //參數(shù)傳完了,通知OpenGL畫畫遣总,從第0個(gè)點(diǎn)開始睬罗,共4個(gè)點(diǎn)

? ? ? ? GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);

? ? }

Tips

? ? 因?yàn)镺penGL中如果顯示不正確,錯(cuò)誤一般都比較難找旭斥,所以在代碼里有在一些地方去獲取是否配置正確容达,來方便找錯(cuò)

? ? 記錄我遇到的兩個(gè)bug,如果遇到問題可做參考垂券,第一個(gè)是在獲取索引的時(shí)候花盐,類型弄錯(cuò)了羡滑。第二個(gè)是在頂點(diǎn)著色器中,獲取像素點(diǎn)aCoord進(jìn)行矩陣變換的時(shí)候算芯,寫成了紋理坐標(biāo) * 矩陣柒昏,這樣出來的結(jié)果是錯(cuò)的,一定要是 矩陣 * 紋理坐標(biāo)熙揍。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末职祷,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子届囚,更是在濱河造成了極大的恐慌有梆,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,284評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件意系,死亡現(xiàn)場(chǎng)離奇詭異泥耀,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)昔字,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門爆袍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人作郭,你說我怎么就攤上這事陨囊。” “怎么了夹攒?”我有些...
    開封第一講書人閱讀 164,614評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵蜘醋,是天一觀的道長。 經(jīng)常有香客問我咏尝,道長压语,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評(píng)論 1 293
  • 正文 為了忘掉前任编检,我火速辦了婚禮胎食,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘允懂。我一直安慰自己厕怜,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,699評(píng)論 6 392
  • 文/花漫 我一把揭開白布蕾总。 她就那樣靜靜地躺著粥航,像睡著了一般。 火紅的嫁衣襯著肌膚如雪生百。 梳的紋絲不亂的頭發(fā)上递雀,一...
    開封第一講書人閱讀 51,562評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音蚀浆,去河邊找鬼缀程。 笑死搜吧,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的杠输。 我是一名探鬼主播赎败,決...
    沈念sama閱讀 40,309評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼蠢甲!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起据忘,我...
    開封第一講書人閱讀 39,223評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤鹦牛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后勇吊,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體曼追,經(jīng)...
    沈念sama閱讀 45,668評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,859評(píng)論 3 336
  • 正文 我和宋清朗相戀三年汉规,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了礼殊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,981評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡针史,死狀恐怖晶伦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情啄枕,我是刑警寧澤婚陪,帶...
    沈念sama閱讀 35,705評(píng)論 5 347
  • 正文 年R本政府宣布,位于F島的核電站频祝,受9級(jí)特大地震影響泌参,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜常空,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,310評(píng)論 3 330
  • 文/蒙蒙 一沽一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧漓糙,春花似錦铣缠、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至为狸,卻和暖如春歼郭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背辐棒。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評(píng)論 1 270
  • 我被黑心中介騙來泰國打工病曾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留牍蜂,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,146評(píng)論 3 370
  • 正文 我出身青樓泰涂,卻偏偏與公主長得像鲫竞,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子逼蒙,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,933評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容