NeHe OpenGL教程 第八课 混合

完全的透明(simple transparency)

在OpenGL中很多特定的效果依赖于一些类型的混合。混合用来把将要绘制到屏幕上的像素的颜色和已经绘制到屏幕上的像素的颜色结合起来。颜色如何混合依赖于颜色的alpha值 和/或使用的混合函数。alpha通常是指定的颜色组成的第四个参数。过去,你使用GL_RGB 来说明颜色是由3种成分组成的。GL_RGBA 能够用来指定alpha值。另外,我们可以使用glColor4f() 函数代替glColor3f() 函数。

大部分人认为alpha是材料的透明度。alpha值等于0.0f意味着这种材料是完全透明的。而1.0f则意味着完全不透明。

 

混合方程式(The Blending Equation )

如果你不太喜欢研究数学,只是想知道如何实现透明,可以跳过这部分。如果你想理解混合是如何实现的,仔细阅读下面的内容。

(Rs Sr + Rd Dr, Gs Sg + Gd Dg, Bs Sb + Bd Db, As Sa + Ad Da)

OpenGL将使用上面的方程式计算两种像素的混合结果。下标s和d说明了源像素和目标像素。S和D是混合元素。这些值指示了你想如何混合这些像素。最常用的S和D的值是(As, As, As, As) (AKA source alpha) 对于 S,和(1, 1, 1, 1) - (As, As, As, As) (AKA one minus src alpha) 对于 D。它将产生一个向下面这样的混合方程式:

(Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bd (1 - As), As As + Ad (1 - As))

这个方程式将产生透明的/半透明的风格的效果。

 

OpenGL中的混合

我们像启用其他功能一样启用混合。然后我们设置混合方程式,当绘制透明物体的时候关闭深度缓存,因为我们仍然想在半透明的图形后面绘制物体。这并不是最合理的使用混合的方式,但是大部分时候应用于一些简单的物体,它的效果很好。Rui Martins Adds: 在你绘制完整个场景之后,正确绘制所有透明(alpha < 1.0f)的多边形的方法是,以逆向深度的顺序(从最远处开始)绘制它们。这是因为混合俩个多边形(1和2)的效果因为绘制的顺序不同,结果也不同。例如:假设多边形1更接近观察者,正确的方法应该是首先绘制多边形2,然后在绘制多边形1。如果你观察它们,这样更真实,所有从这两个多边形(透明的)后面照射过来的光线在到达观察者的眼睛之前,都是先通过多边形2,然后再通过多边形1。你应该按照深度把这些多边形排好序,在绘制完整个场景之后绘制它们,使用启用的深度缓存,否则你得到的将是错误的结果。我知道有时这很辛苦,但是这是正确的方式。

我们将使用上节课的代码。我们首先在程序的最上面添加两个变量。为了清晰起见我将重写整个部分的代码。

#include <windows.h>  // Windows头文件
#include    <stdio.h>  // 标准输入/输出头文件
#include <gl\gl.h>    // OpenGL32库头文件
#include <gl\glu.h>   // GLu32库头文件
#include <gl\glaux.h> // GLaux库头文件
HGLRC      hRC=NULL;         // 永久的渲染上下文( Rendering Context)
HDC                hDC=NULL;         // 私有的GDI设备上下文( GDI Device Context)
HWND              hWnd=NULL;        // 获得我们窗口的句柄
HINSTANCE   hInstance;        // 获得应用程序的实例
bool   keys[256];              // 用于键盘行为的数组
bool   active=TRUE;            // 窗口活动标记,默认设置为TRUE
bool   fullscreen=TRUE;        // 全屏标记,默认设置为全屏

BOOL    light;    // 光照打开/关闭

BOOL    blend;   //混合 关闭/打开(NEW)
BOOL    lp;    // L键是否按下
BOOL    fp;      // F键是否按下

BOOL    bp;    //B键是否按下(NEW)

GLfloat xrot;        // x轴上的转动角度
GLfloat yrot;       // y轴上的转动角度
GLfloat xspeed;        // x轴上的转动速度
GLfloat yspeed;        // y轴上的转动速度
GLfloat z=-5.0f;      //在屏幕里的深度

GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f };    // 环绕光

GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f };              // 散射光

GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f };                 //光源位置

GLuint  filter;               // 使用哪种纹理
GLuint  texture[3];          // 存储三种纹理
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);    // 声明WndProc函数

然后到LoadGLTextures()函数中。找到if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))这行代码。把这行代码改为下面的代码。我们在这节课中使用玻璃属性的纹理代替条板箱纹理。

if (TextureImage[0]=LoadBMP("Data/glass.bmp"))  // 加载Glass Bitmap ( MODIFIED )

