OpenGL ES

Visual Studio配置OpenGL ES环境

Posted by CS on November 30, 2024

1.安装

1.1 Visual Studio配置OpenGL ES环境

1.1.1 下载OpenGL ES相关配置

1.下载《OpenGL ES3.0 Programming guide》示例代码并解压,如图:

https://codeload.github.com/danginsburg/opengles3-book/zip/master

  1. 下载mali opengl es模拟器(我下载的是64位的,在Visual Studio中也选择debug x64),并解压,如图:

    https://developer.arm.com/downloads/-/opengl-es-emulator-downloads

  2. 然后用Cmake编译(Cmake下载地址:https://cmake.org/download/)。

    IMG_256

    安装之后,将Cmake安装目录的Bin路径添加到windows系统环境环境变量Path中。

    然后打开Cmak-GUI.exe,编译源码。需要手动创建一个编译后保存二进制文件的目录(build)。依次进行如下步骤:

    再点击“Configure”后,出现以下窗口,来选取编译所用的生成器。我安装的是VS 2019,电脑是64位操作系统(对应mali opengl es模拟器也是64位)。

    然后点击“Finish”。然后会出现红色字体提示。将“EFL_LIBRARY”“OPENGLES3_LIBRARY”分别选择D:/OpenGlProject/OpenGL_ES_All/Mali_OpenGL_ES_Emulator64bit/libEGL.lib、D:/OpenGlProject/OpenGL_ES_All/Mali_OpenGL_ES_Emulator64bit/libGLESv2.lib。如图对应地址值:

    然后点击“Generate”生成所需文件。此时,可以之间点击“Open Project”按钮,直接用VS2019打开生成的工程(也可以在刚刚创建的build文件夹中打开GLFW.sln)。

    1.1.2 配置visual studio

    1. 打开工程后,首先右击“Hello_Triangle”子工程,先以“Hello_Triangle”子工程为启动项。

  3. 进入工程属性页面,配置include包含目录、lib库目录:

  4. 然后配置好VS,选择“Debug”“x64”。点击“本地Windows调试器”试运行一下(会提示缺少mali opengl es模拟器动态库的错误):

  5. 将mali opengl es模拟器文件夹中的动态库等,复制到子工程“Hello_Triangle”的Debug目录下:

    复制到:

  6. 最后,点击“本地Windows调试器”运行一下,无误的话便会显示一个三角形:

    (补充)OpenGL ES 基础

  7. YUV渲染

    原理:也叫做 YCbCr,其中 “Y” 表示明亮度(Luminance),“U” 和 “V” 分别表示色度(Chrominance)和浓度(Chroma)。

    YUV 编码模型的图像数据一般不能直接用于显示,还需要将其转换为 RGB(RGBA) 编码模型,才能够正常显示图像。

    IMG_256

    在GLSL中计算如下,构建mat3矩阵时按照列构建:

    vec4 YUV2RGB(vec2 Coord) { ` **vec3** rgb = **mat3**(1.0, 1.0, 1.0, 0, -0.344, 1.770, 1.403, -0.714, 0) \* **vec3**(texture(y\_samp, Coord).r, texture(u\_samp, Coord).r - 0.5, texture(v\_samp, Coord).r - 0.5); `return vec4(rgb, 1.0);

  8. FBO离屏渲染

    使用 FBO 可以让渲染操作不用再渲染到屏幕上,而是渲染到离屏 Buffer 中,然后可以使用 glReadPixels 或者 HardwareBuffer 将渲染后的图像数据读出来,从而实现在后台利用 GPU 完成对图像的处理。

    创建并初始化 FBO 的步骤:

    // 创建一个 2D 纹理用于连接 FBO 的颜色附着

    glGenTextures(1, &m_FboTextureId);

    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glBindTexture(GL_TEXTURE_2D, GL_NONE);

    // 创建 FBO

    glGenFramebuffers(1, &m_FboId);

    // 绑定 FBO

    glBindFramebuffer(GL_FRAMEBUFFER, m_FboId);

    // 绑定 FBO 纹理

    glBindTexture(GL_TEXTURE_2D, m_FboTextureId);

    // 将纹理连接到 FBO 附着

    glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_FboTextureId, 0);

    // 分配内存大小

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_RenderImage.width, m_RenderImage.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    // 检查 FBO 的完整性状态

    if (glCheckFramebufferStatus(GL_FRAMEBUFFER)!= GL_FRAMEBUFFER_COMPLETE) {

    ` `LOGCATE(“FBOSample::CreateFrameBufferObj glCheckFramebufferStatus status != GL_FRAMEBUFFER_COMPLETE”);

    ` `return false;

    }

    // 解绑纹理

    glBindTexture(GL_TEXTURE_2D, GL_NONE);

    // 解绑 FBO

    glBindFramebuffer(GL_FRAMEBUFFER, GL_NONE);

2.OpenGL ES 使用场景

  1. 图片处理。比如图片色调转换、美颜等。视频滤镜、音频滤镜.
  2. 摄像头预览效果处理。比如美颜相机、恶搞相机等。
  3. 视频处理。摄像头预览效果处理可以,这个自然也不在话下了。
  4. 3D 游戏。比如神庙逃亡、都市赛车等。

2.1 OpenGL ES人脸检测

人脸检测主要基于预训练的“face_detection_yunet”深度学习人脸检测模型。

结合OpenCV库对视频帧提取,对人脸关键点、文字、贴图等可视化图像处理。

最终基于OpenGL将数据帧在window渲染显示。

1.首先配置环境:

opencv_world455d.lib

glfw/glfw3dll.lib

opengl32.lib

glew\lib\Release\x64\glew32.lib

assimp-vc142-mtd.lib

2.初始化人脸检测模型

// *模型参数*

String modelPath = “resources/face_detection_yunet_2022mar.onnx”;

int backendId = 0;

int targetId = 0;

float scoreThreshold = 0.9;

` `float nmsThreshold = 0.3;

` `int topK = 5;

` `// Initialize FaceDetectorYN

` `Ptr detector = FaceDetectorYN::create(modelPath, "", Size(320, 320), scoreThreshold, nmsThreshold, topK, backendId, targetId);

3.可视化,将帽子拼接到原图

static Mat visualize(Mat input, Mat faces, Mat hat, Mat hat_alpha, bool print_flag = false, double fps = 30, int thickness = 2)

{

    Mat output = input.clone();

    if (fps > 0)

    {

        putText(output, format(“FPS: %.2f”, fps), Point2i(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));

    }

    for (int i = 0; i < faces.rows; i++)

    {

        if (print_flag)

        {

            cout « “Face “ « i

                 « ”, top-left coordinates: (“ « faces.at(i, 0) << ", " << faces.at(i, 1) << "), "

                 « “box width: “ « faces.at(i, 2) << ", box height: " << faces.at(i, 3) << ", "

                 « “score: “ « faces.at(i, 14) << "\n";

        }

        // Draw bounding box

        rectangle(output, Rect2i(int(faces.at(i, 0)), int(faces.at(i, 1)), int(faces.at(i, 2)), int(faces.at(i, 3))), Scalar(0, 255, 0), thickness); // \*左上,宽高\*

        float bbox_wid = faces.at(i, 2);

        float scale = (bbox_wid / hat.cols) * 2.0;

        Mat iHat, iHat_alpha;

        resize(hat, iHat, Size(), scale, scale);

        resize(hat_alpha, iHat_alpha, Size(), scale, scale);

        // *以bbox上边为帽子的下边缘*

        int bx = faces.at(i, 0); // \*bbox左\*

        int by = faces.at(i, 1); // \*bbox上\*

        int offset = (int)(iHat.cols / 2 - faces.at(i, 2) / 2); // \*帽子与人脸对齐所需要左移像素数\*

        int sx = bx - offset;                                          // *帽子左*

        int sy = max(0, by - iHat.rows) + (int)(iHat.rows * 0.45);     // *帽子上*

        iHat.copyTo(output(Rect(sx, sy, iHat.cols, iHat.rows)), iHat_alpha); // *(帽子左上角,帽子大小)*

        // std::cout « faces.at(i, 0) << "660左##" << faces.at(i, 1) << "90人脸高##" << faces.at(i, 2) << "80人脸宽##" << faces.at(i, 3) << "100#人脸上" << std::endl;

        // Draw landmarks

        circle(output, Point2i(int(faces.at(i, 4)), int(faces.at(i, 5))), 2, Scalar(255, 0, 0), thickness);     // \*右眼\*

        circle(output, Point2i(int(faces.at(i, 6)), int(faces.at(i, 7))), 2, Scalar(0, 0, 255), thickness);     // \*左眼\*

        circle(output, Point2i(int(faces.at(i, 8)), int(faces.at(i, 9))), 2, Scalar(0, 255, 0), thickness);     // \*鼻子\*

        circle(output, Point2i(int(faces.at(i, 10)), int(faces.at(i, 11))), 2, Scalar(255, 0, 255), thickness); // \*右嘴角\*

        circle(output, Point2i(int(faces.at(i, 12)), int(faces.at(i, 13))), 2, Scalar(0, 255, 255), thickness); // \*左嘴角\*

        // Put score

        putText(output, format(“%.4f”, faces.at(i, 14)), Point2i(int(faces.at(i, 0)), int(faces.at(i, 1)) + 15), FONT\_HERSHEY\_SIMPLEX, 0.5, Scalar(0, 255, 0)); //\*显示人脸检测精度\*

    }

    return output;

}

4.最后,用OpenGL渲染显示窗口:

// *人脸检测(in:frame ; out:frameOut)*

        frame = detect_face(frame);

        // *激活纹理*

        CreateTexture(frame.cols, frame.rows, frame.data, 0);

        // *绘制一个四边形*

        glm::mat4 model = glm::mat4(1.0f);

        // model = glm::translate(model, glm::vec3(0.7f, 3.8f, 2.0f));

        // model = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));

        // model = glm::scale(model, glm::vec3(0.1f)); // Make it a smaller cube

        ourShader.use();

        glBindVertexArray(planeVAO);

        ourShader.setMat4(“model”, model);

        glDrawArrays(GL_QUADS, 0, 4);

        glBindVertexArray(0);

        // glfw: swap buffers and poll IO events (keys pressed/released, mouse moved etc.)

        glfwSwapBuffers(window);

        glfwPollEvents();

        waitKey(1000 / fps);

5.人脸检测与帽子贴图显示:

1280*720

左上角显示视频帧率; 显示人脸关键点(双眼、双嘴角、鼻子); 将帽子贴图上去(在OpenCV中处理)

顶点着色器:

由于加载的原始纹理图片y轴翻转,所以我直接在顶点着色器内部进行一次y轴翻转,将图片摆正。

#version 330 core

layout (location = 0) in vec2 aPos;

layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

uniform mat4 model;

void main()

{

` `//TexCoord = aTexCoord;

` `TexCoord = vec2(aTexCoord.x, 1.0f-aTexCoord.y); //*1.图片翻转(修改纹理)*

` `vec4 pos = vec4(aPos,0.0f, 1.0f);

` `gl_Position = model * pos;

}

或者修改顶点:

void main()

{

` `TexCoord = aTexCoord;

` `vec4 pos = vec4(aPos.x, -aPos.y, 0.0f, 1.0f); //*2.图片翻转(修改顶点)*

` `gl_Position = model * pos;

}

片段着色器:

#version 330 core

varying vec2 TexCoord;

uniform sampler2D _texture;

void main()

{

` `vec4 color = texture2D(_texture,TexCoord);

` `gl_FragColor = color;

}

6.灰度显示、反相、卷积边缘检测:

顶点着色器与上面一样不变。

片段着色器:

#version 330 core

in vec2 TexCoord;

out vec4 FragColor;

uniform sampler2D _texture;

void main()

{

` `// 1.绿幕背景换成蓝色背景

` `vec4 texColor = texture2D(_texture, TexCoord);

` `bool bkg_R = texColor.x > 10.0/255 && texColor.x < 105.0/255;

` `bool bkg_G = texColor.y > 90.0/255 && texColor.y <= 255.0/255;

` `bool bkg_B = texColor.z > 35.0/255 && texColor.z < 160.0/255;

` `if (bkg_R && bkg_G && bkg_B)

` `texColor = vec4(0.0f, 0.0f, 0.5f, 1.0f);

` `// 2.反相(颜色反转)

` `vec4 texColor = vec4(1.0f - texture(_texture, TexCoord));

` `texColor = vec4(texColor.r, texColor.g, texColor.b, 1.0f);

` `// 3.转灰度图

` `vec4 texColor = texture(_texture, TexCoord);

` `float average = (texColor.r + texColor.g + texColor.b) / 3.0;

` `texColor = vec4(average,average,average,1.0);

` `// 4.***** 卷积核 ******

` `const float offset = 1.0/800.0; //计算卷积的偏移数组

` `vec2 OffSet[9] = vec2[](

` `vec2(-offset, offset), // 左上

` `vec2( 0.0f, offset), // 正上

` `vec2( offset, offset), // 右上

` `vec2(-offset, 0.0f), // 左

` `vec2( 0.0f, 0.0f), // 中

` `vec2( offset, 0.0f), // 右

` `vec2(-offset, -offset), // 左下

` `vec2( 0.0f, -offset), // 正下

` `vec2( offset, -offset) // 右下

` `);

` `float kernel[9] = float[]( //锐化的卷积核

` `// -1, -1, -1,

` `// -1, 9, -1,

` `// -1, -1, -1

` `1, 0, -1, //垂直边缘检测

` `1, 0, -1,

` `1, 0, -1

` `);

` `vec4 sampleTex[9];

` `for(int i=0;i<9;i++)

` `{

` `sampleTex[i] = vec4(texture2D(_texture, TexCoord.st+OffSet[i])); //获取像素点周围共9个像素

` `}

` `vec4 col = vec4(0.0f);

` `for(int i=0;i<9;i++)

` `{

` `col += sampleTex[i]*kernel[i];

` `}

` `vec4 texColor = vec4(col.r, col.g, col.b, 1.0f);

` `FragColor = texColor;

}

绿幕背景更换效果:

反相(颜色反转)效果:

转灰度图效果:

(卷积核)锐化效果:

(卷积核)垂直边缘检测:

(卷积核)水平边缘检测:

2.2 OpenGL ES滤镜效果

在OpenGL中颜色是用向量vec4中RGBA四个分量表示(取值范围0.0f-1.0f)。

###

2.2.1基本色调处理

  • 冷色调:单一增加B通道值;
  • 暖色调:增加R、G通道值。

冷色调片段着色器:

#version 330 core

in vec2 TexCoord;

out vec4 FragColor;

uniform sampler2D _texture;

void main()

{

` `vec4 texColor = texture2D(_texture, TexCoord);

` `texColor = texColor + vec4(0.0f, 0.0f, 0.2f, 0.0f); //暖色调(增加R、G通道值)

` `FragColor = texColor;

}

暖色调片段着色器:

` `texColor = texColor + vec4(0.2f, 0.2f, 0.0f, 0.0f); //暖色调(增加R、G通道值)

(冷色调,暖色调)

熔铸片段着色器:

vec4 mask = texture2D(_texture, TexCoord);

float r = mask.r * 0.5 / (mask.g + mask.b + 0.01);

float g = mask.r * 0.5 / (mask.r + mask.b + 0.01);

float b = mask.r * 0.5 / (mask.r + mask.g + 0.01);

vec4 texColor = vec4(r, g, b, 1.0);

灰度处理:

有5中方法来实现灰度滤镜的算法(前三种方法是利用权重来实现的):

  • 浮点算法: Gray = R * 0.3 + G * 0.59 + B * 0.11 (根据对应纹素的颜色值调整RGB的比例)
  • 平均值法: Gray = (R + G + B) / 3; (获取到对应纹素的RGB平均值,填充到三个通道上面)
  • 仅取绿色: Gray = G (一个颜色填充三个通道)

(浮点计算,平均值法)

(仅取绿色)

浮雕:

浮雕就是检测边缘信息,然后转灰度图,然后提亮。

浮雕片段着色器:

const highp vec3 gray = vec3(0.2125, 0.7154, 0.0721);

const vec4 bgColor = vec4(0.5, 0.5, 0.5, 1.0);

const vec2 iResolution = vec2(100.0, 100.0);

vec2 uv = TexCoord;

vec2 preUV = vec2(uv.x-0.5/iResolution.x, uv.y-0.5/iResolution.y);

vec4 currentMask = texture2D(_texture, TexCoord);

vec4 preMask = texture2D(_texture, preUV);

vec4 delColor = currentMask - preMask; // 两个图层相减得到边缘信息

float luminance = dot(delColor.rgb, gray); //转灰度(三通道浮点计算法)

vec4 texColor = vec4(vec3(luminance), 0.0) + bgColor;// 边缘检测的灰度图+背景提亮

FragColor = texColor;

(边缘 -> 灰度 -> 提亮)

###

2.2.2模糊处理

均值模糊:

多个方向纹理偏移叠加。

通过多个纹理叠加,每个纹理uv偏移量设置不同达到一点重影效果来实现模糊。

一般偏移不大于0.01,太大就是重影,不像模糊了。

vec4 texColor = texture2D(_texture, TexCoord);

float dis = 0.005; //距离越大越模糊

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x-dis, TexCoord.y+dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y-dis));

