11、OpenGL - 混合(颜色混合)

混合


OpenGL 渲染时会把颜色存在颜色缓冲区中,每个片段的深度值也是放在深度缓冲区。
当深度缓冲区被关闭时,新的颜色将简单的覆盖原来颜色缓冲区存在的颜色值。
当深度缓冲区再次打开时,新的颜色片段只是当他们比原来的值更接近临近的裁剪屏幕才会替换原来的颜色片段


//开启混合

gl_Enable(GL_BIEND);

 

1、颜色混合

目标颜色:已经存储在颜色缓存区的颜色值(已经存在的颜色,旧颜色)
源颜色:作为当前渲染命令结果进入颜色缓冲区的颜色(新进来的颜色,新颜色)

(颜色混合可以这样理解,好比做一个滤镜效果。目标颜色就是我们当前原图的颜色值,是我们要渲染的目标。源颜色就是滤镜颜色。就好比是获取 一个数据源,然后根据这个数据源上的颜色值去渲染我们实际的目标图片,得到混合后的颜色)。

固定着色器 / 可编程着色器 -> 使用开关方式 -> 颜色混合(单纯的2个图层重叠进行混合)
可编程着色器 -> 片元着色器 -> 处理图片原图颜色 + 颜色(颜色值)-> 进行颜色混合方程式计算 -> 套用公式

当混合功能被开启的时候,源颜色和目标颜色的组合方式是混合方程式控制的。在默认情况下,混合方程式如下所示:

Cf = (Cs * S) + (Cd * D);

  • Cf:最终计算参数的颜色
  • Cs:源颜色
  • Cd:目标颜色
  • S:源混合因子,源Alpha 混合因子
  • D:目标混合因子,目标 Alpha 混合因子

 

2、设置混合因子

使用  glBlendFunc 函数
 

//S:源混合因子
//D:目标混合因子
glBlendFunc(Glenum S, GLenum D);
函数  RGB混合因子Alpha混合因子
GL_ZERO                        (0, 0, 0)                0
GL_ONE                       (1, 1, 1)                1
GL_SRC_COLOR                (Rs, Gs, Bs)            As
GL_ONE_MINUS_SCR_COLOR    (1, 1, 1) - (Rd, Gd, Bs)    1 - As
GL_DST_COLOR                (Rd, Gd, Bd)            Ad
GL_ONE_MINUS_DST_COLOR        (1, 1, 1) - (Rd, Gd, Bd)    1 - Ad
GL_SRC_ALPHA                (As, As, As)            As
GL_ONE_MINUS_SCR_ALPHA        (1, 1, 1) - (As, As, As)    1 - As
GL_DST_ALPHA                (Ad, Ad, Ad)            Ad
GL_ONE_MINUS_DST_ALPHA        (1, 1, 1) - (Ad, Ad, Ad)    1 - Ad
GL_CONSTANT_COLOR            (Rc, Gc, Bc)            Ac
GL_ONE_MINUS_CONSTANT_ALPHA (1, 1, 1) - (Rc, Gc, Bc)  1 - Ac
GL_CONSTANT_ALPHA            (Ac, Ac, Ac)            Ac
GL_ONE_MINUS_CONSTANT_ALPHA(1, 1, 1) - (Ac, Ac, Ac)    1 - Ac
GL_SRC_ALPHA_STURATE       (f, f, f) *f = min(As, 1-Ad) 1

表中R、G、B、A 分别代表 红、绿、蓝、alpha
表中下标S、D,分别代表源、⽬标
表中C 代表常量颜⾊(默认⿊色)


举例:下面通过一个常用的混合函数组合来说明问题:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

如果颜色缓存区已经有一种颜色红色(1.0f, 0.0f, 0.0f, 0.0f),这个目标颜色Cd,如果在这上面用一种alpha 为0.6 的蓝色(0.0f, 0.0f, 1.0f, 0.6f)
Cd(目标颜色) = (1.0f, 0.0f, 0.0f, 0.0f);
Cs(源颜色) = (0.0f, 0.0f, 1.0f, 0.6f);
S = 源alpha 值 = 0.6f;
D = 1 - 源 alpha 值 = 1-0.6f = 0.4f;
方程式
Cf = (Cs * S) + (Cd * D)
等价于
(Blue * 0.6f) + (Red * 0.4f)

