代码编织梦想

1.动态网格滤镜特效

1.1顶点着色器
#version 300 es
layout(location = 0) in vec3 attr_position;
layout(location = 1) in vec2 attr_uv;

uniform mat4   uni_mat;
out vec2   v_texcoord;

void main(void)
{
    v_texcoord = attr_uv;
    gl_Position = uni_mat* vec4(attr_position,1.0);
}

传入的是MVP矩阵和材质坐标

1.2片元着色器
#version 300 es
precision mediump float;
//precision highp float;

uniform sampler2D uni_textureY;
uniform sampler2D uni_textureU;
uniform sampler2D uni_textureV;

in vec2 v_texcoord;
out vec4 fragColor;

uniform float u_offset;//偏移量
uniform vec2 texSize;//纹理尺寸

vec4 YuvToRgb(vec2 uv){
    vec3 yuv;
    vec3 rgb;
    yuv.x = texture(uni_textureY, uv).r;
    yuv.y = texture(uni_textureU, uv).r - 0.5;
    yuv.z = texture(uni_textureV, uv).r - 0.5;
    rgb = mat3( 1,1,1, 0,-0.39465,2.03211,1.13983,-0.58060,0) * yuv;
    return vec4(rgb, 1);
}


void main(void)
{
    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)
    {
        fragColor = YuvToRgb(v_texcoord);
    }
    else
    {
        fragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }
}

YuvToRgb:这个用于将yuv的数据转成rgb渲染到屏幕上

动态网格主要是将纹理划分为多个网格,然后根据一个偏移量动态改变网格的宽度。

mod:返回 x – y * floor (x / y) ,即求模计算 %

floor:返回小于等于 x 的最大整数值

计算之前需要将纹理坐标系转换为图片坐标系,保证网格没有被拉伸。

2.分屏特效

2.1顶点着色器
#version 300 es
layout(location = 0) in vec3 attr_position;
layout(location = 1) in vec2 attr_uv;

uniform mat4   uni_mat;
out vec2   v_texcoord;

void main(void)
{
    v_texcoord = attr_uv;
    gl_Position = uni_mat* vec4(attr_position,1.0);
}

这块主要是顶点数据以及材质,所以跟上边的是一样的

2.2片元着色器
#version 300 es
precision mediump float;
//precision highp float;

uniform sampler2D uni_textureY;
uniform sampler2D uni_textureU;
uniform sampler2D uni_textureV;

in vec2 v_texcoord;
out vec4 fragColor;

vec4 YuvToRgb(vec2 uv){
    vec3 yuv;
    vec3 rgb;
    yuv.x = texture(uni_textureY, uv).r;
    yuv.y = texture(uni_textureU, uv).r - 0.5;
    yuv.z = texture(uni_textureV, uv).r - 0.5;
    rgb = mat3( 1,1,1, 0,-0.39465,2.03211,1.13983,-0.58060,0) * yuv;
    return vec4(rgb, 1);
}


void main(void)
{

    /*四分屏  效果正常*/
    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;
    }

    fragColor = YuvToRgb(newTexCoord);
}

分屏滤镜的原理是在多个指定区域内对整个纹理进行下采样(缩小),从而实现整个图像在多个区域内多次显示。

3.缩放的圆特效

3.1顶点着色器
#version 300 es
layout(location = 0) in vec3 attr_position;
layout(location = 1) in vec2 attr_uv;

uniform mat4   uni_mat;
out vec2   v_texcoord;

void main(void)
{
    v_texcoord = attr_uv;
    gl_Position = uni_mat* vec4(attr_position,1.0);
}

这块主要是顶点数据以及材质,所以跟上边的是一样的

3.2片元着色器
#version 300 es
precision mediump float;
//precision highp float;

uniform sampler2D uni_textureY;
uniform sampler2D uni_textureU;
uniform sampler2D uni_textureV;

in vec2 v_texcoord;
out vec4 fragColor;

