本文正在参加「金石计划」

前语

水印贴图又称画中画,这种功用在Opengl中是怎么完成的呢?咱们能够简略地了解成两张纹路的叠加,一个纹路作为布景,别的一个纹路经过调整极点坐标作为一个小的前景。

提到水印贴图的完成,很多朋友可能会想到经过mix混合函数完成,但是并不引荐这样做,抛开性能先不说,这种完成方法就不太便利后期拓展。今天咱们就经过mix内建函数和 多纹路制作两种方法完成一个水印贴图的功用。

mix混合贴图

先来看看咱们用到的贴图资源,首要咱们需求烘托的布景纹路是:

Opengl ES之水印贴图

需求贴图的水印是:

Opengl ES之水印贴图

片元着色器:

#version 300 es
precision mediump float;
out vec4 FragColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D waterTexture;
void main()
{
   vec4 textColor = texture(ourTexture, TexCoord);
   vec4 waterColor = texture(waterTexture, TexCoord);
   FragColor = mix(textColor, waterColor, 0.7);
}

这个片元着色器很简略,便是传递两个纹路,一个是布景纹路,一个是水印纹路,然后经过mix内建混合函数将颜色混合烘托显现。

首要的烘托代码:

//    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
//    glClear(GL_COLOR_BUFFER_BIT);
//    glUseProgram(program);
//
//    // 激活纹路
//    glActiveTexture(GL_TEXTURE2);
//    // 绑定纹路
//    glBindTexture(GL_TEXTURE_2D, textureId);
//    glUniform1i(textureSampler, 2);
//
//
//    // 激活纹路
//    glActiveTexture(GL_TEXTURE3);
//    // 绑定纹路
//    glBindTexture(GL_TEXTURE_2D, waterTextureId);
//    glUniform1i(waterTextureSampler, 3);
//
//    /**
//     * size 几个数字表示一个点,显现是两个数字表示一个点
//     * normalized 是否需求归一化,不必,这儿现已归一化了
//     * stride 步长,接连极点之间的距离,假如极点直接是接连的,也可填0
//     */
//    // 启用极点数据
//    glEnableVertexAttribArray(positionHandle);
//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
//
//    // 纹路坐标
//    glEnableVertexAttribArray(textureHandle);
//    glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);
//
//    // 4个极点制作两个三角形组成矩形
//    glDrawArrays(GL_TRIANGLE_STRIP,0,4);
//
//    glUseProgram(0);
//
//    // 禁用极点
//    glDisableVertexAttribArray(positionHandle);
//    if(nullptr != eglHelper){
//        eglHelper->swapBuffers();
//    }
//
//    glBindTexture(GL_TEXTURE_2D, 0);

运行起来看到作用是这样的:

Opengl ES之水印贴图

貌似不太对?好像被蒙上一层黑色阴影的感觉,这是由于咱们没有翻开Opengl的混合功用,没有设置混合模式导致的,咱们在制作时翻开对应的设置即可:

//    // 能够注释掉这两句看看作用是不一样的
////    glEnable(GL_BLEND); //翻开混合功用
////    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //指定混合模式

再次运行看看作用:

Opengl ES之水印贴图

这次对了,看起来不好看是由于水印贴图的大小和布景纹路大小一致了,期望传神一点的话能够适当修正着色器言语,在特定的极点区域才采样运用mix函数混合,其他区域就显现布景纹路的采样即可,同时假如需求调整水印贴图的权重,直接调整着色器中mix函数的第三个参数即可。

布景纹路和水印贴图独自制作

上面提到不引荐运用mix混合贴图的完成方法,那么更加合理的方法是什么呢?

咱们编程讲究的是功用粒度拆分要合理,假如能坐到本经纹路和水印纹路独自制作别离就能更加地便利后续功用的拓展,将制作别离开来也能更好地搭配FBO离屏烘托。

这儿为了贪便利完成单个功用而已,就不采用FBO的方法了,当然假如是实践应用中还是主张运用FBO的方法更加地合理。

废话少扯,直接上代码吧,注意看关键注释:

