Opengl ES之颜色混合

关于混合,其实我们在之前的文章中也有使用到混合的功能了,例如在 Opengl ES之水印贴图 使用混合实现水印贴图。

什么是颜色混合?

颜色混合就是把两种颜色按某种规则混合起来得到新的颜色。

在使用颜色混合时我们首先需要开启混合功能:

glEnable(GL_BLEND);

比如我们有两种颜色颜色,一种是纯蓝色RGBA(0, 0, 255, 255),一种是纯绿色RGBA(0, 255, 0, 255),混合时分别各取一半,就得到了混合后的颜色RGBA(0,127, 127, 255), 这就是Opengl ES中颜色混合的工作模式,但是不一定就是上面的这条计算公式,上述的只是笔者举的一个例子。

这个颜色混合的默认公式如下,为什么说是默认呢?因为可以通过其它的函数改变这个公式:

C_f = (C_src * S) + (C_dest * D)

C_f :最终计算产生的颜色
C_src :源颜色
C_dest :是目标颜色
S :源混合因子
D :目标混合因子。

而上述的混合因子SD可以使用glBlendFunc函数进行设置:

//  S 和 D 都是枚举值,不是可直接指定的实际值。
glBlendFunc(GLenum s, GLenum D);

更多关于这个函数的枚举值可以参考官方介绍:https://registry.khronos.org/OpenGL-Refpages/es2.0/xhtml/glBlendFunc.xml

一般情况下,RGBA都是使用8位来表示,因此在这个官方介绍中的表格数据中的K相关的参数都可以直接看成1即可,也就是简单地将公式中的除数看出1更方便理解。

颜色混合的大致原理

通常OpenGL渲染时会把颜色值放在颜色缓冲区,每个像素的深度值放在深度缓冲区。 当深度测试被关闭,新的颜色值会简单地覆盖颜色缓冲区中已经存在的其他值。当深度测试被打开,则会保留深度值Z更小的。 但如果打开了 混合功能,那么下层的颜色值就不会被清除了,而是按照上面所介绍的那样使用某种公式计算出新的混合颜色。

下面通过一个简单的例子介绍下函数glBlendFun的使用,例如:

glBlendFun(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

如果颜色缓冲区已经有一种蓝色(0.0f, 0.0f, 1.0f, 1.0f),这就是目标颜色,然后在这上面用一种alpha值为 0.4 的红色(1.0f, 0.0f, 0.0f, 0.4f),绘制了一些什么东西(这个后面画上去就是源颜色),那么就最终颜色是如何计算的呢?

C_dest = 目标颜色 = (0.0f, 0.0f, 1.0f, 1.0f)
C_src = 源颜色 = (1.0f, 0.0f, 0.0f, 0.4f)
//  因为GL_SRC_ALPHA
S = 源 alpha 值 = 0.4
// 因为GL_ONE_MINUS_SRC_ALPHA
D = 1 减去 src alpha 值 = 1.0 - 0.4 = 0.6
默认混合方程式:
C_f = (C_src * S) + (C_dest * D)
也就是
C_f = (C_src * 0.4) + (C_dest * 0.6)

因此最终的颜色是原先的蓝色(目标颜色)与后来的红色(源颜色)进行公式混合后的组合。源颜色的alpha值越高,添加的源颜色成分就越多,目标颜色所保留的成分就越少。

以下时笔者绘制的两个纯色矩形的混合运行结果,一个是蓝色,一个是红色,程序混合效果如图:

Opengl ES之颜色混合

完整的opengl渲染代码:

#include "ColorBlendOpengl.h"
#include "../utils/Log.h"


// 顶点着色器
static const char *ver = "#version 300 es\n"
                         "in vec4 aColor;\n"
                         "in vec4 aPosition;\n"
                         "out vec4 vColor;\n"
                         "void main() {\n"
                         "    vColor = aColor;\n"
                         "    gl_Position = aPosition;\n"
                         "}";

// 片元着色器
static const char *fragment = "#version 300 es\n"
                              "precision mediump float;\n"
                              "in vec4 vColor;\n"
                              "out vec4 fragColor;\n"
                              "void main() {\n"
                              "    fragColor = vColor;\n"
                              "}";

// 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
        0.5f,-0.5f, // 右下
        0.5f,0.5f, // 右上
        -0.5f,-0.5f, // 左下
        -0.5f,0.5f // 左上
};