uniform float u_offset;//偏移量
uniform vec2 texSize;//纹理尺寸

vec4 YuvToRgb(vec2 uv){
    vec3 yuv;
    vec3 rgb;
    yuv.x = texture(uni_textureY, uv).r;
    yuv.y = texture(uni_textureU, uv).r - 0.5;
    yuv.z = texture(uni_textureV, uv).r - 0.5;
    rgb = mat3( 1,1,1, 0,-0.39465,2.03211,1.13983,-0.58060,0) * yuv;
    return vec4(rgb, 1);
}



void main(void)
{
    vec2 imgTex = v_texcoord * texSize;//将纹理坐标系转换为图片坐标系
    float r = (u_offset + 0.2 ) * texSize.x;   //0.2 表示圆圈的默认大小
    if(distance(imgTex, vec2(texSize.x / 2.0, texSize.y / 2.0)) < r)
    {
        fragColor = YuvToRgb(v_texcoord);
    }
    else
    {
        fragColor = vec4(1.0, 1.0, 1.0, 1.0);
    }

}

4.渲染程序部分

void MSDynamicGridLine::UpdateYUVData(MSYUVData_Frame *yuvFrame) {
    if(yuvFrame == nullptr ){
        return;
    }

    if(m_nVideoH != yuvFrame->height || m_nVideoW != yuvFrame->width){

        if(nullptr != m_pBufYuv420p)
        {
            free(m_pBufYuv420p);
            m_pBufYuv420p=nullptr;
        }
    }


    m_nVideoW =  yuvFrame->width;
    m_nVideoH = yuvFrame->height;

    m_yFrameLength = yuvFrame->luma.length;
    m_uFrameLength = yuvFrame->chromaB.length;
    m_vFrameLength = yuvFrame->chromaR.length;


    //申请内存存一帧yuv图像数据,其大小为分辨率的1.5倍
    int nLen = m_yFrameLength + m_uFrameLength +m_vFrameLength;

    if(nullptr == m_pBufYuv420p)
    {
        m_pBufYuv420p = ( unsigned char*) malloc(nLen);
    }

    memcpy(m_pBufYuv420p,yuvFrame->luma.dataBuffer,m_yFrameLength);
    memcpy(m_pBufYuv420p+m_yFrameLength,yuvFrame->chromaB.dataBuffer,m_uFrameLength);
    memcpy(m_pBufYuv420p+m_yFrameLength +m_uFrameLength,yuvFrame->chromaR.dataBuffer,m_vFrameLength);

    m_bUpdateData =true;
}

这个方法用于更新YUV数据,由于数据是从相机过来的,跟opengl 不是一个线程,所以需要通过内存共享的方式进行拷贝。

