天天看點

NDK OpenGL ES 3.0 開發(十七):相機基礎濾鏡相機基礎濾鏡聯系與交流

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/103335598

相機基礎濾鏡

上文中我們通過 ImageReader 擷取到 Camera2 預覽的 YUV 資料,然後利用 OpenGLES 渲染實作相機預覽,這一節将利用 GLSL (OpenGL 着色器語言)基于不同的着色器實作多種基礎濾鏡。

GLSL 一些使用頻率比較高的内建函數

内建函數 函數說明
float distance (genType p0, genType p1) 計算向量 p0 ,p1 之間的距離
float length (genType x) 傳回向量 x 的長度
genType floor (genType x) 傳回小于等于 x 的最大整數值
genType ceil (genType x) 傳回大于等于 x 的最小整數值
genType mod (genType x, float y) 傳回 x – y * floor (x / y) ,即求模計算 %
float dot (genType x, genType y) 向量 x ,y 之間的點積
vec3 cross (vec3 x, vec3 y) 向量 x ,y 之間的叉積
genType normalize (genType x) 标準化向量,傳回一個方向和 x 相同但長度為 1 的向量

動态網格

NDK OpenGL ES 3.0 開發(十七):相機基礎濾鏡相機基礎濾鏡聯系與交流

動态網格濾鏡主要是将紋理劃分為多個網格,然後根據一個偏移量動态改變網格線的寬度。mod 和 floor 為 GLSL 的内建函數,分别表示取模和取整。需要注意的是,計算之前需要将紋理坐标系轉換為圖檔坐标系,保證網格沒有被拉伸。

//dynimic mesh 動态網格着色器
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
uniform float u_offset;//偏移量
uniform vec2 texSize;//紋理尺寸
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
void main()
{
    vec2 imgTexCoord = v_texcoord * texSize;//将紋理坐标系轉換為圖檔坐标系
    float sideLength = texSize.y / 6.0;//網格的邊長
    float maxOffset = 0.15 * sideLength;//設定網格線寬度的最大值
    float x = mod(imgTexCoord.x, floor(sideLength));
    float y = mod(imgTexCoord.y, floor(sideLength));

    float offset = u_offset * maxOffset;

    if(offset <= x
    && x <= sideLength - offset
    && offset <= y
    && y <= sideLength - offset)
    {
        gl_FragColor = YuvToRgb(v_texcoord);
    }
    else
    {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}      

分屏

NDK OpenGL ES 3.0 開發(十七):相機基礎濾鏡相機基礎濾鏡聯系與交流

分屏濾鏡的原理是在多個指定區域内對整個紋理進行下采樣(縮小),進而實作整個圖像在多個區域内多次顯示。

//分屏(四分屏)
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
void main()
{
    vec2 newTexCoord = v_texcoord;
    if(newTexCoord.x < 0.5)
    {
        newTexCoord.x = newTexCoord.x * 2.0;
    }
    else
    {
        newTexCoord.x = (newTexCoord.x - 0.5) * 2.0;
    }

    if(newTexCoord.y < 0.5)
    {
        newTexCoord.y = newTexCoord.y * 2.0;
    }
    else
    {
        newTexCoord.y = (newTexCoord.y - 0.5) * 2.0;
    }

    gl_FragColor = YuvToRgb(newTexCoord);
}      

縮放的圓

NDK OpenGL ES 3.0 開發(十七):相機基礎濾鏡相機基礎濾鏡聯系與交流

縮放的圓效果實作主要依賴偏移量來動态改變圓半徑的大小,在半徑區域内對紋理采樣顯示圖像,在半徑區域外傳回一個固定顔色(如白色)。distance 也是 GLSL 的内建函數,用于計算兩點之間的距離。另外需要注意是,在計算之前首先要将紋理坐标系轉換為圖檔坐标系,否則繪制的将會是一個橢圓形圖像(圖像寬高不同的情況下),想一想為什麼會這樣?

//scale circle 縮放的圓
#version 100
precision highp float;
varying vec2 v_texcoord;
uniform lowp sampler2D s_textureY;
uniform lowp sampler2D s_textureU;
uniform lowp sampler2D s_textureV;
uniform float u_offset;
uniform vec2 texSize;
vec4 YuvToRgb(vec2 uv) {
    float y, u, v, r, g, b;
    y = texture2D(s_textureY, uv).r;
    u = texture2D(s_textureU, uv).r;
    v = texture2D(s_textureV, uv).r;
    u = u - 0.5;
    v = v - 0.5;
    r = y + 1.403 * v;
    g = y - 0.344 * u - 0.714 * v;
    b = y + 1.770 * u;
    return vec4(r, g, b, 1.0);
}
void main()
{
    vec2 imgTex = v_texcoord * texSize;//将紋理坐标系轉換為圖檔坐标系
    float r = (u_offset + 0.208 ) * texSize.x;
    if(distance(imgTex, vec2(texSize.x / 2.0, texSize.y / 2.0)) < r)
    {
        gl_FragColor = YuvToRgb(v_texcoord);
    }
    else
    {
        gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}      

在計算之前首先要将紋理坐标系轉換為圖檔坐标系,其原因在于紋理縱橫坐标的取值範圍均為 [0, 1] ,從數值上看紋理的縱橫方向長度相同,但是在 OpenGL 采樣時,圖像的寬高比往往不是 1 ,這就導緻了數值相同的縱橫坐标,對應不同的采樣權重,出現了預期繪制圓形而實際上卻繪制出橢圓的情況。

聯系與交流

技術交流/擷取源碼可以添加我的微信:Byte-Flow

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
NDK OpenGL ES 3.0 開發(十七):相機基礎濾鏡相機基礎濾鏡聯系與交流

繼續閱讀