const static GLfloat VERTICES_02[] = {
        0.8f,-0.0f, // 右下
        0.8f,0.8f, // 右上
        -0.0f,-0.0f, // 左下
        -0.0f,0.8f // 左上
};


// rgba 蓝色
const static GLfloat COLOR_ICES[] = {
        0.0f,0.0f,1.0f,1.0f
};

// rgba 红色
const static GLfloat COLOR_ICES_02[] = {
        1.0f,0.0f,0.0f,0.4f
};

ColorBlendOpengl::ColorBlendOpengl():BaseOpengl() {
    initGlProgram(ver,fragment);
    positionHandle = glGetAttribLocation(program,"aPosition");
    colorHandle = glGetAttribLocation(program,"aColor");
    LOGD("program:%d",program);
    LOGD("positionHandle:%d",positionHandle);
    LOGD("colorHandle:%d",colorHandle);
}

void ColorBlendOpengl::onDraw() {

    // 打开混合
    glEnable(GL_BLEND);
    // 混合模式
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

    // 清屏
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);
    /**
     * size 几个数字表示一个点,显示是两个数字表示一个点
     * normalized 是否需要归一化,不用,这里已经归一化了
     * stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0
     */
    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);
    // 启用顶点数据
    glEnableVertexAttribArray(positionHandle);

    // 这个不需要glEnableVertexAttribArray
    glVertexAttrib4fv(colorHandle, COLOR_ICES);

    // 4个顶点绘制两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);
    // 第二个矩形绘制
    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES_02);
    // 启用顶点数据
    glEnableVertexAttribArray(positionHandle);

    glVertexAttrib4fv(colorHandle, COLOR_ICES_02);
    // 4个顶点绘制两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    glUseProgram(0);

    // 禁用顶点
    glDisableVertexAttribArray(positionHandle);
    if(nullptr != eglHelper){
        eglHelper->swapBuffers();
    }
}

ColorBlendOpengl::~ColorBlendOpengl() noexcept {

}

自定义使用颜色混合

上面说过默认混合公式为:

C_f = (C_src * S) + (C_dest * D)

既然说是默认,那就有方法改成非默认的,我们可以使用函数glBlendEquation选择不同的混合公式。

模式公式
GL_FUNC _ADDC_f = (C_src * S) + (C_dest * D)
GL_FUNC_SUBTRACTC_f = (C_src * S) – (C_dest * D)
GL_FUNC_REVERSE_SUBTRACTC_f = (C_dest * D) – (C_src * S)
GL_MINC_f = min(C_src , C_dest)
GL_MAXC_f = max(C_src , C_dest)

除了函数glBlendFunc之外,还可以利用下面的函数更加灵活地进行选择:

void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);

函数glBlendFuncSeparate的参数说明如下:

  • strRGB: 源颜色的混合因⼦
  • dstRGB: ⽬标颜⾊色的混合因⼦
  • strAlpha: 源颜色的 Alpha 因⼦
  • dstAlpha: ⽬标颜色的 Alpha 因⼦

也就是我们可以使用函数glBlendFunc指定源和目标 RGBA 值的混合函数,再使用函数glBlendFuncSeparate为 RGB 和 alpha 指定单独的混合因子。

颜色混合与抗锯齿

OpenGL 混合功能的另一个用途就是抗锯齿。大多数情况下一个独立的渲染片段将会映射到计算机屏幕上的一个像素。这些像素是一个微小的矩形,而这个矩形通常可以清除地看到两种颜色的分界,它们常常被称为锯齿,而图像一旦放大往往让人觉得图像分界不自然。 为了消除图元之间的锯齿状边缘,OpenGL使用混合功能来混合片段的颜色,也就是把像素的目标颜色与周围像素的颜色进行混合。

开启抗锯齿功能时,首先需要启动混合功能:

glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

同时还需要确保把混合方程式设置为GL_ADD。在启用混合功能并选择正确的混合函数以及混合方程式之后,可以选择调用 glEnable 函数对点、直线和多边形进行抗锯齿处理。

// Smooth out points
scss复制代码glEnable(GL_POINT_SMOOTH);
// Smooth out lines  
glEnable(GL_LINE_SMOOTH);    
// Smooth out polygon edges      
glEnable(GL_POLYGON_SMOOTH); 

好了,关于Opengl中的颜色混合就先介绍到这里,要想真正融会贯通,大家还是得多写代码实践测试。

关注我,一起进步,人生不止coding!!!

思想觉悟

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/29904.html

(0)

相关推荐

发表回复

登录后才能评论