texColor+=texture2D(_texture, vec2(TexCoord.x+dis, TexCoord.y+dis));

texColor/=13.0; //周边13个颜色相加,然后取平均,作为这个点的颜色

高斯模糊(低通):

是一种典型的模糊方法,一般用于降噪、模糊处理。

类似半透明玻璃的效果。数字信号角度就是过滤高频,保留低频(低通滤波器)。

用到的高斯核,对应于2D高斯曲线值(正态分布),高斯核为:

IMG_256

越是接近中心点取值就越大。

模糊的本质就是让当前像素值和周围像素值接近,各个像素和周围像素值同时作用。

3*3高斯卷积核: 1/16∗121242121

3x3高斯核片段着色器:

const int KernelSize = 9; // 3x3=9(2)高斯模糊

const float fStep = 0.003; //3x3的卷积核设置太大时,会像重影,效果不如5x5的卷积核

vec2 uv = TexCoord;

//用来存储3x3的卷积矩阵

float Kernel[KernelSize] = float[](

` `1.0, 2.0, 1.0, //3X3卷积核

` `2.0, 4.0, 2.0,

` `1.0, 2.0, 1.0

);

vec2 Offset[KernelSize] = vec2[]( //像素点偏移位置

` `vec2(-fStep,-fStep), vec2(0.0,-fStep), vec2(fStep,-fStep),

` `vec2(-fStep,0.0), vec2(0.0,0.0), vec2(fStep,0.0),

` `vec2(-fStep, fStep), vec2(0.0, fStep), vec2(fStep, fStep)

);