void MSDynamicGridLine::Render(MSGLCamera *pCamera) {
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if(m_bUpdateData == false){
        return;
    }

    static MSVertex triangleVert[] = {
            {-1, 1,  1,     0,0},
            {-1, -1,  1,    0,1},
            {1,  1,  1,     1,0},
            {1,  -1,  1,    1,1},
    };


    glm::mat4x4  objectMat = glm::mat4x4(1.0);
    glm::mat4x4  objectTransMat = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, -5));
    objectMat = objectMat * objectTransMat;

    objectMat = pCamera->projectionMatrix * pCamera->viewMatrix * objectMat ;


    m_pOpenGLShader->Bind();

    m_pOpenGLShader->SetUniformValue("uni_mat",objectMat);


    m_pOpenGLShader->EnableAttributeArray("attr_position");
    m_pOpenGLShader->SetAttributeBuffer("attr_position",GL_FLOAT,triangleVert,3,sizeof(MSVertex));

    m_pOpenGLShader->EnableAttributeArray("attr_uv");
    m_pOpenGLShader->SetAttributeBuffer("attr_uv",GL_FLOAT,&triangleVert[0].u,2,sizeof(MSVertex));

    m_PeriodicFrameIndex++;
    float progress = GetFrameProgress();
    m_pOpenGLShader->SetUniformValue("u_offset",0.2f * progress);

    LOGD("m_nVideoW is %d,progress is %f",m_nVideoW,progress);

    if (m_nVideoW>0){
        m_pOpenGLShader->SetUniformValue("texSize",glm::vec2(m_nVideoW,m_nVideoH));
    }else{
        m_pOpenGLShader->SetUniformValue("texSize",glm::vec2(720,1280));
    }


    m_pOpenGLShader->SetUniformValue("uni_textureY",0);

    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, m_textures[0]);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW, m_nVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pBufYuv420p);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    m_pOpenGLShader->SetUniformValue("uni_textureU",1);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, m_textures[1]);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE,m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)(m_pBufYuv420p+m_yFrameLength));
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    m_pOpenGLShader->SetUniformValue("uni_textureV",2);
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, m_textures[2]);
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)(m_pBufYuv420p+m_yFrameLength+m_uFrameLength));
    glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    glBindTexture(GL_TEXTURE_2D, 0);
    m_pOpenGLShader->DisableAttributeArray("attr_position");
    m_pOpenGLShader->DisableAttributeArray("attr_uv");

    m_pOpenGLShader->Release();

    return;
}
  • 将参数传递给shader:mvp矩阵、顶点、偏移量等
  • 生成三个纹理,用于承载yuv三个分量的数据
  • 然后进行绘制glDrawArrays
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u014078003/article/details/127992769

android平台下opengl es 3.0使用glsurfaceview对相机camera预览实时处理_handy周的博客-爱代码爱编程

OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始android平台下OpenGL ES 3.0绘制纯色背景android平台下OpenGL ES 3.0绘制圆点、直线和三角形and

android平台下opengl es 3.0使用textureview对相机camera预览实时处理_handy周的博客-爱代码爱编程

OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始android平台下OpenGL ES 3.0绘制纯色背景android平台下OpenGL ES 3.0绘制圆点、直线和三角形and

android平台下opengl es 3.0给图片添加黑白滤镜-爱代码爱编程

OpenGL ES 3.0学习实践 android平台下OpenGL ES 3.0从零开始android平台下OpenGL ES 3.0绘制纯色背景android平台下OpenGL ES 3.0绘制圆点、直线和三角形and

NDK OpenGL ES 3.0 开发(十六):相机预览-爱代码爱编程

该原创文章首发于微信公众号:字节流动 OpenGL ES 相机预览 相机开发是 OpenGL ES 的重要应用,利用 OpenGL 可以很方便地实现相机美颜、滤镜、塑型以及一些动态特效,其性能显著优于对应功能的 CPU 实现。 相机的预览实现一般有 2 种方式,一种是基于 Android 原生 SurfaceTexture 的纯 GPU

NDK OpenGL ES 3.0 开发(十七):相机基础滤镜-爱代码爱编程

该原创文章首发于微信公众号:字节流动 相机基础滤镜 上文中我们通过 ImageReader 获取到 Camera2 预览的 YUV 数据,然后利用 OpenGLES 渲染实现相机预览,这一节将利用 GLSL (OpenGL 着色器语言)基于不同的着色器实现多种基础滤镜。 GLSL 一些使用频率比较高的内建函数 内建函数函数说明float

OpenGL ES 3.0 开发(十八):相机 LUT 滤镜-爱代码爱编程

该原创文章首发于微信公众号:字节流动 OpenGLES 相机 LUT 滤镜 什么是 LUT ? LUT 是 Look Up Table 的简称,称作颜色查找表,是一种针对色彩空间的管理和转换技术。它可以分为一维 LUT(1D LUT) 和 三维 LUT(3D LUT),其中三维 LUT 比较常用。简单来讲,LUT 就是一个 RGB 组合到另一

NDK OpenGL ES 3.0 开发(十九):相机抖音滤镜-爱代码爱编程

