相機(jī)之使用OpenGL預(yù)覽
相機(jī)之使用OpenGL拍照
相機(jī)之使用OpenGL錄像
相機(jī)之為錄像添加音頻
相機(jī)之大眼
相機(jī)之貼紙
相機(jī)之口紅
相機(jī)之美顏磨皮
LUT 濾鏡
LUT 是 Look Up Table 的簡稱,即顏色查找表钱骂,也就是輸入一個(gè)顏色茵瀑,返回另一個(gè)顏色的映射表,LUT 濾鏡實(shí)質(zhì)上就是獨(dú)立像素點(diǎn)替換,即根據(jù) OpenGL 采樣器對紋理進(jìn)行采樣得到的像素點(diǎn)勺阐,再基于像素點(diǎn)的(R绊谭,G,B)分量查表氓涣,獲得 LUT 映射的(R1牛哺,G1,B1)劳吠,替換原來的輸出
RGB 的每個(gè)顏色分量均有 256 中可能引润,因此 RGB 完整的顏色就有 256 * 256 * 256 種顏色,而每個(gè)顏色占用 3 個(gè)字節(jié)痒玩,也就有 256 * 256 * 256 * 3 個(gè)字節(jié)淳附,即 48M 內(nèi)存空間
為了降低內(nèi)存占用,LUT 以一定的采樣間隔凰荚,將相近的 n (采樣步長燃观,通常為 4 ) 種顏色采樣使用一條映射記錄并存儲(chǔ),這樣只需要 64 * 64 * 64 * 3 個(gè)字節(jié)便瑟,對于忽略的顏色值可以進(jìn)行插值獲得其相似結(jié)果
LUT 通常是用一張分辨率為 512 X 512 的圖片
LUT 圖在橫豎方向上被分成了 8 X 8 一共 64 個(gè)小方格缆毁,每一個(gè)小方格內(nèi)的 B(Blue)分量為一個(gè)定值,64 個(gè)小方格一共表示了 B 分量的 64 種取值到涂,對于每一個(gè)小方格脊框,橫豎方向又各自分為 64 個(gè)小格,以左上角為原點(diǎn)践啄,橫向小格的 R(Red)分量依次增加浇雹,縱向小格的 G(Green)分量依次增加
LUT濾鏡優(yōu)缺點(diǎn)
LUT可以應(yīng)用于Camera實(shí)時(shí)預(yù)覽和視頻實(shí)時(shí)處理,速度快屿讽,效果穩(wěn)定昭灵,更容易進(jìn)行資源化配置,由于LUT是以圖像資源的方式存在伐谈,隨著LUT數(shù)量的增多烂完,會(huì)增加軟件包體積的大小,主流的優(yōu)化方式是將資源線上化
片段著色器
precision mediump float;
uniform sampler2D vTexture;
uniform sampler2D s_LutTexture;
varying vec2 v_TextureCoord;
uniform float filterRatio;
void main(){
// 首先原始采樣像素的 RGBA 值
vec4 textureColor =texture2D(vTexture, v_TextureCoord);
// textureColor.b是一個(gè)0-1之間的浮點(diǎn)數(shù)诵棵,乘以63確定B分量所在位置(0-63)
float blueColor = textureColor.b * 63.0;
// 因?yàn)闀?huì)出現(xiàn)浮點(diǎn)誤差抠蚣,所以取與B分量值最接近的2個(gè)小方格的坐標(biāo),之后根據(jù)小數(shù)點(diǎn)進(jìn)行插值運(yùn)算
vec2 quad1;
// floor 向下取整
quad1.y = floor(floor(blueColor) / 8.0);
quad1.x = floor(blueColor) - (quad1.y * 8.0);
vec2 quad2;
quad2.y = floor(ceil(blueColor) / 7.9999);
quad2.x = ceil(blueColor) - (quad2.y * 8.0);
// 計(jì)算R分量和G分量
vec2 texPos1;
// 每個(gè)小格子代表的紋理坐標(biāo)長度是1/8=0.125
// ((0.125-1.0/512.0)*textureColor.r) 也就是 ((64-1)*textureColor.r)/512
// (64-1)*textureColor.r 將當(dāng)前實(shí)際像素的r值映射到0-63履澳,除以512是轉(zhuǎn)化為紋理坐標(biāo)中實(shí)際的點(diǎn)嘶窄,LUT圖的分辨率為512*512
// 0.5/512.0 表示每個(gè)小格子中怀跛,R分再為64個(gè),G分再為64個(gè)柄冲,形成的最小的小方格的一半
texPos1.x = (quad1.x * 0.125) + ((0.125 - 1.0/512.0) * textureColor.r) + 0.5/512.0;
texPos1.y = (quad1.y * 0.125) + ((0.125 - 1.0/512.0) * textureColor.g) + 0.5/512.0;
vec2 texPos2;
texPos2.x = (quad2.x * 0.125) + ((0.125 - 1.0/512.0) * textureColor.r) + 0.5/512.0;
texPos2.y = (quad2.y * 0.125) + ((0.125 - 1.0/512.0) * textureColor.g) + 0.5/512.0;
// 取目標(biāo)映射對應(yīng)的像素值
vec4 newColor1 = texture2D(s_LutTexture, texPos1);
vec4 newColor2 = texture2D(s_LutTexture, texPos2);
// 使用mix方法對2個(gè)邊界像素值進(jìn)行插值混合吻谋,fract函數(shù)是指取小數(shù)部分
vec4 newColor = mix(newColor1, newColor2, fract(blueColor));
// 將原圖與映射后的圖進(jìn)行插值混合,adjust調(diào)節(jié)范圍為(0-1)羊初,取0時(shí)完全使用原圖滨溉,取1時(shí)完全取映射后的濾鏡圖
gl_FragColor = mix(textureColor, vec4(newColor.rgb, textureColor.w), filterRatio);
}
著色器程序
class LutFilter(context: Context, width: Int, height: Int) :
FboFilter(context, R.raw.base_vertex, R.raw.lut_frag, width, height) {
var filterRatio = 0.0f
private val lutTextureId = TextureUtil.loadTexture(context, R.drawable.lut)
private val s_LutTexture = GLES20.glGetUniformLocation(mProgram, "s_LutTexture")
private val filterRatioLocation = GLES20.glGetUniformLocation(mProgram, "filterRatio")
override fun onDrawInFBO(textureId: Int) {
// 先將textureId的圖像畫到這一個(gè)FBO中
//激活紋理單元0
GLES20.glActiveTexture(GLES20.GL_TEXTURE0)
//將textureId紋理綁定到紋理單元0
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId)
//將紋理單元0傳給vTexture,告訴vTexture采樣器從紋理單元0讀取數(shù)據(jù)
GLES20.glUniform1i(vTexture, 0)
// 傳遞LUT圖的采樣器
GLES20.glActiveTexture(GLES20.GL_TEXTURE1)
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, lutTextureId)
GLES20.glUniform1i(s_LutTexture, 1)
GLES20.glUniform1f(filterRatioLocation, filterRatio)
//在textureId紋理上畫出圖像
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4)
//解除綁定
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)
}
}