vec4 sum = vec4(0.0);

for (int i = 0; i < KernelSize; i++)

{

` `vec4 tmp = texture2D(_texture, uv + Offset[i]);

` `sum += tmp * Kernel[i]/16.0;

}

vec4 texColor = sum;

(3*3高斯核步长0.003, 步长0.005)步长太大时,会出现重影,3*3高斯核效果不太好。

5*5高斯卷积核

5x5高斯核片段着色器:

const int KernelSize = 25; // 5x5=25(2)高斯模糊

const float fStep = 0.003; //3x3的卷积核设置太大时,会像重影,效果不如5x5的卷积核

vec2 uv = TexCoord;

//用来存储3x3的卷积矩阵

float Kernel[KernelSize] = float[](

` `1.0, 4.0, 6.0, 4.0, 1.0, //5X5卷积核

` `4.0, 16.0, 24.0, 16.0, 4.0,

` `6.0, 24.0, 36.0, 24.0, 6.0,

` `4.0, 16.0, 24.0, 16.0, 4.0,

` `1.0, 4.0, 6.0, 4.0, 1.0

);

vec2 Offset[KernelSize] = vec2[]( //像素点偏移位置

ec2(-2.0*fStep,-2.0*fStep), vec2(-fStep,-2.0*fStep), vec2(0.0,-2.0*fStep), vec2(fStep,-2.0*fStep), ec2(2.0*fStep,-2.0*fStep),

` `vec2(-2.0*fStep,-1.0*fStep), vec2(-fStep,-1.0*fStep), vec2(0.0,-1.0*fStep), ec2(fStep,-1.0*fStep), vec2(2.0*fStep,-1.0*fStep),

` `vec2(-2.0*fStep,0.0), vec2(-fStep,0.0), vec2(0.0,0.0), ec2(fStep,0.0), vec2(2.0*fStep,0.0),

` `vec2(-2.0*fStep,1.0*fStep), vec2(-fStep,1.0*fStep), vec2(0.0,1.0*fStep), ec2(fStep,1.0*fStep), vec2(2.0*fStep,1.0*fStep),

` `vec2(-2.0*fStep,2.0*fStep), vec2(-fStep,2.0*fStep), vec2(0.0,2.0*fStep), ec2(fStep,2.0*fStep), vec2(2.0*fStep,2.0*fStep)

);

vec4 sum = vec4(0.0);

for (int i = 0; i < KernelSize; i++)

{

` `vec4 tmp = texture2D(_texture, uv + Offset[i]);

` `sum += tmp * Kernel[i]/256.0;

}

vec4 texColor = sum;

(5*5高斯核步长0.003, 步长0.005)步长太大时,5*5高斯核效果比3*3效果好。

