分享
 
 
 

NeHe的opengl教程delphi版(8)----简单的透明

王朝delphi·作者佚名  2006-01-08
窄屏简体版  字體: |||超大  

{

呵呵,这两课早就翻译好了,一直没贴,大家久等了(有人再等吗?)

简单的透明

OpenGL中的绝大多数特效都与某些类型的(色彩)混合有关。

混色的定义为,将某个象素的颜色和已绘制在屏幕上与其对应的象素颜色相互结合。

至于如何结合这两个颜色则依赖于颜色的alpha通道的分量值,以及/或者所使用的混色函数。

Alpha通常是位于颜色值末尾的第4个颜色组成分量。

前面这些课我们都是用GL_RGB来指定颜色的三个分量。

相应的GL_RGBA可以指定alpha分量的值。

更进一步,我们可以使用glColor4f()来代替glColor3f()。

绝大多数人都认为Alpha分量代表材料的透明度。

这就是说,alpha值为0.0时所代表的材料是完全透明的。

alpha值为1.0时所代表的材料则是完全不透明的。

混色的公式

若您对数学不感冒,而只想看看如何实现透明,请跳过这一节。

若您想深入理解(色彩)混合的工作原理,这一节应该适合您吧。

『CKER的补充:其实并不难^-^。原文中的公式如下,CKER再唠叨一下吧。

其实混合的基本原理是就将要分色的图像各象素的颜色以及背景颜色均按照RGB规则各自分离之后,

根据-图像的RGB颜色分量*alpha值+背景的RGB颜色分量*(1-alpha值)

-这样一个简单公式来混合之后,最后将混合得到的RGB分量重新合并。』

公式如下:

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

OpenGL按照上面的公式计算这两个象素的混色结果。

小写的s和r分别代表源象素和目标象素。大写的S和D则是相应的混色因子。

这些决定了您如何对这些象素混色。

绝大多数情况下,各颜色通道的alpha混色值大小相同,

这样对源象素就有 (As, As, As, As),

目标象素则有1, 1, 1, 1) - (As, As, As, As)。

上面的公式就成了下面的模样:

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

这个公式会生成透明/半透明的效果。

OpenGL中的混色

在OpenGL中实现混色的步骤类似于我们以前提到的OpenGL过程。

接着设置公式,并在绘制透明对象时关闭写深度缓存。

因为我们想在半透明的图形背后绘制 对象。

这不是正确的混色方法,但绝大多数时候这种做法在简单的项目中都工作的很好。

Rui Martins 的补充: 正确的混色过程应该是先绘制全部的场景之后再绘制透明的图形。

并且要按照与深度缓存相反的次序来绘制(先画最远的物体)。

考虑对两个多边形(1和2)进行alpha混合,不同的绘制次序会得到不同的结果。

(这里假定多边形1离观察者最近,那么正确的过程应该先画多边形2,再画多边形1。

正如您再现实中所见到的那样,

从这两个<透明的>多边形背后照射来的光线总是先穿过多边形2,

再穿过多边形1,最后才到达观察者的眼睛。)

在深度缓存启用时,您应该将透明图形按照深度进行排序,

并在全部场景绘制完毕之后再绘制这些透明物体。否则您将得到不正确的结果。

我知道某些时候这样做是很令人痛苦的,但这是正确的方法。

我们将使用第七课的代码。

一开始先在代码开始处增加两个新的变量。出于清晰起见,我重写了整段代码。

}

Var

h_RC : HGLRC; // Rendering Context(着色描述表)。

h_DC : HDC; // Device Context(设备描述表)

h_Wnd : HWND; // 窗口句柄

h_Instance : HINST; // 程序Instance(实例)。

keys : Array[0..255] Of Boolean; // 用于键盘例程的数组

light : Boolean; // 光源的开/关

blend : Boolean; // Blending OFF/ON? ( 新增 )

lp : Boolean; // L键按下了么?

fp : Boolean; // F键按下了么?

bp : Boolean; // B 键按下了么? ( 新增 )

xrot : GLfloat; // X 旋转

yrot : GLfloat; // Y 旋转

xspeed : GLfloat; // X 旋转速度

yspeed : GLfloat; // Y 旋转速度

z : GLfloat = -5.0 f; // 深入屏幕的距离

LightAmbient : Array[0..3] Of GLfloat = (0.5, 0.5, 0.5, 1.0); //环境光参数 ( 新增 )

LightDiffuse : Array[0..3] Of GLfloat = (1.0, 1.0, 1.0, 1.0); // 漫射光参数 ( 新增 )

LightPosition : Array[0..3] Of GLfloat = (0.0, 0.0, 2.0, 1.0); // 光源位置 ( 新增 )

filter : GLuint; // 滤波类型

texture : Array[0..2] Of GLuint; // 3种纹理的储存空间

Procedure glGenTextures(n: GLsizei; Var textures: GLuint); stdcall; external

opengl32;

Procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external

opengl32;

Function gluBuild2DMipmaps(target: GLenum; components, width, height: GLint;

format, atype: GLenum; data: Pointer): Integer; stdcall; external glu32 name

'gluBuild2DMipmaps';

{

然后往下移动到 LoadGLTextures() 这里。

找到 if (TextureImage[0]=LoadBMP('Data/Crate.bmp'))

这一行。我们现在使用有色玻璃纹理来代替上一课中的木箱纹理。

if (TextureImage[0]=LoadBMP("Data/glass.bmp")); // 载入玻璃位图 ( 已修改 )

}

Function LoadTexture: boolean; // 载入位图并转换成纹理

Var

Status : boolean; // Status 指示器

TextureImage : Array[0..1] Of PTAUX_RGBImageRec; // 创建纹理的存储空间

