OpenGL 超级宝典笔记 —— 混合

本文详细介绍了OpenGL中的颜色混合和深度测试原理。在关闭深度测试时,新的颜色值会覆盖旧的;开启深度测试后,只有通过深度测试的颜色才会更新。启用混合功能后,源颜色与目标颜色按特定方程式结合,产生新的颜色。通过调整混合因子和方程式,可以实现透明和半透明效果,如物体的倒影。示例代码展示了在不同光照和混合设置下创建反射效果的场景。
摘要由CSDN通过智能技术生成

混合

在正常情况下,OpenGL 渲染时会把颜色值输入到颜色缓冲区中,深度值输入到深度缓冲区中。如果我们关闭深度测试,那么新的颜色值会简单地覆盖已经存在于颜色缓冲区中的值。当开启深度测试时,颜色段只有在通过深度测试时,才会覆盖已经存在于颜色缓冲区中的值。在这两种情况下,在渲染时颜色值要么完全被废弃,要么就直接覆盖旧的颜色值。现在介绍一种新的方式,混合。

glEnable(GL_BLEND);

当开启混合时,输入的颜色值将会和已经存在于颜色缓冲区中的颜色进行组合。至于如何进行组合,取决于你为混合设置的参数。

组合颜色

首先需要了解两个概念:

  • 目标颜色:已经存在于颜色缓冲区中的颜色值。这个颜色包含红、绿、蓝三种成分,和可选的 alpha 值。
  • 源颜色:来自于渲染命令的颜色。包含红、绿、蓝和可选的 alpha 值成分。

当开启 OpenGL 的混合时,源颜色是如何与目标颜色进行结合的,取决于你设置的混合方程式。默认情况下的,混合方程如下:

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

在这里,Ci 是最终被计算出来的颜色,Cs 是源颜色,Cd 是目标颜色。而 S 和 D 分别是源混合因子和目标混合因子。这些混合因子通过:glBlendFunc (GLenum S, GLenum D); 设置。S 和 D 是枚举值,其枚举列表如下:(R、G、B、A 分别代表红色成分,绿色成分,蓝色成分,Alpha 成分)

FunctionRGB Blend FactorsAlpha Blend Factor
GL_ZERO

(0,0,0)