2.2.3马赛克

马赛克处理我分为正方形、六边形、三角形的马赛克。

正方形马赛克:

把图片的一个相当大小的区域用同一个颜色值来表示。

滤镜算法主要有以下几步:

  • 根据纹理坐标计算实际图像中的位置,相当于将纹理颜色区放大;
  • 计算出一个小马赛克的坐标,即找到马赛克提取颜色值的像素点;
  • 将马赛克坐标换算回纹理坐标,即将纹理颜色区缩小。

IMG_256

片段着色器:

const vec2 TexSize = vec2(600.0, 600.0); // 图片size(1)正方形马赛克

const vec2 mosaicSize = vec2(4.0, 4.0);

vec2 intXY = vec2(TexCoord.x*TexSize.x, TexCoord.y*TexSize.y);//计算图像的实际位置

vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);

vec2 UVMosaic = vec2(XYMosaic.x/TexSize.x, XYMosaic.y/TexSize.y);//换算回纹理坐标(0.0-1.0)

vec4 texColor = texture2D(_texture, UVMosaic);

六边形马赛克:

将一张图片,分割成由六边形组成,再取每个六边形的重点画出一个个的矩形,根据矩形的奇偶排列情况求出对应的2个中心点,并计算纹理坐标与两个中心点的距离,根据距离判断,采取就近原则,当前的六边形就采用近的中心点的颜色值。

IMG_256

首先设置矩形的长宽比例:

TB长:3 TR宽:√3

IMG_256

根据纹理坐标,计算矩阵坐标:

wx: 3*len wy: √3*len

IMG_256

根据行列奇偶情况,获取六边形中心点坐标:

IMG_256

然后计算矩形四个顶点坐标(六边形中心点坐标):1.5改为3

IMG_256

然后计算当前片元距两个中心点的距离:

s1 = √((v1.x-x)² + (v1.y-y)²)

s2 = √((v2.x-x)² + (v2.y-y)²)

IMG_256

片段着色器:

float length = 0.015; //马赛克大小 (2)六边形马赛克

float TR = 0.866025; // √3/2

float x = TexCoord.x;

float y = TexCoord.y;

int wx = int(x / 1.5 / length);

int wy = int(y / TR / length);

vec2 v1, v2, vn;

if (wx/2 * 2 == wx) {

` `if (wy/2 * 2 == wy) {

` `//(0,0),(1,1)

` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));

` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

` `} else {

` `//(0,1),(1,0)

` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));

` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

` `}

}else {

` `if (wy/2 * 2 == wy) {

` `//(0,1),(1,0)

` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy + 1));

` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy));

` `} else {

` `//(0,0),(1,1)

` `v1 = vec2(length * 1.5 * float(wx), length * TR * float(wy));

` `v2 = vec2(length * 1.5 * float(wx + 1), length * TR * float(wy + 1));

` `}

}

float s1 = sqrt(pow(v1.x - x, 2.0) + pow(v1.y - y, 2.0));

float s2 = sqrt(pow(v2.x - x, 2.0) + pow(v2.y - y, 2.0));

if (s1 < s2) {

` `vn = v1;

} else {

` `vn = v2;

}

vec4 texColor = texture2D(_texture, vn);

三角形马赛克:

是基于六边形,将六边形6等分,得到6个正三角形。然后求每个三角形中心点,根据夹角判断属于哪个三角形,就将该三角形中心点颜色作为当前三角形颜色。

首先求夹角:

IMG_256

计算6个三角形中心点:

IMG_256

根据夹角判断属于哪个三角形:

IMG_256

顶点着色器部分:(省略六边形部分)

vec4 mid = texture2D(_texture, vn);

float a = atan((x - vn.x)/(y - vn.y)); //计算夹角

vec2 area1 = vec2(vn.x, vn.y - mosaicSize * TR / 2.0);

vec2 area2 = vec2(vn.x + mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);

vec2 area3 = vec2(vn.x + mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);

vec2 area4 = vec2(vn.x, vn.y + mosaicSize * TR / 2.0);

vec2 area5 = vec2(vn.x - mosaicSize / 2.0, vn.y + mosaicSize * TR / 2.0);

vec2 area6 = vec2(vn.x - mosaicSize / 2.0, vn.y - mosaicSize * TR / 2.0);

if (a >= PI6 && a < PI6 * 3.0) {

` `vn = area1;

} else if (a >= PI6 * 3.0 && a < PI6 * 5.0) {

` `vn = area2;

} else if ((a >= PI6 * 5.0 && a <= PI6 * 6.0)   (a < -PI6 * 5.0 && a > -PI6 * 6.0)) {

` `vn = area3;

} else if (a < -PI6 * 3.0 && a >= -PI6 * 5.0) {

` `vn = area4;

} else if(a <= -PI6 && a> -PI6 * 3.0) {

` `vn = area5;

} else if (a > -PI6 && a < PI6) {

` `vn = area6;

}

vec4 texColor;

if(gl_FragCoord.y > 250.0)

` `texColor = texture2D(_texture, vn);

else

` `texColor = texture2D(_texture, TexCoord);

###

2.2.4多分镜处理

多分镜的原理类似,具体参照下面的示意图。

二分屏

IMG_256

片段着色器部分:

vec2 uv = TexCoord; //(1)左右二分镜(不能在原TexCoord上修改)

float x;

if(uv.x>=0.0 && uv.x<=0.5)

` `x = uv.x + 0.25;

else

` `x = uv.x - 0.25;

vec4 texColor = texture2D(_texture, vec2(x,uv.y));

vec2 uv = TexCoord; //(1)上下二分镜(不能在原TexCoord上修改)

float y;

if(uv.y>=0.0 && uv.y<=0.5)

` `y = uv.y + 0.25;

else

` `y = uv.y - 0.25;

vec4 texColor = texture2D(_texture, vec2(uv.x,y));

三分屏

IMG_256

片段着色器部分:

vec2 uv = TexCoord; //(2)左右三分镜(不能在原TexCoord上修改)

float x;

if(uv.x>=0.0 && uv.x<=1.0/3.0)

` `x = uv.x + 1.0/3.0;

else if(uv.x>1.0/3.0 && uv.x<=2.0/3.0)

` `x = uv.x;

else

` `x = uv.x - 1.0/3.0;

vec4 texColor = texture2D(_texture, vec2(x,uv.y));

vec2 uv = TexCoord; //(2)上下三分镜(不能在原TexCoord上修改)

float y;

if(uv.y>=0.0 && uv.y<=1.0/3.0)

` `y = uv.y + 1.0/3.0;

else if(uv.y>1.0/3.0 && uv.y<=2.0/3.0)

` `y = uv.y;

else

` `y = uv.y - 1.0/3.0;

vec4 texColor = texture2D(_texture, vec2(uv.x,y));

四分屏

IMG_256

片段着色器部分:

vec2 uv = TexCoord; //(3)四分镜(不能在原TexCoord上修改)

if(uv.x<=0.5)

` `uv.x = uv.x * 2.0;

else

` `uv.x = (uv.x - 0.5) * 2.0;

if(uv.y<=0.5)

` `uv.y = uv.y * 2.0;

else

` `uv.y = (uv.y - 0.5) * 2.0;