WaterMarkOpengl.cpp
// 极点着色器
static const char *ver = "#version 300 es\n"
                         "in vec4 aPosition;\n"
                         "in vec2 aTexCoord;\n"
                         "out vec2 TexCoord;\n"
                         "void main() {\n"
                         "  TexCoord = aTexCoord;\n"
                         "  gl_Position = aPosition;\n"
                         "}";
// 片元着色器
// 多纹路重叠,经过mix混合完成水印
//static const char *fragment = "#version 300 es\n"
//                              "precision mediump float;\n"
//                              "out vec4 FragColor;\n"
//                              "in vec2 TexCoord;\n"
//                              "uniform sampler2D ourTexture;\n"
//                              "uniform sampler2D waterTexture;\n"
//                              "void main()\n"
//                              "{\n"
//                              "    vec4 textColor = texture(ourTexture, TexCoord);\n"
//                              "    vec4 waterColor = texture(waterTexture, TexCoord);\n"
//                              "    FragColor = mix(textColor, waterColor, 0.7);\n"
//                              "}";
static const char *fragment = "#version 300 es\n"
                              "precision mediump float;\n"
                              "out vec4 FragColor;\n"
                              "in vec2 TexCoord;\n"
                              "uniform sampler2D ourTexture;\n"
                              "void main()\n"
                              "{\n"
                              "    vec4 textColor = texture(ourTexture, TexCoord);\n"
                              "    FragColor = textColor;\n"
                              "}";