在InitGL() 函数中加入下面两行代码。第一行代码把物体的绘制明亮度设置为50%alpha(不透明)最强光。这意味着当启用混合时,物体将为50%透明。第二行代码使用我们使用的混合类型。

Rui Martins Adds:alpha值等于0.0f意味着这种材料是完全透明的。而1.0f则意味着完全不透明。

glColor4f(1.0f,1.0f,1.0f,0.5f);         // 最强亮度, 50% Alpha ( NEW )
glBlendFunc(GL_SRC_ALPHA,GL_ONE);   // 基于源alpha值用于半透明的混合函数 ( NEW )

观察下面这段代码,你可以在第七课最后面找到。

if (keys[VK_LEFT])              // Left Arrow 键是否被按下?
{
    yspeed-=0.01f;              // 减小yspeed值
}

在这段代码的正下方,我们将加入下面的代码。下面的代码用来监视'B'键是否被按下。如果它被按下,检查混合是关闭还是打开的。如果混合是打开的,我们就关掉它。如果混合是关闭的,我们就打开它。

if (keys['B'] && !bp)               // B 键是否被按下,bp是不是等于 FALSE?
{
    bp=TRUE;                // 如果是, bp 设置为TRUE
    blend = !blend;             // 切换 blend 为TRUE / FALSE    
    if(blend)               // blend 是否为TRUE?
    {
        glEnable(GL_BLEND);     // 打开混合

        glDisable(GL_DEPTH_TEST);   // 关闭深度测试、

    }
    else                    // 否则
    {
        glDisable(GL_BLEND);        // 关闭混合

        glEnable(GL_DEPTH_TEST);    // 打开深度测试

    }
}
if (!keys['B'])                 // B 键是否被松开?
{
    bp=FALSE;               // 如果是, bp 设置为FALSE
}

如果我们想使用纹理映射,如何指明使用的颜色呢?很简单,在处理纹理模式时,纹理映射的每一个像素都乘以当前的颜色。所以,如果我们要绘制的颜色是(0.5, 0.6, 0.4),我们乘以它,得到(0.5, 0.6, 0.4, 0.2) (如果没有指明,alpha假设为1.0f)。(原文:But how can we specify the color if we are using a texture map? Simple, in modulated texture mode, each pixel that is texture mapped is multiplied by the current color. So, if the color to be drawn is (0.5, 0.6, 0.4), we multiply it times the color and we get (0.5, 0.6, 0.4, 0.2) (alpha is assumed to be 1.0 if not specified).

)

就是这样。在OpenGL中混合实际上非常简单。

Note (11/13/99)

我(NeHe)修改了混合部分的代码,这样使用这个物体看起来更加真实。源使用alpha值然后去实现混合将会产生人工制作的效果。会导致背面更黑,以及侧面。物体可能开起来有点奇怪。我使用的混合方式不是最好的,但它可以实现混合的效果,当启用光照的时候,物体看起来很真实。谢谢Tom提供的原始代码,他使用的混合alpha值的方式是很合理的,但是不像人们想象的那样吸引人。

这段代码又做了一些修改,来处理有些显卡对于glDepthMask()函数支持不够理想的问题。看起来好像这条命令在某些显卡上不能启用或禁止深度测试,所以我又改回了过去常用的glEnable 和glDisable函数来启用和禁用深度测试。

(原文:

I ( NeHe ) have modified the blending code so the output of the object looks more like it should. Using Alpha values for the source and destination to do the blending will cause artifacting. Causing back faces to appear darker, along with side faces. Basically the object will look very screwy. The way I do blending may not be the best way, but it works, and the object appears to look like it should when lighting is enabled. Thanks to Tom for the initial code, the way he was blending was the proper way to blend with alpha values, but didn't look as attractive as people expected :)

The code was modified once again to address problems that some video cards had with glDepthMask(). It seems this command would not effectively enable and disable depth buffer testing on some cards, so I've changed back to the old fashioned glEnable and Disable of Depth Testing.

)

 

纹理映射的alpha(Alpha from Texture Map)

用于透明的alpha值可以像颜色值一样从纹理映射中读取出来,想要这样做,你将需要把alpha值添加到你加载的图像中,然后在调用glTexImage2D()函数时使用GL_RGBA 颜色格式。

(原文:

The alpha value that is used for transparency can be read from a texture map just like color, to do this, you will need to get alpha into the image you want to load, and then use GL_RGBA for the color format in calls to glTexImage2D().

)