vec4 texColor = texture2D(_texture, uv);

2.2.5旋涡

在一定半径范围内,将采样点旋转一定角度,如果旋转时,旋转角随着半径距离递减,就会出现旋涡效果。

片段着色器部分:

const float PI = 3.14159265;

const float rotateRadian = PI / 3.0;

const float radiusRatio = 0.8; // 旋涡半径缩小率

const float center = 0.5; //纹理坐标中心(范围0.0-1.0)

const vec2 iResolution = vec2(100.0, 100.0);

float radius = min(iResolution.x, iResolution.y) * radiusRatio / 2.0; // 计算旋涡半径

vec2 uv = TexCoord; // (不能在原TexCoord上修改)

uv.x *= iResolution.x;

uv.y *= iResolution.y;

vec2 centerUV = iResolution.xy * center;

vec2 deltaUV = uv - centerUV;

float deltaR = length(deltaUV);

float beta = atan(deltaUV.y, deltaUV.x) + rotateRadian * 2.0 * (1.0-(deltaR / radius) * (deltaR / radius));//抛物线递减因子

vec2 dstUV = uv;

if (deltaR <= radius)

{

` `dstUV = centerUV + deltaR * vec2(cos(beta), sin(beta));

}

dstUV.x /= iResolution.x;

dstUV.y /= iResolution.y;

vec4 texColor = texture2D(_texture, dstUV);

2.2.6边缘检测

垂直边缘检测Sobel算子: 10−120−210−1

水平边缘检测Sobel算子: 121000−1−2−1

卷积核的竖直与水平边缘检测:

#version 330 core

in vec2 TexCoord;

out vec4 FragColor;

uniform sampler2D _texture;

void main()

{

` `// 4.***** 卷积核 ******

` `const float offset = 1.0/800.0; //计算卷积的偏移数组

` `vec2 OffSet[9] = vec2[](

` `vec2(-offset, offset), // 左上

` `vec2( 0.0f, offset), // 正上

` `vec2( offset, offset), // 右上

` `vec2(-offset, 0.0f), // 左

` `vec2( 0.0f, 0.0f), // 中

` `vec2( offset, 0.0f), // 右

` `vec2(-offset, -offset), // 左下

` `vec2( 0.0f, -offset), // 正下

` `vec2( offset, -offset) // 右下

` `);

` `float kernel[9] = float[]( //锐化的卷积核

` `// -1, -1, -1,

` `// -1, 9, -1,

` `// -1, -1, -1

` `1, 0, -1, //垂直边缘检测

` `2, 0, -2,

` `1, 0, -1

` `// 1, 2, 1, //水平边缘检测

` `// 0, 0, 0,

` `// -1,-2,-1

);

` `vec4 sampleTex[9];

` `for(int i=0;i<9;i++)

` `{

` `sampleTex[i] = vec4(texture2D(_texture, TexCoord.st+OffSet[i])); //获取像素点周围共9个像素

` `}

` `vec4 col = vec4(0.0f);

` `for(int i=0;i<9;i++)

` `{

` `col += sampleTex[i]*kernel[i];

` `}

` `vec4 texColor = vec4(col.r, col.g, col.b, 1.0f);

` `FragColor = texColor;

}

(竖直边缘, 水平边缘)

计算梯度边缘检测:

vec4 color = texture2D(_texture, TexCoord);

float len = length(color.rgb); // 纹理距原点距离

vec4 texColor = vec4(vec3(step(0.06, length(vec2(dFdx(len), dFdy(len))))), 1.0); //计算梯度(梯度>0.06赋值1)

2.2.7水波纹

片段着色器部分:

float stongth = 0.1;

vec2 uv = TexCoord.xy;

float waveX = sin((uv.y + iGlobalTime) * 10.0) * 0.5 * 0.05 * stongth;

float waveY = sin((uv.x + iGlobalTime) * 10.0) * 0.5 * 0.05 * stongth;

// vec4 texColor = texture2D(_texture, uv + vec2(waveX, 0.0)); //水平水波(不能在TexCoord上加减)

vec4 texColor = texture2D(_texture, uv + vec2(0.0, waveY)); //竖直水波

##

2.2 美颜效果

  • 磨皮度、磨皮值:本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊.
  • 美白:本质就是提高亮度.
  • 红润:本质就是改变色调.

美颜参数值调整:

使用params向量接收美颜磨皮度、色调四个分量,用来调整片段着色器部分。

/**

` `* @brief *美颜*

` `* @param ourShader

` `* 磨皮度、磨皮值:本质就是让像素点模糊,可以使用高斯模糊,但是可能导致边缘会不清晰,用双边滤波(Bilateral Filter) ,有针对性的模糊像素点,能保证边缘不被模糊.

` `* 美白:本质就是提高亮度.

` `* 红润:本质就是改变色调.

` `*/

void Beauty(Shader *ourShader)

{

` `float beautyLevel = 1.2f; // *磨皮度*

` `float toneLevel = -0.5f; // *色调*

` `float brightness = 0.1f; // *亮度* 0.47f;

` `float texelWidthOffset = 2.0f;

` `float texelHeightOffset = 2.0f;

` `brightness = sin((float)glfwGetTime() * 3.0) * 0.5; //(-0.5,0.5)

` `// toneLevel = sin((float)glfwGetTime()*3.0)*1.0; //(-1.0,1.0)

` `// beautyLevel = (sin((float)glfwGetTime()*3.0)+1.0)*1.5; //(0.0,3.0)

` `cout«“beautyLevel:”«beautyLevel«”, brightness:”«brightness«”, toneLevel:”«toneLevel«endl;

` `glm::vec4 params = glm::vec4(0.4f + beautyLevel, 0.7f + beautyLevel, 0.4f + toneLevel, 0.4f + toneLevel);

` `ourShader->setVec4(“params”, params);

` `ourShader->setFloat(“brightness”, brightness);

` `ourShader->setFloat(“texelWidthOffset”, texelWidthOffset);

` `ourShader->setFloat(“texelHeightOffset”, texelHeightOffset);

}

片段着色器部分:

#version 330 core

in vec2 TexCoord;

out vec4 FragColor;

uniform sampler2D _texture;

const vec2 singleStepOffset = vec2(2.0f / 1280.0f, 2.0f / 720.0f);

// params:(0.4f+beautyLevel, 0.7f+beautyLevel, 0.4f+toneLevel,0.4f+toneLevel)

uniform vec4 params; // toneLevel:-0.5f(-5.0f,5.0f), beautyLevel:1.2f(0.0f,2.5f)

uniform float brightness; //美白亮度0.47f(0.0f,1.0f)

uniform float texelWidthOffset; // 2.0f(-10.0f,10.0f)

uniform float texelHeightOffset; // 2.0f(-10.0f,10.0f)

const vec3 W = vec3(0.299, 0.587, 0.114);

const mat3 saturateMatrix = mat3(

` `1.1102, -0.0598, -0.061,

` `-0.0774, 1.0826, -0.1186,

` `-0.0228, -0.0228, 1.1772);

vec2 blurCoordinates[24];

float hardLight(float color)

{

` `if (color <= 0.5)

` `color = color * color * 2.0;

` `else

` `color = 1.0 - ((1.0 - color) * (1.0 - color) * 2.0);

` `return color;

}

