博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
OpenGL ES 3.0(九)实现美颜相机功能
阅读量:4293 次
发布时间:2019-05-27

本文共 13192 字,大约阅读时间需要 43 分钟。

着色器代码

虽然知道磨皮应该用双边滤波算法,但实际上我并不懂具体的代码编写过程,因此着色器代码用的是 GitHub 上找的一个项目: ,顶点着色器如下:

#version 300 eslayout(location=0) in vec4 aPosition;layout(location=1) in vec4 aTexCoord;uniform mat4 mMatrix;out vec2 vTexCoord;void main() {    vTexCoord = (mMatrix * aTexCoord).xy;    gl_Position = aPosition;}

片段着色器如下:

#version 300 es#extension GL_OES_EGL_image_external_essl3 : requireprecision highp float;in highp vec2 vTexCoord;uniform samplerExternalOES sTexture;uniform highp vec2 singleStepOffset;uniform highp vec4 params;uniform highp float brightness;const highp vec3 W = vec3(0.299, 0.587, 0.114);const highp mat3 saturateMatrix = mat3(        1.1102, -0.0598, -0.061,        -0.0774, 1.0826, -0.1186,        -0.0228, -0.0228, 1.1772);highp vec2 blurCoordinates[24];highp float hardLight(highp 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;}layout(location=0) out vec4 fragColor;void main() {    highp vec3 centralColor = texture(sTexture, vTexCoord).rgb;    blurCoordinates[0] = vTexCoord.xy + singleStepOffset * vec2(0.0, -10.0);    blurCoordinates[1] = vTexCoord.xy + singleStepOffset * vec2(0.0, 10.0);    blurCoordinates[2] = vTexCoord.xy + singleStepOffset * vec2(-10.0, 0.0);    blurCoordinates[3] = vTexCoord.xy + singleStepOffset * vec2(10.0, 0.0);    blurCoordinates[4] = vTexCoord.xy + singleStepOffset * vec2(5.0, -8.0);    blurCoordinates[5] = vTexCoord.xy + singleStepOffset * vec2(5.0, 8.0);    blurCoordinates[6] = vTexCoord.xy + singleStepOffset * vec2(-5.0, 8.0);    blurCoordinates[7] = vTexCoord.xy + singleStepOffset * vec2(-5.0, -8.0);    blurCoordinates[8] = vTexCoord.xy + singleStepOffset * vec2(8.0, -5.0);    blurCoordinates[9] = vTexCoord.xy + singleStepOffset * vec2(8.0, 5.0);    blurCoordinates[10] = vTexCoord.xy + singleStepOffset * vec2(-8.0, 5.0);    blurCoordinates[11] = vTexCoord.xy + singleStepOffset * vec2(-8.0, -5.0);    blurCoordinates[12] = vTexCoord.xy + singleStepOffset * vec2(0.0, -6.0);    blurCoordinates[13] = vTexCoord.xy + singleStepOffset * vec2(0.0, 6.0);    blurCoordinates[14] = vTexCoord.xy + singleStepOffset * vec2(6.0, 0.0);    blurCoordinates[15] = vTexCoord.xy + singleStepOffset * vec2(-6.0, 0.0);    blurCoordinates[16] = vTexCoord.xy + singleStepOffset * vec2(-4.0, -4.0);    blurCoordinates[17] = vTexCoord.xy + singleStepOffset * vec2(-4.0, 4.0);    blurCoordinates[18] = vTexCoord.xy + singleStepOffset * vec2(4.0, -4.0);    blurCoordinates[19] = vTexCoord.xy + singleStepOffset * vec2(4.0, 4.0);    blurCoordinates[20] = vTexCoord.xy + singleStepOffset * vec2(-2.0, -2.0);    blurCoordinates[21] = vTexCoord.xy + singleStepOffset * vec2(-2.0, 2.0);    blurCoordinates[22] = vTexCoord.xy + singleStepOffset * vec2(2.0, -2.0);    blurCoordinates[23] = vTexCoord.xy + singleStepOffset * vec2(2.0, 2.0);    highp float sampleColor = centralColor.g * 22.0;    sampleColor += texture(sTexture, blurCoordinates[0]).g;    sampleColor += texture(sTexture, blurCoordinates[1]).g;    sampleColor += texture(sTexture, blurCoordinates[2]).g;    sampleColor += texture(sTexture, blurCoordinates[3]).g;    sampleColor += texture(sTexture, blurCoordinates[4]).g;    sampleColor += texture(sTexture, blurCoordinates[5]).g;    sampleColor += texture(sTexture, blurCoordinates[6]).g;    sampleColor += texture(sTexture, blurCoordinates[7]).g;    sampleColor += texture(sTexture, blurCoordinates[8]).g;    sampleColor += texture(sTexture, blurCoordinates[9]).g;    sampleColor += texture(sTexture, blurCoordinates[10]).g;    sampleColor += texture(sTexture, blurCoordinates[11]).g;    sampleColor += texture(sTexture, blurCoordinates[12]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[13]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[14]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[15]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[16]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[17]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[18]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[19]).g * 2.0;    sampleColor += texture(sTexture, blurCoordinates[20]).g * 3.0;    sampleColor += texture(sTexture, blurCoordinates[21]).g * 3.0;    sampleColor += texture(sTexture, blurCoordinates[22]).g * 3.0;    sampleColor += texture(sTexture, blurCoordinates[23]).g * 3.0;    sampleColor = sampleColor / 62.0;    highp float highPass = centralColor.g - sampleColor + 0.5;    for (int i = 0; i < 5; i++) {        highPass = hardLight(highPass);    }    highp float lumance = dot(centralColor, W);    highp float alpha = pow(lumance, params.r);    highp vec3 smoothColor = centralColor + (centralColor-vec3(highPass))*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);    highp vec3 lvse = vec3(1.0)-(vec3(1.0)-smoothColor)*(vec3(1.0)-centralColor);    highp vec3 bianliang = max(smoothColor, centralColor);    highp vec3 rouguang = 2.0*centralColor*smoothColor + centralColor*centralColor - 2.0*centralColor*centralColor*smoothColor;    fragColor = vec4(mix(centralColor, lvse, alpha), 1.0);    fragColor.rgb = mix(fragColor.rgb, bianliang, alpha);    fragColor.rgb = mix(fragColor.rgb, rouguang, params.b);    highp vec3 satcolor = fragColor.rgb * saturateMatrix;    fragColor.rgb = mix(fragColor.rgb, satcolor, params.a);    fragColor.rgb = vec3(fragColor.rgb + vec3(brightness));}