颜色混合总结
刚刚的例子,最终颜色是以原先的红色(目标颜色)与 后来的蓝色(源颜色)进行组合。
源颜色的 alpha 值越高,添加的蓝色颜色成分越高,目标颜色所保留的成分就会越少。

混合函数经常用于实现在其他一些不透明的物体前面绘制一个透明物体的效果。

 

3、混合方程式

修改颜色混合方程式。默认混合方程式

Cf = (Cs * S) + (Cd * D)

 

实际上不止一种颜色混合方程式,OpenGL有5个不同的方程式进行选择。

//选择混合方程式的函数

glbBendEquation(Glenum mode);
模式     函数
GL_FUNC_ADDCf = (Cs * S) + (Cd * D)
GL_FUNC_SUBTRACT Cf = (Cs * S) - (Cd * D)
GL_FUNC_REVERSE_SUBTRACT Cf = (Cd * D) - (Cs * S)
GL_MIN        Cf = min(Cs, Cd)
GL_MAX Cf = max(Cs, Cd)

常用混合颜色
默认初始化为黑色 (0.0f, 0.0f, 0.0f, 0.0f)。可以修改这个常量混合颜色
 

void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha );

glBlendFuncSeparate函数
除了能使⽤OpenGL内置的混合因⼦,还可以有更灵活的选择。

//strRGB: 源颜色的混合因⼦
//dstRGB: 目标颜⾊的混合因⼦
//strAlpha: 源颜⾊的Alpha因⼦
//dstAlpha: 目标颜⾊的Alpha因⼦
void glBlendFuncSeparate(GLenum strRGB, GLenum dstRGB , GLenum strAlpha, GLenum dstAlpha);


glBlendFuncSeparate 注意
glBlendeFunc 指定源和目标 RGBA 值的混合函数;但是 glBlendFuncSeparate 函数则允许为RGB 和 alpha 成分单独指定混合函数

在混合因子表中

  • GL_CONSTANT_COLOR,
  • GL_ONE_MINUS_CONSTANT_COLOR
  • GL_CONSTANT_ALPHA
  • GL_ONE_MINUS_CONSTANT

值允许混合方程式引入一个常量混合颜色。

 

4、demo

详细代码参见Demo  颜色混混 demo

Demo地址 -> OpenGLDemos -> 4.ColorMixing

画四个固定的矩形,中间画一个可以移动的矩形,设置矩形的透明度为0.5,移动中间的矩形与其他四个矩形重叠,看一下混合的效果

#include <stdio.h>
//颜色组合
#include "GLTools.h"
#include "GLShaderManager.h"

#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLBatch squareBatch;//批次类
GLBatch greenBatch;
GLBatch redBatch;
GLBatch blueBatch;
GLBatch blackBatch;

GLShaderManager shaderManager;//着色器管理器