void main()

{

` `vec3 centralColor = texture2D(_texture, TexCoord).rgb;

` `vec2 singleStepOffset = vec2(texelWidthOffset, texelHeightOffset);

` `blurCoordinates[0] = TexCoord.xy + singleStepOffset * vec2(0.0, -10.0);

` `blurCoordinates[1] = TexCoord.xy + singleStepOffset * vec2(0.0, 10.0);

` `blurCoordinates[2] = TexCoord.xy + singleStepOffset * vec2(-10.0, 0.0);

` `blurCoordinates[3] = TexCoord.xy + singleStepOffset * vec2(10.0, 0.0);

` `blurCoordinates[4] = TexCoord.xy + singleStepOffset * vec2(5.0, -8.0);

` `blurCoordinates[5] = TexCoord.xy + singleStepOffset * vec2(5.0, 8.0);

` `blurCoordinates[6] = TexCoord.xy + singleStepOffset * vec2(-5.0, 8.0);

` `blurCoordinates[7] = TexCoord.xy + singleStepOffset * vec2(-5.0, -8.0);

` `blurCoordinates[8] = TexCoord.xy + singleStepOffset * vec2(8.0, -5.0);

` `blurCoordinates[9] = TexCoord.xy + singleStepOffset * vec2(8.0, 5.0);

` `blurCoordinates[10] = TexCoord.xy + singleStepOffset * vec2(-8.0, 5.0);

` `blurCoordinates[11] = TexCoord.xy + singleStepOffset * vec2(-8.0, -5.0);

` `blurCoordinates[12] = TexCoord.xy + singleStepOffset * vec2(0.0, -6.0);

` `blurCoordinates[13] = TexCoord.xy + singleStepOffset * vec2(0.0, 6.0);

` `blurCoordinates[14] = TexCoord.xy + singleStepOffset * vec2(6.0, 0.0);

` `blurCoordinates[15] = TexCoord.xy + singleStepOffset * vec2(-6.0, 0.0);

` `blurCoordinates[16] = TexCoord.xy + singleStepOffset * vec2(-4.0, -4.0);

` `blurCoordinates[17] = TexCoord.xy + singleStepOffset * vec2(-4.0, 4.0);

` `blurCoordinates[18] = TexCoord.xy + singleStepOffset * vec2(4.0, -4.0);

` `blurCoordinates[19] = TexCoord.xy + singleStepOffset * vec2(4.0, 4.0);

` `blurCoordinates[20] = TexCoord.xy + singleStepOffset * vec2(-2.0, -2.0);

` `blurCoordinates[21] = TexCoord.xy + singleStepOffset * vec2(-2.0, 2.0);

` `blurCoordinates[22] = TexCoord.xy + singleStepOffset * vec2(2.0, -2.0);

` `blurCoordinates[23] = TexCoord.xy + singleStepOffset * vec2(2.0, 2.0);

` `float sampleColor = centralColor.g * 22.0;

` `sampleColor += texture2D(_texture, blurCoordinates[0]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[1]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[2]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[3]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[4]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[5]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[6]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[7]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[8]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[9]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[10]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[11]).g;

` `sampleColor += texture2D(_texture, blurCoordinates[12]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[13]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[14]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[15]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[16]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[17]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[18]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[19]).g * 2.0;

` `sampleColor += texture2D(_texture, blurCoordinates[20]).g * 3.0;

` `sampleColor += texture2D(_texture, blurCoordinates[21]).g * 3.0;

` `sampleColor += texture2D(_texture, blurCoordinates[22]).g * 3.0;

` `sampleColor += texture2D(_texture, blurCoordinates[23]).g * 3.0;

` `sampleColor = sampleColor / 62.0;

` `float ass = centralColor.g - sampleColor + 0.5;

` `for (int i = 0; i < 5; i++)

` `{

` `ass = hardLight(ass);

` `}

` `float lumance = dot(centralColor, W);

` `float alpha = pow(lumance, params.r);

` `vec3 smoothColor = centralColor + (centralColor - vec3(ass)) * alpha * 0.1;

` `smoothColor.r = clamp(pow(smoothColor.r, params.g), 0.0, 1.0);

` `smoothColor.g = clamp(pow(smoothColor.g, params.g), 0.0, 1.0);

` `smoothColor.b = clamp(pow(smoothColor.b, params.g), 0.0, 1.0);

` `vec3 lvse = vec3(1.0) - (vec3(1.0) - smoothColor) * (vec3(1.0) - centralColor);

` `vec3 bianliang = max(smoothColor, centralColor);

` `vec3 rouguang = 2.0 * centralColor * smoothColor + centralColor * centralColor - 2.0 * centralColor * centralColor * smoothColor;

` `vec4 texColor = vec4(mix(centralColor, lvse, alpha), 1.0);

` `texColor.rgb = mix(texColor.rgb, bianliang, alpha);

` `texColor.rgb = mix(texColor.rgb, rouguang, params.b);

` `vec3 satcolor = texColor.rgb * saturateMatrix;

` `texColor.rgb = mix(texColor.rgb, satcolor, params.a);

` `texColor.rgb = vec3(texColor.rgb + vec3(brightness));

` `FragColor = vec4(texColor.r / 1.0, texColor.g / 1.0, texColor.b / 1.0, texColor.a / 1.0);

}

亮度调整:

色调调整:

美颜磨皮调整:

3.OpenGL ES 3.0 in Android

本工程在Android studio 2021 3.1,ndk 为android-ndk-r21e。

使用库:

ncnn + opencv3 + mnn + glm + opengles 3

其中ncnn为新版基于Vulkan加速的推理框架ncnnVulkan;OpenCV主要获取Mat矩阵;mnn为另一种模型推理框架;glm为配合OpenGL的向量库。

3.1 OpenGL ES工程

1. java/com/example/cameraspecialeffect/MainActivity.java