该原创文章首发于微信公众号:字节流动 OpenGLES 相机抖音滤镜 最近几篇文章主要是利用 OpenGL 实现相机预览的一些常见的滤镜,上一篇主要介绍了 LUT 滤镜的原理及简单实现方法,而本文主要介绍抖音短视频 App 里面一些常见滤镜的实现,这里只做抛砖引玉,玩滤镜主要靠想象力去实现一些酷炫的效果。 分色偏移 分色偏移滤镜原理:基

android-camera2相机开发-8-使用opengl实现滤镜效果-爱代码爱编程

项目github地址 前一篇文章中,实现了 opengles 进行相机预览的功能,基本的流程如下: 把相机的预览数据做成纹理,绑定到opengles对应的纹理单元上然后通过opengles 的内置函数 texture(),在片段着色器中根据纹理和纹理坐标进行插值计算直接将计算结果输出到颜色缓冲区,显示到屏幕的像素上。给图像添加滤镜本质上就是图片处理,也

Android OpenGLES 3.0 开发极简教程-爱代码爱编程

Android OpenGLES 3.0 开发极简教程 https://github.com/githubhaohao/NDK_OpenGLES_3_0 备注: 其中一些 Case 的 3D 效果是通过手势触发(转动和缩放)。 展示图 基础篇 NDK OpenGL ES 3.0 开发(一):绘制一个三角形 NDK OpenGL ES 3

《Android 美颜类相机开发汇总》第五章 Android OpenGLES 美颜定制实现-爱代码爱编程

在介绍美颜定制之前,我们先来复习一下OpenGL中图像绘制原理。OpenGL的图像绘制,是由许许多多三角形构成的。OpenGL的绘制离不开三角形的绘制。通常对于不需要对图像细节进行处理的时候,我们一般会使用glDrawArrays方法将整张图片绘制处理。但如果要对图像的某一个部分进行形变等微调,这时候通常将图像划分为许许多多的三角形。比如MLS算法原理就是

Android OpglEs录屏功能实现-爱代码爱编程

文章目录 前言实现实现方案方案一:MediaCodec录制概述博客方案二:GLES20.glReadPixels()概述方案三:PBO概述方案四: ImageReader概述相关技术MediaCodec概述MediaMuxer概述相关博客问题 前言 最近在做录屏相关的项目,学习过程中记录的笔记如下,方便日后回顾。 实现 实现方案 方案一

利用 OpenGL ES 给视频播放器和相机做个字符画滤镜-爱代码爱编程

该原创文章首发于微信公众号:字节流动 最后不少朋友问,“OpenGL ES 入门后怎么学习写一些滤镜?”,“怎么学习 shader ?”。 最近请教了一些大佬,他们一致认为正确的做法就是“去模仿”。先去模仿别人的滤镜怎么实现的,比如观察抖音的一些简单的滤镜,然后自己琢磨去实现一个。 当然,最有效率的方法是研究一些相关的开源项目,比如大名鼎鼎

全网最全的 Android 音视频和 OpenGL ES 干货,都在这了-爱代码爱编程

有位大佬说,“这是全网最全的 Android OpenGL ES 教程”,哈哈,对于这种善意的“商业互吹”,当然是欣然接受,这无疑给了我更多的动力和激情来完善这几个系列的文章。 学习 Android 音视频开发,其实可以从学习图像渲染技术(OpenGL ES)入手,毕竟图像渲染一开始可以获得即时反馈,知识点少且不枯燥。 以图像渲染技术为切入点便可

Android OpenGL开发学习(二)手把手教你实现抖音分屏相机-爱代码爱编程

目录 前言一、常规操作二、使用步骤1.创建SurfaceTexture2.自定义Renderer3.坐标系4.OpenGL和Camera相结合5.实际运行效果6.分屏效果7.项目地址总结 前言 上面文章介绍了一下OpenGL基本使用,由于接触OpenGL时间不长,理解不够深入,讲得不是很清楚,接下来用这篇文章,通过一个实际的开发例子,重新介绍