// 运用制作两个三角形组成一个矩形的方式(三角形带)
// 榜首第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
        1.0f,-1.0f, // 右下
        1.0f,1.0f, // 右上
        -1.0f,-1.0f, // 左下
        -1.0f,1.0f // 左上
};
// 贴图纹路坐标(参阅手机屏幕坐标体系,原点在左上角)
const static GLfloat TEXTURE_COORD[] = {
        1.0f,1.0f, // 右下
        1.0f,0.0f, // 右上
        0.0f,1.0f, // 左下
        0.0f,0.0f // 左上
};
WaterMarkOpengl::WaterMarkOpengl() {
    initGlProgram(ver,fragment);
    positionHandle = glGetAttribLocation(program,"aPosition");
    textureHandle = glGetAttribLocation(program,"aTexCoord");
    textureSampler = glGetUniformLocation(program,"ourTexture");
    waterTextureSampler = glGetUniformLocation(program,"waterTexture");
}
WaterMarkOpengl::~WaterMarkOpengl() noexcept {
}
void WaterMarkOpengl::onDraw() {
//    // 能够注释掉这两句看看作用是不一样的
////    glEnable(GL_BLEND); //翻开混合功用
////    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //指定混合模式
//
//    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
//    glClear(GL_COLOR_BUFFER_BIT);
//    glUseProgram(program);
//
//    // 激活纹路
//    glActiveTexture(GL_TEXTURE2);
//    // 绑定纹路
//    glBindTexture(GL_TEXTURE_2D, textureId);
//    glUniform1i(textureSampler, 2);
//
//
//    // 激活纹路
//    glActiveTexture(GL_TEXTURE3);
//    // 绑定纹路
//    glBindTexture(GL_TEXTURE_2D, waterTextureId);
//    glUniform1i(waterTextureSampler, 3);
//
//    /**
//     * size 几个数字表示一个点,显现是两个数字表示一个点
//     * normalized 是否需求归一化,不必,这儿现已归一化了
//     * stride 步长,接连极点之间的距离,假如极点直接是接连的,也可填0
//     */
//    // 启用极点数据
//    glEnableVertexAttribArray(positionHandle);
//    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
//
//    // 纹路坐标
//    glEnableVertexAttribArray(textureHandle);
//    glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);
//
//    // 4个极点制作两个三角形组成矩形
//    glDrawArrays(GL_TRIANGLE_STRIP,0,4);
//
//    glUseProgram(0);
//
//    // 禁用极点
//    glDisableVertexAttribArray(positionHandle);
//    if(nullptr != eglHelper){
//        eglHelper->swapBuffers();
//    }
//
//    glBindTexture(GL_TEXTURE_2D, 0);
    // 能够注释掉这两句看看作用是不一样的
    glEnable(GL_BLEND); //翻开混合功用
    glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); //指定混合模式
    // 先制作布景,正常纹路
   // 让纹路图片居中
    glViewport((eglHelper->viewWidth - imageWidth)/2,(eglHelper->viewHeight - imageHeight)/2,imageWidth,imageHeight);
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);
    // 激活纹路
    glActiveTexture(GL_TEXTURE2);
    // 绑定纹路
    glBindTexture(GL_TEXTURE_2D, textureId);
    glUniform1i(textureSampler, 2);
    /**
     * size 几个数字表示一个点,显现是两个数字表示一个点
     * normalized 是否需求归一化,不必,这儿现已归一化了
     * stride 步长,接连极点之间的距离,假如极点直接是接连的,也可填0
     */
    // 启用极点数据
    glEnableVertexAttribArray(positionHandle);
    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
    // 纹路坐标
    glEnableVertexAttribArray(textureHandle);
    glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);
    // 4个极点制作两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);
    // 然后运用一致的着色器,改动纹路数据再制作一次水印 极点坐标和纹路坐标和上面运用的一样,因而不必从头赋值了
    // 调整窗口方位,调整水印的制作方位,让水印制作的布景的右下角
    int waterWidth = 200;
    int waterHeight = 300;
    // 以手机屏幕左下角为坐标原点
    glViewport((eglHelper->viewWidth - imageWidth)/2 + imageWidth - waterWidth,(eglHelper->viewHeight - imageHeight)/2,waterWidth,waterHeight);
    // 激活纹路
    glActiveTexture(GL_TEXTURE3);
    // 绑定纹路
    glBindTexture(GL_TEXTURE_2D, waterTextureId);
    glUniform1i(textureSampler, 3);
    // 4个极点制作两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);
    glUseProgram(0);
    // 禁用极点
    glDisableVertexAttribArray(positionHandle);
    if(nullptr != eglHelper){
        eglHelper->swapBuffers();
    }
    glBindTexture(GL_TEXTURE_2D, 0);
}
void WaterMarkOpengl::setPixel(void *data, int width, int height, int length) {
    LOGD("texture setPixel");
    imageWidth = width;
    imageHeight = height;
    glGenTextures(1, &textureId);
    // 绑定纹路
    glBindTexture(GL_TEXTURE_2D, textureId);
    // 为当时绑定的纹路目标设置盘绕、过滤方法
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    // 生成mip贴图
    glGenerateMipmap(GL_TEXTURE_2D);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D, 0);
}
void WaterMarkOpengl::setWaterPixel(void *data, int width, int height, int length) {
    LOGD("texture setWaterPixel");
    glGenTextures(1, &waterTextureId);
    // 绑定纹路
    glBindTexture(GL_TEXTURE_2D, waterTextureId);
    // 为当时绑定的纹路目标设置盘绕、过滤方法
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    // 生成mip贴图
    glGenerateMipmap(GL_TEXTURE_2D);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D, 0);
}

首要思维便是先制作布景纹路,然后经过glViewport改动视窗的大小再制作水印纹路,实例中咱们将心形水印制作在布景纹路的右下角。

运行作用:

Opengl ES之水印贴图

系列教程源码

github.com/feiflyer/ND…

后续demo假如有完善可能会更新。

Opengl ES系列入门介绍

Opengl ES之EGL环境建立
Opengl ES之着色器
Opengl ES之三角形制作
Opengl ES之四边形制作
Opengl ES之纹路贴图
Opengl ES之VBO和VAO
Opengl ES之EBO
Opengl ES之FBO
Opengl ES之PBO
Opengl ES之YUV数据烘托
YUV转RGB的一些理论知识
Opengl ES之RGB转NV21
Opengl ES之踩坑记
Opengl ES之矩阵改换(上)
Opengl ES之矩阵改换(下)
Opengl ES之水印贴图

关注我,一同前进,人生不止coding!!!