Begin

Status := false;

ZeroMemory(@TextureImage, sizeof(TextureImage)); // 将指针设为 NULL

TextureImage[0] := LoadBMP('Walls.bmp');

If TextureImage[0] <> Nil Then

Begin

Status := TRUE; // 将 Status 设为 TRUE

glGenTextures(1, texture[0]); // 创建纹理

// 创建 Nearest 滤波贴图

glBindTexture(GL_TEXTURE_2D, texture[0]);

// 生成纹理

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // ( 新增 )

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // ( 新增 )

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,

TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,

TextureImage[0].data);

glBindTexture(GL_TEXTURE_2D, texture[1]); // 使用来自位图数据生成 的典型纹理

// 生成纹理

glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,

TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,

TextureImage[0].data);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 线形滤波

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 线形滤波

// 创建 MipMapped 纹理

glBindTexture(GL_TEXTURE_2D, texture[2]);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,

GL_LINEAR_MIPMAP_NEAREST); // ( 新增 )

gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0].sizeX,

TextureImage[0].sizey, GL_RGB, GL_UNSIGNED_BYTE,

TextureImage[0].data); //(新增) }

End;

If assigned(TextureImage[0]) Then // 纹理是否存在

If assigned(TextureImage[0].data) Then // 纹理图像是否存在

TextureImage[0].data := Nil; // 释放纹理图像占用的内存

TextureImage[0] := Nil; // 释放图像结构

result := Status; // 返回 Status

End;

{

在glInit()代码段加入以下两行。

第一行以全亮度绘制此物体,并对其进行50%的alpha混合(半透明)。

当混合选项打开时,此物体将会产生50%的透明效果。

第二行设置所采用的混合类型。

Rui Martins 的补充:

alpha通道的值为 0.0意味着物体材质是完全透明的。

1.0 则意味着完全不透明。

}

Procedure glInit(); // 此处开始对OpenGL进行所有设置

Begin

If (Not LoadTexture) Then // 调用纹理载入子例程

exit; // 如果未能载入,退出

glEnable(GL_TEXTURE_2D); // 启用纹理映射

glShadeModel(GL_SMOOTH); // 启用阴影平滑

glClearColor(0.0, 0.0, 0.0, 0.0); // 黑色背景

glClearDepth(1.0); // 设置深度缓存

glEnable(GL_DEPTH_TEST); // 启用深度测试

glDepthFunc(GL_LESS); // 所作深度测试的类型

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //高度优化的透视投影计算

glLightfv(GL_LIGHT1, GL_AMBIENT, @LightAmbient[0]); // 设置环境光

glLightfv(GL_LIGHT1, GL_DIFFUSE, @LightDiffuse[0]); // 设置漫射光

glLightfv(GL_LIGHT1, GL_POSITION, @LightPosition); // 光源位置

glEnable(GL_LIGHT1); // 启用一号光源

glColor4f(1.0, 1.0, 1.0, 0.5); // 全亮度, 50% Alpha 混合( 新增 )

glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 基于源象素alpha通道值的半透明混合函数 ( 新增 )

End;

{在接近第七课结尾处的地方找到下面的代码段。

If keys[VK_LEFT] Then //Left方向键按下了么?

yspeed := yspeed - 0.01; //若是, 减少yspeed

接着上面的代码,我们增加如下的代码。

这几行监视B键是否按下。

如果是的话,计算机检查混合选项是否已经打开。

然后将其置为相反的状态。

}

If (keys[ord('B')] And Not bp) Then //B 健按下且bp为 FALSE么?

Begin

bp := TRUE; // 若是, bp 设为 TRUE

blend := Not blend; // 切换混合选项的 TRUE / FALSE

If (blend) Then // 混合打开了么?

Begin

glEnable(GL_BLEND); // 打开混合

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

End

Else // 否则

Begin

glDisable(GL_BLEND); // 关闭混合

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

End;

End;

If (Not keys[ord('B')]) Then // B 键松开了么?

Begin

bp := FALSE; // 若是, bp设为 FALSE

End;

{

但是怎样才能在使用纹理贴图的时候指定混合时的颜色呢?很简单,

在调整贴图模式时,文理贴图的每个象素点的颜色都是由alpha通道参数

与当前地象素颜色相乘所得到的。

比如,绘制的颜色是 (0.5, 0.6, 0.4),

我们会把颜色相乘得到(0.5, 0.6, 0.4, 0.2)

(alpha参数在没有指定时,缺省为零)。

就是如此!OpenGL实现Alpha混合的确很简单!

}

{

原文注 (11/13/99)

我(NeHe)混色代码进行了修改,以使显示的物体看起来更逼真。

同时对源象素和目的象素使用alpha参数来混合,会导致物体的人造痕迹看起来很明显。

会使得物体的背面沿着侧面的地方显得更暗。

基本上物体会看起来很怪异。

我所用的混色方法也许不是最好的,但的确能够工作。

启用光源之后,物体看起来很逼真。

感谢Tom提供的原始代码,他采用的混色方法是正确的,

但物体看起来并不象所期望的那样吸引人:)

代码所作的再次修改是因为在某些显卡上glDepthMask()函数存在寻址问题。

这条命令在某些卡上启用或关闭深度缓冲测试时似乎不是很有效,

所以我已经将启用或关闭深度缓冲测试的代码转成老式的glEnable和glDisable。

纹理贴图的Alpha混合

用于纹理贴图的alpha参数可以象颜色一样从问题贴图中读取。

方法如下,您需要在载入所需的材质同时取得其的alpha参数。

然后在调用glTexImage2D()时使用GL_RGBA的颜色格式。

}

//运行一下看看效果

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有