创建一个OpenGL窗口: 在这个教程里,我将教你在Windows环境中创建OpenGL程序.它将显示一个空的OpenGL窗口,可以在窗口和全屏模式下切换,按ESC退出.它是我们以后应用程序的框架. 理解OpenGL如何工作非常重要,你可以在教程的末尾下载源程序,但我强烈建议你至少读一遍教程,然后再开始编程. 2.你的第一个多边形: 在第一个教程的基础上,我们添加了一个三角形和一个四边形。也许你认为这很简单,但你已经迈出了一大步,要知道任何在OpenGL中绘制的模型都会被分解为这两种简单的图形。 读完了这一课,你会学到如何在空间放置模型,并且会知道深度缓存的概念。 3.添加颜色: 作为第二课的扩展,我将叫你如何使用颜色。你将理解两种着色模式,在左图中,三角形用的是光滑着色,四边形用的是平面着色。 注意三角形上的颜色是如何混合的。 颜色为OpenGlL 工程增加很多。通过理解平面着色(flat coloring)和平滑着色(smooth coloring),你能显著的改善你的OpenGL Demo的样子。 4.旋转: 在这一课里,我将教会你如何旋转三角形和四边形。左图中的三角形沿Y轴旋转,四边形沿着X 轴旋转。 这一章将引入两个变量, rtri 被用来存储三角形的角度, rquad存储四边形的角度。 和容易创建一个多边形组成的场景。让这些物体动起来是整个场景变得生动起来。在后面的课程钟我将教给你如何绕屏幕上的一个点旋转物体,使得物体绕屏幕而不是它的轴转动。 5.3D形体: 既然我们已经领会到多边形,方形,色彩和旋转。现在该建立3D物体了。我将使用多边形和矩形c创建3D物体。这次我们将扩展上一章的教程,并且将三角形转换成一个彩色的棱锥,把正方形变为一个实心正方体。棱锥使用混合色,正方体每个面使用一种颜色。在3D空间创建物体可能很费时间,但是所获得的结果(收获)值得这样做。充分发挥你的想象力吧。 6.纹理映射: 你想要它,它现在就在这里了,那就是 ... 纹理映射!!!在这一章我将教会你如何将一幅位图(bitmap)映射到正方体的六个面上去。我们将使用第一章的OpenGL代码来创建工程。创建一个空的窗口比修改上一课的代码更容易。 你将会发现第一章的代码在对于快速创建工程来说是及其有价值的。第一章的代码为你设置好了一切,你所需要做的只是集中精力为效果编程。 7.纹理滤波, 光照和键盘控制: 好的,我希望到现在你已经理解了所有的东西,因为这是一个巨大的教程。我想教给你两个新的方法来过滤(filter)你的纹理,简单的光照,键盘控制并且还可能更多 :) .如果你对到这一课为止你所学的东西并不充满信心,那就回头复习一下。玩一下其它课程的代码,不要操之过急。最好专心把每一课学好,而不是蜻蜓点水,只知道如何把东西做出来。 8.混合 有理由等一下,一个来自很酷的Hypercosm的程序员伙伴问(我)他是否可以写一章关于混合教程第八课通常正是讲混合的,所以太巧了。这一章教程扩展了第七章。混合是一项很酷的技术 .. 我希望你们能好好享受这一章教程。这一章的作者是Tom Stanis他在这制作一章上花费了很多精力,所以让他知道你觉得怎么样。混合可不是一个好讲的话题。 9.在3D空间中移动位图: 这一章覆盖了一些你们要求的主题,你想知道如何移动你在3D屏幕空间上创造的物体。你想要知道如何在屏幕上绘制一幅位图,并且位图的黑色部分不会覆盖它后面的东西。你想要简单的动画,想要更多的混合的应用,这一章将教会你所有这些。You'll notice there's no spinning boxes(yaker:很惭愧这一句我不是很明白)。前面的课程覆盖了OpenGL的基础,每一章都基于前面的内容。前面的课程涵盖了基础的OpenGL,每一课都是在前一课的基础上创建的。这一课是前面几课知识的综合,当你学习这课时,请确保你已经掌握了前面几课的知识。 10.加载3D世界,并在其中漫游: 你一直期待的教程来了!这一章友一个叫Lionel Brites的伙伴制作。这一课里你讲学到如何导入一个3D世界。代码仍然使用第一章的,但是,课程页面只是解释了新的部分,包括导入3D场景,在3D世界中移动。下载VC++代码并且在你阅读教程的同时阅读代码。按[B]键控制混合,[F]键控制滤波,[L]键控制光照(但光并不随场景移动),还有[Page UP]和[Page Down]键。我希望你能喜欢Lionel对网站的贡献。我有空的时候我会让这个教程更容易学习。 11.旗帜效果
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值