GLfloat blockSize = 0.2f;
GLfloat vVerts[] = {
    -blockSize, -blockSize, 0.0f,
    blockSize,  -blockSize, 0.0f,
    blockSize,  blockSize,  0.0f,
    -blockSize, blockSize,  0.0f,
};
//main手动触发
//设置窗口背景颜色
//初始化存储着色器
//设置图形顶点数据
//利用GLBatch 三角形批次类,将数据传递给着色器
void SetupRC()
{
//    设置窗口颜色
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
//    初始化着色器
    shaderManager.InitializeStockShaders();
    
//    绘制1个移动的矩形
    squareBatch.Begin(GL_TRIANGLE_FAN, 4);
    squareBatch.CopyVertexData3f(vVerts);
    squareBatch.End();
    
//    绘制4个固定矩形
//  第一个矩形
    GLfloat vBlock1[] = {
        0.25f, 0.25f, 0.0f,
        0.75f, 0.25f, 0.0f,
        0.75f, 0.75f, 0.0f,
        0.25f, 0.75f, 0.0f,
    };
    
    greenBatch.Begin(GL_TRIANGLE_FAN, 4);
    greenBatch.CopyVertexData3f(vBlock1);
    greenBatch.End();
    
//    第二个矩形
    GLfloat vBlock2[] = {
           -0.75f, 0.25f, 0.0f,
           -0.25f, 0.25f, 0.0f,
           -0.25f, 0.75f, 0.0f,
           -0.75f, 0.75f, 0.0f,
       };
       
       redBatch.Begin(GL_TRIANGLE_FAN, 4);
       redBatch.CopyVertexData3f(vBlock2);
       redBatch.End();
    
    //    第三个矩形
    GLfloat vBlock3[] = {
           -0.75f, -0.75f, 0.0f,
           -0.25f, -0.75f, 0.0f,
           -0.25f, -0.25f, 0.0f,
           -0.75f, -0.25f, 0.0f,
       };
       
       blueBatch.Begin(GL_TRIANGLE_FAN, 4);
       blueBatch.CopyVertexData3f(vBlock3);
       blueBatch.End();
    
    //    第四个矩形
    GLfloat vBlock4[] = {
           0.25f, -0.75f, 0.0f,
           0.75f, -0.75f, 0.0f,
           0.75f, -0.25f, 0.0f,
           0.25f, -0.25f, 0.0f,
       };
       
       blackBatch.Begin(GL_TRIANGLE_FAN, 4);
       blackBatch.CopyVertexData3f(vBlock4);
       blackBatch.End();
}
//上下左右键控制移动
void SpecialKeys(int key, int x, int y)
{
    GLfloat stepSize = 0.025f;
    
    GLfloat blockX = vVerts[0];
    GLfloat blockY = vVerts[7];
    
    if (key == GLUT_KEY_UP)
        blockY += stepSize;
    
    if (key == GLUT_KEY_DOWN)
        blockY -= stepSize;
    
    if (key == GLUT_KEY_LEFT)
        blockX -= stepSize;
    
    if (key == GLUT_KEY_RIGHT)
        blockX += stepSize;
    
    if (blockX < -1.0f) blockX = -1.0f;
    if (blockX > (1.0f - blockSize * 2)) blockX = 1.0f - blockSize * 2;
    if (blockY < (-1.0f + blockSize * 2)) blockY = -1.0f + blockSize * 2;
    if (blockY > 1.0f) blockY = 1.0f;
    
    vVerts[0] = blockX;
    vVerts[1] = blockY - blockSize * 2;
    
    vVerts[3] = blockX + blockSize * 2;
    vVerts[4] = blockY - blockSize * 2;
    
    vVerts[6] = blockX + blockSize * 2;
    vVerts[7] = blockY;
    
    vVerts[9] = blockX;
    vVerts[10] = blockY;
    
    squareBatch.CopyVertexData3f(vVerts);
    glutPostRedisplay();
}
void RenderScene(void)
{
//    清理缓冲区 (颜色、深度、模板缓冲区)
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
    
//    1、开启混合
    glEnable(GL_BLEND);
//    2、开启组合函数 计算混合颜色因子
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
//   定义4种颜色
    GLfloat vRed[]   = { 1.0f, 0.0f, 0.0f, 1.0f};
    GLfloat vRed1[]  = { 1.0f, 0.0f, 0.0f, 0.5f};
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 0.5f};
    GLfloat vBlue[]  = { 0.0f, 0.0f, 1.0f, 0.5f};
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 0.5f};
    
//    3、使用着色器管理器
//    使用单位着色器
//    参数1:简单的使用默认笛卡尔坐标系(-1,1),所有片段都应用一种颜色。GLT_SHADER_IDENTITY
//    参数2:着色器颜色
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed);
    
//    4、容器类开始绘制
    squareBatch.Draw();
    
//    5、召唤场景的时候,将4个固定矩形绘制好
//    使用单位着色器
//    参数1:简单的使用默认笛卡尔坐标系(-1,1),所有片段都应用一种颜色。GLT_SHADER_IDENTITY
//    参数2:着色器颜色
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vGreen);
    greenBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vRed1);
    redBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlue);
    blueBatch.Draw();
    
    shaderManager.UseStockShader(GLT_SHADER_IDENTITY, vBlack);
    blackBatch.Draw();
    
//    5、关闭混合
    glDisable(GL_BLEND);
    
//    同步绘制命令
    glutSwapBuffers();
}

void ChangeSize(int w, int h)
{
    glViewport(0, 0, w, h);
}
int main(int argc, char * argv[])
{
    gltSetWorkingDirectory(argv[0]);
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize(800, 600);
    glutCreateWindow("移动矩形,观察外观");
    
    GLenum err = glewInit();
    if (GLEW_OK != err)
    {
        fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    glutReshapeFunc(ChangeSize);
    glutDisplayFunc(RenderScene);
    glutSpecialFunc(SpecialKeys);
    
    SetupRC();
    
    glutMainLoop();
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值