上篇回顧
上一篇文章一看就懂的OpenGL ES教程——渲染宮崎駿動(dòng)漫重拾童年 已經(jīng)詳細(xì)闡述了如何用OpenGL es將原始的YUV數(shù)據(jù)組成的視頻渲染到屏幕上,想必有很多童鞋在閱讀了它之后依然覺(jué)得回味無(wú)窮,學(xué)習(xí)的胃口也越來(lái)越大了,因?yàn)槟銈冎纼H僅渲染視頻是不夠的,我們要的是,能夠在視頻上面玩出花來(lái),于是,在本系列已經(jīng)漸入高潮的上一篇文章之后,我將在本文掀起更大的“浪花”,進(jìn)一步滿(mǎn)足列位看官的求知欲。本文將講解使用OpenGL es給視頻添加各種放抖音濾鏡特效
,這也是我在本系列第一篇文章中承諾過(guò)的。
說(shuō)起濾鏡,這是這個(gè)年頭一個(gè)很火很神奇的東西,幾乎可以說(shuō)能化腐朽為神奇(化野獸為美女?)都不過(guò)分。最初是在photoshop這類(lèi)圖像處理軟件中逐步為世人所知,后來(lái)被抖音這類(lèi)視頻軟件玩出花來(lái),為世人特別是花季少女所傾心~
那濾鏡的實(shí)現(xiàn)原理又是怎樣的呢?
濾鏡基本原理
其實(shí)我們?cè)诒鞠盗兄暗奈恼乱呀?jīng)有接觸過(guò)這方面的東西,還記得這張圖么:
嘻嘻,你又怎么會(huì)不記得呢~~ 這圖就是在一看就懂的OpenGL ES教程——臨摹畫(huà)手的浪漫之紋理映射(實(shí)踐篇)最后做圖層混合的示例圖,還記得我們?cè)趺磳?shí)現(xiàn)的么?
回顧一下當(dāng)時(shí)的片段著色器:
#version 300 es
precision mediump float;
in vec2 TexCoord;
out vec4 FragColor;
//傳入的紋理
uniform sampler2D ourTexture;
//新增紋理單元
uniform sampler2D ourTexture1;
void main() {
//對(duì)2個(gè)紋理進(jìn)行混合
FragColor = mix(texture(ourTexture, TexCoord), texture(ourTexture1, TexCoord), 0.5);
};
通過(guò)2個(gè)紋理單元去采樣2張圖片
,對(duì)當(dāng)前片段
采樣到的顏色值按照0.5透明度的比例進(jìn)行混合
,即顏色值1的0.5透明度+顏色值2的0.5透明度。
所以我們可以得出結(jié)論,濾鏡的實(shí)現(xiàn),就是在片段著色器采樣的時(shí)候,我們不再老老實(shí)實(shí)將采樣得到的顏色值直接賦給當(dāng)前片段的最終顏色值,而是增加一些額外的處理,最后再賦給當(dāng)前片段的最終顏色值。
至于什么樣的處理,那就需要好好發(fā)揮我們的想象力和算法功底了。
下面我將用幾個(gè)代表性的初級(jí)視頻濾鏡
實(shí)例來(lái)講解,因?yàn)閷W(xué)習(xí)都是一個(gè)循序漸進(jìn)的過(guò)程,所以對(duì)濾鏡實(shí)現(xiàn)的具體講解,我也將分為從易到難的級(jí)別逐步講解。
值得注意的是,所用視頻依舊是大家最喜愛(ài)的《龍貓》
哦~所以代碼全部基于上一篇的代碼,改的主要是片段著色部分。
濾鏡升級(jí)打怪
青銅
首先是青銅級(jí)別,即最簡(jiǎn)單的級(jí)別,也就是對(duì)每個(gè)片段都是一樣的處理方式
。
灰度濾鏡
顧名思義,就是將每一幀畫(huà)面轉(zhuǎn)化為灰度圖。灰度就是沒(méi)有色彩,對(duì)于RGB來(lái)說(shuō),即色彩分量全部相等,對(duì)于YUV來(lái)說(shuō),即UV分量都為默認(rèn)值128。
這一點(diǎn)從RGB和YUV的互相轉(zhuǎn)化公式就能推出來(lái),以下為BT601的Limited Range的轉(zhuǎn)化公式:
對(duì)于RGB轉(zhuǎn)YUV的公式來(lái)說(shuō),假如RGB三個(gè)分量都相等,那么可以算出此時(shí)Y等于RGB中任意一個(gè)分量的值,而U、V都是128。
而對(duì)于YUV轉(zhuǎn)RGB的公式來(lái)說(shuō),當(dāng)YUV的U、V都為128的時(shí)候,R、G、B都等于Y。
基于此,片段著色器代碼就不難寫(xiě)了。
第一種是最簡(jiǎn)單的,就是直接將采樣到的YUV中的UV置為默認(rèn)值,即無(wú)色的值
。
#version 300 es
precision mediump float;
//紋理坐標(biāo)
in vec2 vTextCoord;
//輸入的yuv三個(gè)紋理
uniform sampler2D yTexture;//采樣器
uniform sampler2D uTexture;//采樣器
uniform sampler2D vTexture;//采樣器
out vec4 FragColor;
void main() {
//采樣到的yuv向量數(shù)據(jù)
vec3 yuv;
//yuv轉(zhuǎn)化得到的rgb向量數(shù)據(jù)
vec3 rgb;
//分別取yuv各個(gè)分量的采樣紋理
yuv.x = texture(yTexture, vTextCoord).r;
//直接將uv置為0.0即可(0.5-0.5)
yuv.y = 0.0;
yuv.z = 0.0;
rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.183, 1.816,
1.540, -0.459, 0.0
) * yuv;
FragColor = vec4(rgb, 1.0);
};
由于在OpenGL es內(nèi)部已經(jīng)做了歸一化,所以采樣到的YUV數(shù)值是從0.0-1.0的,因?yàn)閁V每個(gè)通道的范圍為0至255,因而此時(shí)的無(wú)色對(duì)應(yīng)的就是128
(更細(xì)致地來(lái)說(shuō)是色度分量在偏置處理前的取值范圍是-128至127,這時(shí)候的無(wú)色對(duì)應(yīng)的是“0”值。經(jīng)過(guò)偏置后色度分量取值變成了0至255,因而此時(shí)的無(wú)色對(duì)應(yīng)的就是128了),對(duì)應(yīng)在shader中歸一化的數(shù)值就是0.5,又因?yàn)樵诰仃囅喑酥跋葴p0.5,所以這里就直接置為0.0即可
。
第二種,使用灰度轉(zhuǎn)化公式
:
gray = R * 0.2125 + G * 0.7154 + B * 0.0721
直接代入,沒(méi)啥好說(shuō)的,不要問(wèn)我公式怎么推導(dǎo)的。
#version 300 es
precision mediump float;
//紋理坐標(biāo)
in vec2 vTextCoord;
//輸入的yuv三個(gè)紋理
uniform sampler2D yTexture;//采樣器
uniform sampler2D uTexture;//采樣器
uniform sampler2D vTexture;//采樣器
out vec4 FragColor;
void main() {
//采樣到的yuv向量數(shù)據(jù)
vec3 yuv;
//yuv轉(zhuǎn)化得到的rgb向量數(shù)據(jù)
vec3 rgb;
//分別取yuv各個(gè)分量的采樣紋理
yuv.x = texture(yTexture, vTextCoord).r;
//直接將uv置為0.0即可(0.5-0.5)
yuv.y = 0.0;
yuv.z = 0.0;
rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.183, 1.816,
1.540, -0.459, 0.0
) * yuv;
float gray = rgb.r * 0.2125 + rgb.g * 0.7154 + rgb.b * 0.0721;
FragColor = vec4(gray,gray,gray, 1.0);
};
幾行代碼功夫,萌萌的動(dòng)畫(huà)片變得特別有時(shí)代感~
反色濾鏡
所謂的反色,就是其RGB顏色值與其相加和為255的對(duì)應(yīng)顏色值
,將一幅圖置為反色,看起來(lái)就有種拍X光的效果。常見(jiàn)的反色如下表格所示,左右項(xiàng)互為反色(來(lái)源于百度百科):
白色(255,255,255) | 黑色(0,0,0) |
---|---|
灰色-25%(195,195,195) | 灰度-80%(60,60,60) |
褐色(185,122,87) | 深灰藍(lán)綠色(70,133,168) |
玫瑰色(粉紅)(255,174,201) | 深綠色(0,81,54) |
金色(255,201,14) | 藍(lán)色(0,54,241) |
淺黃色(239,228,176) | 墨藍(lán)色(16,27,79) |
酸橙色(181,230,29) | 亮藍(lán)色(74,25,226) |
淡青綠色(153,217,234) | 深紅褐色(102,38,21) |
藍(lán)灰色(112,146,190) | 咖啡色(143,109,65) |
淡藍(lán)紫色(200,191,231) | 深苔蘚色(55,64,24) |
黑色(0,0,0) | 白色(255,255,255) |
---|---|
灰色-50%(127,127,127) | 灰色-50%(127,127,127)(反色就是它本身) |
深紅色(136,0,21) | 淺藍(lán)綠色(119,255,234) |
紅色(237,28,36) | 藍(lán)綠色(18,227,219) |
橙色(255,127,39) | 暗青藍(lán)色(0,128,216) |
黃色(255,242,0) | 靛藍(lán)色(0,13,255) |
綠色(34,177,76) | 暗玫紅色(221,78,179) |
青綠(0,162,232) | 鮮橙色(255,93,23) |
靛青(63,72,204) | 棕黃色(192,183,51) |
紫色(163,73,164) | 草綠色(92,182,91) |
這樣片段著色器就很簡(jiǎn)單了:
#version 300 es
precision mediump float;
//紋理坐標(biāo)
in vec2 vTextCoord;
//輸入的yuv三個(gè)紋理
uniform sampler2D yTexture;//采樣器
uniform sampler2D uTexture;//采樣器
uniform sampler2D vTexture;//采樣器
out vec4 FragColor;
void main() {
//采樣到的yuv向量數(shù)據(jù)
vec3 yuv;
//yuv轉(zhuǎn)化得到的rgb向量數(shù)據(jù)
vec3 rgb;
//分別取yuv各個(gè)分量的采樣紋理
yuv.x = texture(yTexture, vTextCoord).r;
//直接將uv置為0.0即可(0.5-0.5)
yuv.y = 0.0;
yuv.z = 0.0;
rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.183, 1.816,
1.540, -0.459, 0.0
) * yuv;
//取反色
FragColor = vec4(vec3(1.0 - rgb.r, 1.0 - rgb.g, 1.0 - rgb.b), 1.0);
};
只要最后賦值的一行改為以下即可:
FragColor = vec4(vec3(1.0 - rgb.r, 1.0 - rgb.g, 1.0 - rgb.b), 1.0);
一行代碼功夫,一切都”反了“。
白銀
白銀級(jí)別難度當(dāng)然有所提升,主要是不同區(qū)域的片段的處理方式不一樣
了。
灰度反色交叉濾鏡
#version 300 es
precision mediump float;
//紋理坐標(biāo)
in vec2 vTextCoord;
//輸入的yuv三個(gè)紋理
uniform sampler2D yTexture;//采樣器
uniform sampler2D uTexture;//采樣器
uniform sampler2D vTexture;//采樣器
out vec4 FragColor;
void main() {
//采樣到的yuv向量數(shù)據(jù)
vec3 yuv;
//yuv轉(zhuǎn)化得到的rgb向量數(shù)據(jù)
vec3 rgb;
//分別取yuv各個(gè)分量的采樣紋理(r表示?)
yuv.x = texture(yTexture, vTextCoord).r;
yuv.y = texture(uTexture, vTextCoord).g - 0.5;
yuv.z = texture(vTexture, vTextCoord).b - 0.5;
rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.183, 1.816,
1.540, -0.459, 0.0
) * yuv;
//根據(jù)不同的紋理坐標(biāo)區(qū)域,賦值不同顏色值給當(dāng)前當(dāng)前片段顏色值
if (vTextCoord.x < 0.5 && vTextCoord.y < 0.5) {
//左上角區(qū)域,反色濾鏡
FragColor = vec4(vec3(1.0 - rgb.r, 1.0 - rgb.g, 1.0 - rgb.b), 1.0);
} else if (vTextCoord.x > 0.5 && vTextCoord.y > 0.5) {
//右下角區(qū)域,灰度濾鏡
float gray = rgb.r * 0.2125 + rgb.g * 0.7154 + rgb.b * 0.0721;
FragColor = vec4(gray, gray, gray, 1.0);
} else {
FragColor = vec4(rgb, 1.0);
}
};
代碼一出來(lái),其實(shí)也是so easy~關(guān)鍵點(diǎn)就是對(duì)于紋理坐標(biāo)所在區(qū)域的判斷
,如果處于左上角,即x<0.5,y<0.5,則使用反色效果。如果處于右下角,即x>0.5,y>0.5,則使用灰度效果。其余區(qū)域不做額外處理
。
又是幾行代碼的功夫,就戴上了”有色眼鏡“~
黃金
黃金級(jí)別對(duì)于剛接觸的童鞋來(lái)說(shuō)可能是一個(gè)小門(mén)檻,因?yàn)?code>這里開(kāi)始當(dāng)前片段采樣的紋素可能并非是片段本身對(duì)應(yīng)的紋理坐標(biāo)了,而是根據(jù)需要采樣自己想要的紋理坐標(biāo)位置的顏色值。
二分屏
二分屏,顧名思義,即將一個(gè)畫(huà)面分為2個(gè)重復(fù)的畫(huà)面在平均分的屏幕位置上渲染。這里的二分屏為了保證圖像不變形,所以每個(gè)分屏都采樣原來(lái)紋理圖片的中間一半的區(qū)域。如下圖所示,左邊是被渲染的圖元,右邊是被采樣的紋理:
該圖顯示的是針對(duì)片段在第一個(gè)分屏的情況,因?yàn)橹爸v過(guò)紋理映射就是相當(dāng)于將圖元的頂點(diǎn)和紋理的頂點(diǎn)一一對(duì)上。
從這個(gè)圖我們可以得出一個(gè)通用結(jié)論,假如當(dāng)前片段坐標(biāo)為(x,y),當(dāng)y小于0.5的時(shí)候,則采樣紋理圖片對(duì)應(yīng)位置為y+0.25的紋素
那么對(duì)于下方的分屏,就可以順藤摸瓜推出以下結(jié)論:
當(dāng)y大于0.5的時(shí)候,則采樣紋理圖片對(duì)應(yīng)位置為y-0.25的紋素。
上片段著色器代碼:
#version 300 es
precision mediump float;
//紋理坐標(biāo)
in vec2 vTextCoord;
//輸入的yuv三個(gè)紋理
uniform sampler2D yTexture;//采樣器
uniform sampler2D uTexture;//采樣器
uniform sampler2D vTexture;//采樣器
out vec4 FragColor;
void main() {
//采樣到的yuv向量數(shù)據(jù)
vec3 yuv;
//yuv轉(zhuǎn)化得到的rgb向量數(shù)據(jù)
vec3 rgb;
vec2 uv = vTextCoord.xy;
float y;
//關(guān)鍵點(diǎn),對(duì)渲染圖元不同位置的點(diǎn)采樣紋理的不同位置
if (uv.y >= 0.0 && uv.y <= 0.5) {
//當(dāng)渲染圖元的點(diǎn)位于上半部分的時(shí)候,采樣比其縱坐標(biāo)大于0.25部分
uv.y = uv.y + 0.25;
}else{
//當(dāng)渲染圖元的點(diǎn)位于下半部分的時(shí)候,采樣比其縱坐標(biāo)小于0.25部分
uv.y = uv.y - 0.25;
}
//分別取yuv各個(gè)分量的采樣紋理
yuv.x = texture(yTexture, uv).r;
yuv.y = texture(uTexture, uv).g - 0.5;
yuv.z = texture(vTexture, uv).b - 0.5;
rgb = mat3(
1.0, 1.0, 1.0,
0.0, -0.183, 1.816,
1.540, -0.459, 0.0
) * yuv;
FragColor = vec4(rgb, 1.0);
};
四分屏
四分屏就更好玩了,但是原理和二分屏是一樣的。
如下圖,左邊是被渲染的圖元,右邊是被采樣的紋理,像之前所說(shuō)的,可以將紋理和圖元的頂點(diǎn)一一對(duì)上,比如四分屏的第一個(gè)格子和紋理的對(duì)應(yīng)關(guān)系如下圖所示:
所以對(duì)于第一個(gè)分屏,假如此時(shí)需要渲染的片段為(x,y),則可以推出采樣的通用關(guān)系:
當(dāng)x<0.5,y<0.5的時(shí)候,采樣(x*2,y*2)的紋素。
那么以此類(lèi)推,就可以推出:
當(dāng)x>0.5,y<0.5的時(shí)候,采樣((x-0.5)*2,y*2)的紋素。
自我介紹一下,小編13年上海交大畢業(yè),曾經(jīng)在小公司待過(guò),也去過(guò)華為、OPPO等大廠(chǎng),18年進(jìn)入阿里一直到現(xiàn)在。
深知大多數(shù)HarmonyOS鴻蒙開(kāi)發(fā)工程師,想要提升技能,往往是自己摸索成長(zhǎng)或者是報(bào)班學(xué)習(xí),但對(duì)于培訓(xùn)機(jī)構(gòu)動(dòng)則幾千的學(xué)費(fèi),著實(shí)壓力不小。自己不成體系的自學(xué)效果低效又漫長(zhǎng),而且極易碰到天花板技術(shù)停滯不前!
因此收集整理了一份《2024年HarmonyOS鴻蒙開(kāi)發(fā)全套學(xué)習(xí)資料》,初衷也很簡(jiǎn)單,就是希望能夠幫助到想自學(xué)提升又不知道該從何學(xué)起的朋友,同時(shí)減輕大家的負(fù)擔(dān)。
既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上HarmonyOS鴻蒙開(kāi)發(fā)知識(shí)點(diǎn),真正體系化!
由于文件比較大,這里只是將部分目錄大綱截圖出來(lái),每個(gè)節(jié)點(diǎn)里面都包含大廠(chǎng)面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新
如果你覺(jué)得這些內(nèi)容對(duì)你有幫助,可以添加VX:vip204888 (備注鴻蒙獲取)
一個(gè)人可以走的很快,但一群人才能走的更遠(yuǎn)。不論你是正從事IT行業(yè)的老鳥(niǎo)或是對(duì)IT行業(yè)感興趣的新人,都?xì)g迎掃碼加入我們的的圈子(技術(shù)交流、學(xué)習(xí)資源、職場(chǎng)吐槽、大廠(chǎng)內(nèi)推、面試輔導(dǎo)),讓我們一起學(xué)習(xí)成長(zhǎng)!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-857959.html
]
[外鏈圖片轉(zhuǎn)存中…(img-D1wQbynk-1712874749701)]
[外鏈圖片轉(zhuǎn)存中…(img-GGJTwWv2-1712874749702)]
既有適合小白學(xué)習(xí)的零基礎(chǔ)資料,也有適合3年以上經(jīng)驗(yàn)的小伙伴深入學(xué)習(xí)提升的進(jìn)階課程,基本涵蓋了95%以上HarmonyOS鴻蒙開(kāi)發(fā)知識(shí)點(diǎn),真正體系化!
由于文件比較大,這里只是將部分目錄大綱截圖出來(lái),每個(gè)節(jié)點(diǎn)里面都包含大廠(chǎng)面經(jīng)、學(xué)習(xí)筆記、源碼講義、實(shí)戰(zhàn)項(xiàng)目、講解視頻,并且會(huì)持續(xù)更新
如果你覺(jué)得這些內(nèi)容對(duì)你有幫助,可以添加VX:vip204888 (備注鴻蒙獲?。?/strong>
[外鏈圖片轉(zhuǎn)存中…(img-mbDg5nAN-1712874749702)]文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-857959.html
一個(gè)人可以走的很快,但一群人才能走的更遠(yuǎn)。不論你是正從事IT行業(yè)的老鳥(niǎo)或是對(duì)IT行業(yè)感興趣的新人,都?xì)g迎掃碼加入我們的的圈子(技術(shù)交流、學(xué)習(xí)資源、職場(chǎng)吐槽、大廠(chǎng)內(nèi)推、面試輔導(dǎo)),讓我們一起學(xué)習(xí)成長(zhǎng)!
到了這里,關(guān)于一看就懂的OpenGL ES教程——仿抖音濾鏡的各種奇技淫巧(一)_opengl es添加視頻的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!