OpenGL 代码

先创建顶点坐标等数据:

const static GLfloat VERTICES[] = {        -1.0f, 1.0f,        1.0f, 1.0f,        -1.0f, -1.0f,        1.0f, -1.0f};const static GLfloat TEX_COORDS[] = {        0.0f, 1.0f,        1.0f, 1.0f,        0.0f, 0.0f,        1.0f, 0.0f};const static GLushort INDICES[] = {        0, 1, 2,        1, 2, 3};const static GLuint ATTRIB_POSITION = 0;const static GLuint ATTRIB_TEX_COORD = 1;const static GLuint VERTEX_POS_SIZE = 2;const static GLuint INDEX_NUMBER = 6;const static GLuint TEX_COORD_POS_SIZE = 2;

接着加载着色器、使用 VBO 、VAO 缓存顶点数据:

int Beauty::init(AAssetManager *manager, ANativeWindow *window, int width, int height) {    mWindow = window;    resize(width, height);    if (!mEGLCore->buildContext(window)) {        LOGE("buildContext failed");        return -1;    }    std::string *vShader = readShaderFromAsset(manager, "beauty.vert");    std::string *fShader = readShaderFromAsset(manager, "beauty.frag");    mProgram = loadProgram(vShader->c_str(), fShader->c_str());    if (!mProgram) {        LOGE("loadProgram failed!");        return -1;    }    // 设置默认帧缓冲区纹理    glGenTextures(1, &mTexOes);    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexOes);    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);    glTexParameterf(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);    glBindTexture(GL_TEXTURE_EXTERNAL_OES, 0);    initVbo();    initVao();    initPbo();    mMatrixLoc = glGetUniformLocation(mProgram, "mMatrix");    mTexLoc = glGetUniformLocation(mProgram, "sTexture");    mParamsLoc = glGetUniformLocation(mProgram, "params");    mBrightnessLoc = glGetUniformLocation(mProgram, "brightness");    mSingleStepOffsetLoc = glGetUniformLocation(mProgram, "singleStepOffset");    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);    delete vShader;    delete fShader;    return mTexOes;}void Beauty::initVbo() {    // 缓存顶点坐标、纹理坐标、索引数据到缓冲区中    glGenBuffers(3, mVboIds);    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);    glBufferData(GL_ARRAY_BUFFER, sizeof(VERTICES), VERTICES, GL_STATIC_DRAW);    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);    glBufferData(GL_ARRAY_BUFFER, sizeof(TEX_COORDS), TEX_COORDS, GL_STATIC_DRAW);    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVboIds[2]);    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(INDICES), INDICES, GL_STATIC_DRAW);}void Beauty::initVao() {    glGenVertexArrays(1, &mVao);    // 使用缓冲区的数据设置顶点属性,并绑定至 vao    glBindVertexArray(mVao);    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[0]);    glEnableVertexAttribArray(ATTRIB_POSITION);    glVertexAttribPointer(ATTRIB_POSITION, VERTEX_POS_SIZE, GL_FLOAT, GL_FALSE, 0, 0);    glBindBuffer(GL_ARRAY_BUFFER, mVboIds[1]);    glEnableVertexAttribArray(ATTRIB_TEX_COORD);    glVertexAttribPointer(ATTRIB_TEX_COORD, TEX_COORD_POS_SIZE, GL_FLOAT, GL_FALSE, 0, 0);    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mVboIds[2]);    glBindVertexArray(0);}void Beauty::initPbo() {    // 生成 pbo    glGenBuffers(2, mPboIds);    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPboIds[0]);    glBufferData(GL_PIXEL_PACK_BUFFER, mWidth * mHeight * 4, nullptr, GL_DYNAMIC_COPY);    glBindBuffer(GL_PIXEL_PACK_BUFFER, mPboIds[1]);    glBufferData(GL_PIXEL_PACK_BUFFER, mWidth * mHeight * 4, nullptr, GL_DYNAMIC_COPY);}

然后绘制即可:

voidBeauty::draw(GLfloat *matrix, GLfloat beauty, GLfloat saturate, GLfloat bright, bool recording) {    glViewport(0, 0, mWidth, mHeight);    glClear(GL_COLOR_BUFFER_BIT);    glUseProgram(mProgram);    glActiveTexture(GL_TEXTURE0);    glBindTexture(GL_TEXTURE_EXTERNAL_OES, mTexOes);    glUniform1i(mTexLoc, 0);    glUniformMatrix4fv(mMatrixLoc, 1, GL_FALSE, matrix);    glUniform4fv(mParamsLoc, 1, getParams(beauty, saturate));    glUniform1f(mBrightnessLoc, getBright(bright));    glUniform2fv(mSingleStepOffsetLoc, 1, getSingleStepOffset(mWidth, mHeight));    // 从 vao 中读取数据并渲染    glBindVertexArray(mVao);    glDrawElements(GL_TRIANGLES, INDEX_NUMBER, GL_UNSIGNED_SHORT, 0);    glBindVertexArray(0);    // 下面这段代码块是配合 ffmpeg 使用的,用于将美颜后的图像发送到 ffmpeg 中以录制视频,可以忽略    if (recording) {        int rgbSize = mWidth * mHeight * 4;        glReadBuffer(GL_COLOR_ATTACHMENT0);        glBindBuffer(GL_PIXEL_PACK_BUFFER, mPboIds[mPboReadIndex]);        // 从默认帧缓冲区读取数据到 PBO 中,PBO 可以加快数据传输速度        glReadPixels(0, 0, mWidth, mHeight, GL_RGBA, GL_UNSIGNED_BYTE, 0);        if (mPboMapIndex >= 0) {            glBindBuffer(GL_PIXEL_PACK_BUFFER, mPboIds[mPboMapIndex]);            uint8_t *mapData = (uint8_t *) glMapBufferRange(GL_PIXEL_PACK_BUFFER, 0, rgbSize,                                                            GL_MAP_READ_BIT);            uint8_t *rgb = new uint8_t[rgbSize];            memcpy(rgb, mapData, (size_t) rgbSize);            recordImage(rgb, rgbSize, mWidth, mHeight, PIXEL_FORMAT_ABGR); // ffmpeg 视频录制接口            glUnmapBuffer(GL_PIXEL_PACK_BUFFER);        }        mPboMapIndex = mPboReadIndex;        mPboReadIndex = 1 - mPboReadIndex;        glBindBuffer(GL_PIXEL_PACK_BUFFER, 0);    }    glFlush();    mEGLCore->swapBuffer();}GLfloat *Beauty::getParams(const GLfloat beauty, const GLfloat saturate) {    GLfloat *value = new GLfloat[4];    value[0] = 1.6f - 1.2f * beauty;    value[1] = 1.3f - 0.6f * beauty;    value[2] = -0.2f + 0.6f * saturate;    value[3] = -0.2f + 0.6f * saturate;    return value;}GLfloat Beauty::getBright(const GLfloat bright) {    return 0.6f * (-0.5f + bright);}GLfloat *Beauty::getSingleStepOffset(const GLfloat width, const GLfloat height) {    GLfloat *value = new GLfloat[2];    value[0] = 2.0f / width;    value[1] = 2.0f / height;    return value;}

Java 代码

Java 代码很简单,只需要一个 SurfaceView,把 OpenGL 返回的纹理 ID 设置给 Camera 以开启预览即可,需要注意的是,更新预览图的代码需要和 OpenGL 运行在主线程之外的同一线程中:

private SurfaceTexture initOpenGL(Surface surface, int width, int height)                throws ExecutionException, InterruptedException {            Future
future = mExecutor.submit(() -> { AssetManager manager = getContext().getAssets(); int textureId = _init(surface, width, height, manager); if (textureId < 0) { Log.e(TAG, "surfaceCreated init OpenGL ES failed!"); mIsBeautyOpen = false; return null; } SurfaceTexture surfaceTexture = new SurfaceTexture(textureId); surfaceTexture.setOnFrameAvailableListener(surfaceTexture1 -> drawOpenGL()); return surfaceTexture; }); return future.get(); } private void drawOpenGL() { mExecutor.execute(() -> { if (mSurfaceTexture != null) { mSurfaceTexture.updateTexImage(); // 必须运行在 OpenGL 线程环境中 mSurfaceTexture.getTransformMatrix(mMatrix); _draw(mMatrix, mBeautyLevel, mSaturateLevel, mBrightLevel, mIsRecording); } }); } private void releaseOpenGL() { mExecutor.execute(() -> { if (mSurfaceTexture != null) { mSurfaceTexture.release(); mSurfaceTexture = null; } _stop(); }); }

“_” 开头的是 native 方法,可以通过 native 接口设置磨皮、饱和度等参数:

private static native int _init(Surface surface, int width, int height, AssetManager manager);    private static native void _draw(float[] matrix, float beauty, float saturate, float bright,                                     boolean recording);    private static native void _stop();

源码已上传到 。

转载地址:http://tjyws.baihongyu.com/

你可能感兴趣的文章
suse如何创建定时任务?
查看>>
suse搭建ftp服务器方法
查看>>
centos虚拟机设置共享文件夹并通过我的电脑访问[增加smbd端口修改]
查看>>
Socket深度探究4PHP(三)
查看>>
可继承扩展的单例实现
查看>>
VS调试技巧
查看>>
C++线程池实现
查看>>
std::function与回调类
查看>>
类型擦除
查看>>
QML动画按钮实现
查看>>
带授权的友元访问限制
查看>>
模板中void类型强转
查看>>
angular-froala-wysiwyg编辑器插件3.0版本中工具栏toolbarButtons选项不起作用
查看>>
nz-select选项无法默认显示
查看>>
Angular页面调用APP函数方法
查看>>
logstash配置pipelines.yml后报错
查看>>
opencv环境配置
查看>>
C++读写二进制文件
查看>>
2015-7-7 小记
查看>>
meshlab编译问题小结
查看>>