修改滤镜弹出框显示种类final String items[]

  1. cpp/CMakeLists.txt:

    添加库:add_library

  2. cpp/native-lib.cpp:

    添加新的头文件#include;

    修改滤镜渲染函数:Java_com_example_cameraspecialeffect_GLRenderJNI_setRenderTypeJNI

  3. 修改对应添加新cpp与h的include与类名。#idndef

    添加GLSL

  4. cpp/native-lib.cpp:

    如果是BaseVideoRender改的,添加include新,对应的设置asset、g_render。

    回想起自己的这五个半月的工作经历,虽然有眼泪也有辛酸,但最多的还是历练与收获。记得刚到公司的第一天,我被安排在一个办公室文员身边学习。真的,第一天是最难熬的,从学校到社会的大环境的转变,身边接触的人也完全换了主角,老师变成老板,同学变成同事,相处之道完全不同,大家把你当成隐形人的感受只有亲身经历过的人才能体会。“实践是检验真理的唯一标准”从学校到社会的大环境的巨大的转变中,我们可能彷徨,迷茫,无法立刻适应新的环境。我们也许看不惯人与人之间的勾心斗角,无法忍受同事之间漠不关心的眼神和言语。很多时候觉得自己没有受到领导重用,所干的只是一些无关重要的杂活,自己的提议或工作不能得到领导的肯定。做不出成绩时,会有来自各方面的压力,领导的眼色同事的嘲讽。失落时的无奈想找人诉说是却发现没有人能够倾诉。而在学校,有同学老师的关心和支持,每日只是上上课,很简单。谚语有云:“纸上得来终觉浅,绝知此事要躬行。”

    两个月的实习时间虽然不长,但是我从中学到了很多知识,关于做人,做事,做学问。幸好宿舍的女孩子们对我很好,告诉我他们刚出来工作的经历,也是从被当成隐形人开始的,从打杂的过来的,劝我要挺得住,坚持走过来就好了。我刚开始接触的工作就是招聘,我自己还是一个刚刚走上工作岗位的新手,却要去招人,有点紧张也有点兴奋。我们公司是以模具为主的,有很多有关模具的专有名词是我以前从来没有接触过的,所以也就有很多我不熟悉的工种,时间久了有关模具的知识,听的多了,问的多了,也自然就明白一些了。

    大家上午好,我是中央研究院-系统开发二部-图像算法开发组陈帅,在入职的这段时间里,真的是收获很多,提升很多。

    从学校到社会的大环境的巨大的转变中,我在来公司之前会有很多顾虑,觉得我们可能彷徨,迷茫,无法立刻适应新的环境。但来了昊一源之后,便全部打消了这些顾虑。

    从一开始的军训活动,我看到了我们2022届源动力的无限活力、动力、毅力以及创造力,使得我们逐渐认识彼此、认识公司、也更加清楚地认识自己。再到后来的工厂实习,感悟到我们公司对待产品的严谨态度、各个部门之间的通力配合,就像谚语所说“纸上得来终觉浅,绝知此事要躬行”,不但使自己完成了从学校学生到公司一员的转换,更体会了我们公司最近的快速发展与不断壮大。

    实现OpenGL中坐标系统的变换,完成多光源光照渲染,加载渲染3D模型,实现gamma校正,渲染多种滤镜效果,渲染处理视频绿幕抠图。

    3.2绿幕抠图滤镜

    原理:

    1。计算当前像素点RGB值对应的HSV值;

    2。设定HSV三个分量的权重,根据权重计算当前像素点的HSV值到给定背景色的HSV值的欧式距离;

    3。将欧式距离用smoothstep做平滑,0.5以下的一定要滤掉;

    4。将原图和背景图用平滑值混合。

    HSV基本颜色分量范围:

    IMG_256

    片段着色器部分:

    ` `vec2 RGBtoUV(vec3 rgb)

    ` `{

    ` `return vec2

    ` `(

    ` `rgb.r * -0.169 + rgb.g * -0.331 + rgb.b * 0.5 + 0.5,

    ` `rgb.r * 0.5 + rgb.g * -0.419 + rgb.b * -0.081 + 0.5

    ` `);

    ` `}

    ` `vec4 ProcessChromaKey(vec2 texCoord)

    ` `{

    ` `vec4 rgba = texture2D(tex, texCoord);

    ` `float chromaDist = distance(RGBtoUV(texture2D(tex, texCoord).rgb), RGBtoUV(keyColor));

    ` `float baseMask = chromaDist - similarity;

    ` `float fullMask = pow(clamp(baseMask / smoothness, 0., 1.), 1.5);

    ` `rgba.a = fullMask;

    ` `float spillVal = pow(clamp(baseMask / spill, 0., 1.), 1.5);

    ` `float desat = clamp(rgba.r * 0.2126 + rgba.g * 0.7152 + rgba.b * 0.0722, 0., 1.);

    ` `rgba.rgb = mix(vec3(desat, desat, desat), rgba.rgb, spillVal);

    ` `return rgba;

    ` `}

    varying highp vec2 vCoordinate;

    uniform sampler2D uVideoframe;

    uniform highp mat4 uMVPMatrix;

    void main()

    {

    ` `// lookup the color of the texel corresponding to the fragment being

    ` `// generated while rendering a triangle

    ` `lowp vec4 tempColor = texture2D(uVideoframe, vCoordinate);

    ` `// Calculate the average intensity of the texel’s red and blue components

    ` `lowp float rbAverage = tempColor.r * 0.5 + tempColor.b * 0.5;

    ` `// Calculate the difference between the green element intensity and the

    ` `// average of red and blue intensities

    ` `lowp float gDelta = tempColor.g - rbAverage;

    ` `// If the green intensity is greater than the average of red and blue

    ` `// intensities, calculate a transparency value in the range 0.0 to 1.0

    ` `// based on how much more intense the green element is

    ` `tempColor.a = 1.0 - smoothstep(0.0, 0.25, gDelta);

    ` `// Use the cube of the of the transparency value. That way, a fragment that

    ` `// is partially translucent becomes even more translucent. This sharpens

    ` `// the final result by avoiding almost but not quite opaque fragments that

    ` `// tend to form halos at color boundaries.

    ` `tempColor.a = tempColor.a * tempColor.a * tempColor.a;

    ` `gl_FragColor = tempColor;

    }

    完整改写:

    // * 相机纹理 *

    ` `lowp vec4 tempColor = vec4(rgb, 1.0);

    ` `// * 计算纹素的红色和蓝色分量的平均强度 *

    ` `lowp float rbAverage = tempColor.r * 0.5 + tempColor.b * 0.5;

    ` `// * 计算绿色元素强度与红蓝色强度平均值之间的差 *

    ` `lowp float gDelta = tempColor.g * (0.45*green_strenth+0.55) - rbAverage;

    ` `// * 如果绿色强度大于红色和蓝色强度的平均值,则根据绿色元素的强度计算 0.0 到 1.0 范围内的透明度值 *

    ` `tempColor.a = 1.0 - smoothstep(0.0, 0.25, gDelta); // 1.0-0.75

    ` `// * 使用透明度值的多次幂。这样,部分透明的片段变得更加透明。这通过避免几乎但不完全不透明的碎片来锐化最终结果,这些碎片倾向于在颜色边界处形成光晕*

    ` `tempColor.a = tempColor.a * tempColor.a * tempColor.a;

    ` `// *大海*

    ` `vec4 back = texture(back_tex, TexCoord);

    ` `vec3 result_mix;

    ` `result_mix = tempColor.rgb * (tempColor.a) + back.rgb * (1.0-tempColor.a);

    ` `out_color = vec4(result_mix,1.0);

    3.3人像外灰色

    参考片段着色器部分:

    #version 300 es

    precision mediump float;

    in vec2 v_texCoord;

    layout(location = 0) out vec4 outColor;

    uniform sampler2D u_texture0;//rgba相机图

    uniform sampler2D u_texture1;//人像

    uniform int u_renderType;

    uniform float u_offset;

    void main()

    {

    ` `float gray = texture(u_texture1, v_texCoord).r;//人像

    ` `vec4 rgba = texture(u_texture0, v_texCoord); // *相机图*

    ` `if(gray > 0.01) { // *人物部分*

    ` `outColor = rgba; // *相机原图*

    ` `}

    ` `else

    ` `{

    ` `float Y = 0.299 * rgba.r + 0.587 * rgba.g + 0.114 * rgba.b;

    ` `vec4 grayColor = vec4(vec3(Y), 1.0); //RGB 灰度化

    ` `outColor = mix(grayColor, rgba, u_offset); //混合渐变

    ` `}

    YUV转RGB:

    IMG_256

    改写片段着色器部分:

    #version 300 es

    layout(binding = 0) uniform sampler2D y_samp;

    layout(binding = 1) uniform sampler2D u_samp;

    layout(binding = 2) uniform sampler2D v_samp;

    layout(binding = 3) uniform sampler2D alpha_tex;

    in vec2 TexCoord;

    out vec4 out_color;

    // ************************************************************************************************

    // *YUV转RGBA*

    vec4 YUV2RGBA(vec2 Coord)

    {

    ` `vec3 rgb = mat3(1.0, 1.0, 1.0,

    ` `0, -0.344, 1.770,

    ` `1.403, -0.714, 0)

    ` `* vec3(texture(y_samp, Coord).r,

    ` `texture(u_samp, Coord).r - 0.5,

    ` `texture(v_samp, Coord).r - 0.5);

    ` `return vec4(rgb, 1.0);

    }

    // *转灰度(平均值法)*

    vec4 RGB2GREY(vec3 RGB)

    {

    ` `float average = (RGB.r + RGB.g + RGB.b)/3.0;

    ` `return vec4(average, average, average, 1.0);

    }

    // ************************************************************************************************

    void main()

    {

    ` `// *原图*

    ` `vec4 texColor_origin = YUV2RGBA(TexCoord);

    ` `// *转灰度(2)平均值法*

    ` `vec4 texColor_grey = RGB2GREY(texColor_origin.rgb);

    ` `// *人像alpha通道*

    ` `vec4 alpha = texture(alpha_tex, TexCoord);

    ` `// 过滤掉不太确定的地方

    ` `if(alpha.r < 0.12)

    ` `alpha.r = 0.0;

    ` `// *2. 人像部分添加灰色背景*

    ` `if(alpha.r != 0.0)

    ` `{

    ` `texColor_grey.rgb = texColor_origin.rgb;

    ` `}

    ` `// *3. 输出*

    ` `out_color = texColor_grey;

    }

    问题一:

    (用uniform传入的Y、U、V纹理与传入的人像分割纹理时间存在延时)

    解决:

    将YUV改为模型推理时用的输入Mat :src_mat。

    // 初始化纹理(fore)

    ` `glGenTextures(1, &fore_tex_id);

    ` `glBindTexture(GL_TEXTURE_2D, fore_tex_id);

    ` `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    ` `glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

    ` `glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);

    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glActiveTexture(GL_TEXTURE4);

    ` `glBindTexture(GL_TEXTURE_2D, fore_tex_id);

    ` `glTexImage2D(GL_TEXTURE_2D, 0, GL_R32F, src_mat.cols, src_mat.rows, 0, GL_RED, GL_FLOAT, src_mat.data);

    问题二:

    (前后景颜色分割边缘明显)

    解决:

    应改为前后景alpha通道的mix融合,而不是简单判断if(alpha.r != 0.0),去替换人像彩色部分。

    ` `// *原图*

    ` `vec4 texColor_origin = texture(src_mat, TexCoord); // *改为推理时的输入src_mat,避免人像与背景延时大问题*

    ` `texColor_origin = vec4(texColor_origin.b,texColor_origin.g,texColor_origin.r,texColor_origin.a); // RGBA->BGRA

    ` `// *转灰度(2)平均值法*

    ` `vec4 texColor_grey = RGB2GREY(texColor_origin.rgb);

    ` `// *人像alpha通道*

    ` `vec4 alpha = texture(alpha_tex, TexCoord);

    ` `// 过滤掉不太确定的地方(别过滤了,保持平滑mix效果更好)

    // if(alpha.r < 0.12)

    // alpha.r = 0.0;

    ` `// *2. 人像部分添加灰色背景*

    ` `// *(别简单判断融合了,通过alpha渐变融合效果更好)*

    ` `vec3 result_mix = mix(texColor_grey.rgb, texColor_origin.rgb, alpha.r);

    ` `// *3. 输出*

    ` `out_color = vec4(result_mix,1.0);

    3.4脸部马赛克

    主要是基于人脸检测模型推理,

    获得人脸的左下与右上的坐标点位置:

    X1Y1:(lines[max_area_id *8 + 6], lines[max_area_id *8 + 7])

    X2Y2:(lines[max_area_id *8 + 2], lines[max_area_id *8 + 3])

    然后通过uniform传坐标数据至着色器:

    // 发送数据

    ` `glUniform2f(x1y1_loc, lines[max_area_id *8 + 6], lines[max_area_id *8 + 7]);

    ` `glUniform2f(x2y2_loc, lines[max_area_id *8 + 2], lines[max_area_id *8 + 3]);

    在着色器部分:

    #version 300 es

    layout(binding = 0) uniform sampler2D y_samp;

    layout(binding = 1) uniform sampler2D u_samp;

    layout(binding = 2) uniform sampler2D v_samp;

    uniform vec2 x1y1;

    uniform vec2 x2y2;

    uniform int has_face; // 是否有人脸

    uniform vec2 img_size;// 图像尺寸

    in vec2 TexCoord;

    out vec4 out_color;

    vec4 YUV2RGB(vec2 Coord)

    {

    ` `vec3 rgb = mat3(1.0, 1.0, 1.0,

    ` `0, -0.344, 1.770,

    ` `1.403, -0.714, 0)

    ` `* vec3(texture(y_samp, Coord).r,

    ` `texture(u_samp, Coord).r - 0.5,

    ` `texture(v_samp, Coord).r - 0.5);

    ` `return vec4(rgb, 1.0);

    }

    void main()

    {

    ` `vec4 texColor1 = YUV2RGB(TexCoord);

    ` `vec2 mosaicSize = vec2(48.0, 48.0);//马赛克大小

    ` `vec2 intXY = vec2(TexCoord.x*img_size.x, TexCoord.y*img_size.y);//计算图像的实际位置

    ` `vec2 XYMosaic = vec2(floor(intXY.x/mosaicSize.x)*mosaicSize.x, floor(intXY.y/mosaicSize.y)*mosaicSize.y);

    ` `vec2 UVMosaic = vec2(XYMosaic.x/img_size.x, XYMosaic.y/img_size.y);//换算回纹理坐标(0.0-1.0)

    ` `vec4 texColor2 = YUV2RGB(UVMosaic);

    ` `// * 人脸范围 & 人脸数>0 *

    ` `if(has_face>0 && (gl_FragCoord.x > (x1y1.x+1.0)*0.5*img_size.x && gl_FragCoord.x < (x2y2.x+1.0)*0.5*img_size.x) && (gl_FragCoord.y>(x1y1.y+1.0)*0.5*img_size.y && gl_FragCoord.y<(x2y2.y+1.0)*0.5*img_size.y))

    // if(gl_FragCoord.x < 1400.0/2.0 && gl_FragCoord.y < 1800.0/2.0)

    ` `{

    ` `// *马赛克*

    ` `out_color = texColor2;

    ` `}

    ` `else

    ` `{

    ` `// *原图*

    ` `out_color = texColor1;

    ` `}

    }

    PFLD98人脸关键点:

    IMG_256

    其中ncnn为新版基于Vulkan加速的推理框架ncnnVulkan;OpenCV主要获取Mat矩阵;mnn为另一种模型推理框架;glm为配合OpenGL的向量库