0
GL_ONE(1,1,1)1
GL_SRC_COLOR(Rs ,Gs ,Bs)As
GL_ONE_MINUS_SRC_COLOR(1,1,1)-(Rs,Gs ,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_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_CONST_COLOR(1,1,1)-(Rc,Gc,Bc)1-Ac
GL_CONST_ALPHA(Ac,Ac,Ac)Ac
GL_ONE_MINUS_CONSTANT_ALPHA(1,1,1)-(Ac,Ac,Ac)1-Ac
GL_SRC_ALPHA_SATURATE(f,f,f)*1

* f = min(A s , 1 – A d)

PS:上面表格的颜色值都是用浮点数来表示的。举例说明:

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

通过上面的调用来告诉 OpenGL 我们使用的混合因子。假设现在渲染命令传进来的源 RGBA 颜色值是 (1.0f,0.0f,1.0f,1.0f). 而在颜色缓冲区相同的位置已经有目标颜色 RGBA 值是 (0.0f, 1.0f, 0.0f, 1.0f).

代入公式:Ci = (Cs * S) + (Cd * D)

Cs=(1.0f,0.0f,1.0f,0.6f), GL_SRC_ALPHA 参数说明源混合因子取源颜色的 alpha 值,所以 S = 0.6f。Cd=(0.0f, 1.0f, 0.0f, 1.0f). GL_ONE_MINUS_SRC_ALPHA 说明了目标混合因子取 1-As,D = 1-0.6 = 0.4f;

所以代入计算得 (Cs * S) = (0.6f, 0.0f, 0.6f, 0.36f) , (Cd * D) = (0.0f, 0.4f, 0.0f, 0.4f)

Ci = (Cs * S) + (Cd * D) = (0.6f, 0.4f, 0.6f, 0.76f)

得到的颜色是近似于洋紫色。

上面表格的 GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR,GL_CONSTANT_ALPHA 和 GL_ONE_MINUS_CONSTANT_ALPHA 的值在默认情况下是 (0.0f, 0.0f, 0.0f, 0.0f) 但我们可以通过下面的函数修改:

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

颜色混合经常用于在不透明的前面画一个透明的物体。这样我们就可以先画好背景,然后再在上面画一个半透明的物体。可以制造一种,透过彩色玻璃看外面的风景的效果。下面的例子:(物体在光滑的彩色地面上的倒影)。

#include "gltools.h"
#include "math3d.h"

//光源位置

GLfloat lightPos[4] = { -100.0f, 100.0f, 50.0f, 1.0f };
GLfloat lightPosMirror[4] = {-100.0f, -100.0f, 50.0f, 1.0f};

GLfloat fNoLight[] = { 0.0f, 0.0f, 0.0f, 0.0f };
GLfloat fLowLight[] = { 0.25f, 0.25f, 0.25f, 1.0f };
GLfloat fBrightLight[] = { 1.0f, 1.0f, 1.0f, 1.0f };

void DrawGround()
{
  //画地面

  GLfloat fExtent = 20.0f;
  GLfloat fStep = 0.5f;
  GLfloat y = 0.0f;
  GLfloat fColor;
  GLfloat iStrip, iRun;
  GLint iBounce = 0;

  glShadeModel(GL_FLAT);
  for(iStrip = -fExtent; iStrip <= fExtent; iStrip += fStep)
  {
    glBegin(GL_TRIANGLE_STRIP);
    for(iRun = fExtent; iRun >= -fExtent; iRun -= fStep)
    {
      if((iBounce %2) == 0)
        fColor = 1.0f;
      else
        fColor = 0.0f;

      glColor4f(fColor, fColor, fColor, 0.5f);
      glVertex3f(iStrip, y, iRun);
      glVertex3f(iStrip + fStep, y, iRun);

      iBounce++;
    }
    glEnd();
  }
  glShadeModel(GL_SMOOTH);
}

void DrawWorld()
{
  glColor3f(1.0f, 0.0f, 0.0f);
  glPushMatrix();

    glTranslatef(0.0f, 0.5f, -3.5f);
    gltDrawTorus(0.25, 0.08, 68, 37);

  glPopMatrix();
}

void RenderScene()
{
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glEnable(GL_DEPTH_TEST);
  glPushMatrix();
    //设置光源的倒影
    glLightfv(GL_LIGHT0, GL_POSITION, lightPosMirror);

    glPushMatrix();
      //因为是镜像,所以要反过来绘制
      glFrontFace(GL_CW);          
      glScalef(1.0f, -1.0f, 1.0f);
      DrawWorld();
      glFrontFace(GL_CCW);
    glPopMatrix();
    //画地面时,关闭光源, 地面可见并均匀着色。
    glDisable(GL_LIGHTING);
    //打开混合,设置混合因子
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    //画地面
    DrawGround();
    //关闭混合效果
    glDisable(GL_BLEND);
    glEnable(GL_LIGHTING);

  glPopMatrix();
  //设置光源在左上角
  glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
  DrawWorld();

  glutSwapBuffers();
}


void ChangeSize(GLsizei w, GLsizei h)
{
  if (h == 0)
    h = 1;

  glViewport(0, 0, w, h);

  GLfloat faspect = (GLfloat)w/(GLfloat)h;

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluPerspective(35.0, faspect, 1.0f, 50.0f);

  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
  //往Y轴负方向平移一点
  glTranslatef(0.0f, -0.4f, 0.0f);
  glutPostRedisplay();

}

void SetupRC()
{
  glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
  //开启深度测试,剔除物体背面
  glEnable(GL_DEPTH_TEST);
  glEnable(GL_CULL_FACE);
  glCullFace(GL_BACK);
  //逆时针方向绘制的面为正面
  glFrontFace(GL_CCW);
  
  //开启光照,设置光源参数
  glEnable(GL_LIGHTING);
  glLightModelfv(GL_LIGHT_MODEL_AMBIENT, fNoLight);
  glLightfv(GL_LIGHT0, GL_AMBIENT, fLowLight);
  glLightfv(GL_LIGHT0, GL_DIFFUSE, fBrightLight);
  glLightfv(GL_LIGHT0, GL_SPECULAR, fBrightLight);
  glEnable(GL_LIGHT0);

  //开启颜色追踪,设置镜面加亮效果
  glEnable(GL_COLOR_MATERIAL);
  glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
  glMateriali(GL_FRONT, GL_SHININESS, 128);

}

int main(int args, char **argv)
{
  glutInit(&args, argv);
  glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
  glutInitWindowSize(800, 600);
  glutCreateWindow("Reflection");
  glutDisplayFunc(RenderScene);
  glutReshapeFunc(ChangeSize);
  SetupRC();
  glutMainLoop();
  return 0;
}

改变混合方程

OpenGL 的默认混合方程式:Ci = (Cs * S) + (Cd * D)

但我们可以通过函数:

void glBlendEquation(GLenum mode);

来改变混合方程。可选择的混合方程模式如下表:

还有一个更灵活的函数:

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

这个函数允许你分别指定 RGB 的混合方程和 alpha 成分的混合方程。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值