分享
 
 
 

NeHe的opengl教程delphi版(9)----星星

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

{

欢迎进入第九课。到现在为止,您应该很好的理解OpenGL了。

『CKER:如果没有的话,一定是我翻译的罪过......』。

( myling补充道:我的罪过更大,呵呵)

您已经学会了设置一个OpenGL窗口的每个细节。

学会在旋转的物体上贴图并打上光线以及混色(透明)处理。

这一课应该算是一课中级教程。

您将学到如下的知识:在3D场景中移动位图,并去除位图上的黑色象素(使用混色)。

接着为黑白纹理上色,最后您将学会创建丰富的色彩,

并把上过不同色彩的纹理相互混合,得到简单的动画效果。

我们在第一课的代码基础上进行修改。先在程序源码的开始处增加几个变量。

出于清晰起见,我重写了整段代码。

}

Var

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

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

h_Wnd : HWND; // 窗口句柄

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

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

{下列这几行新加的。

twinkle和 tp是布尔变量, 表示它们只能设为 TRUE 或 FALSE。

twinkle用来跟踪 闪烁 效果是否启用。

tp用来检查 'T'键有没有被按下或松开.

(按下时 tp=TRUE, 松开时 tp=FALSE).}

twinkle : Boolean; // 闪烁的星星 (新增)

tp : Boolean; // 'T' 按下了么? (新增)

{现在我们来创建一个结构。

结构这词听起来有点可怕,但实际上并非如此。(就是delphi的纪录类型)

一个结构使用一组简单类型的数据 (以及变量等)来表达较大的具有相似性的数据组合。

我们知道我们在保持对星星的跟踪。

您可以看到下面的就是 stars;

每个星星有三个整型的色彩值。一个红色 (r), 一个绿色 (g), 以及一个蓝色 (b).

此外,每个星星离屏幕中心的距离不同,

而且可以是以屏幕中心为原点的任意360度中的一个角度。

dist的浮点数来保持对距离 的跟踪.

angle的浮点数保持对星星角度值的跟踪。

因此我们使用了一组数据来描述屏幕上星星的色彩, 距离, 和角度。

不幸的是我们不止对一个星星进行跟踪。

但是无需创建 50 个红色值、 50 个绿色值、 50 个蓝色值、 50 个距离值

以及 50 个角度值,而只需创建一个数组star。}

Type

stars = Record // 为星星创建一个结构,结构命名为stars

r, g, b: integer; // 星星的颜色

dist: GLfloat; // 星星距离中心的距离

angle: GLfloat; // 当前星星所处的角度

End;

Var

star : Array[0..49] Of stars; // 使用 'stars' 结构生成一个包含 50个元素的 'star'数组

{接下来我们设置几个跟踪变量:

星星离观察者的距离变量(zoom),

我们所见到的星星所处的角度(tilt),

以及使闪烁的星星绕Z轴自转的变量spin。

loop变量用来绘制50颗星星。

texture[1]用来存放一个黑白纹理。

如果您需要更多的纹理的话,

您应该增加texture数组的大小至您决定采用的纹理个数。

}

zoom : GLfloat = -15.0; // 星星离观察者的距离

tilt : GLfloat = 90.0; // 星星的倾角

spin : GLfloat; // 闪烁星星的自转

loop : GLuint; // 全局l Loop 变量

texture : Array[0..1] Of GLuint; // 存放一个纹理

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

opengl32;

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

opengl32;

{

紧接着上面的代码就是我们用来载入纹理的代码。

我不打算再详细的解释这段代码。

这跟我们在第六、七、八课中所用的代码是一模一样的。

这一次载入的位图叫做star.bmp。

这里我们使用glGenTextures(1, &texture[0]),

来生成一个纹理。纹理采用线性滤波方式。

}

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('Star.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);

End;

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

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

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

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

result := Status; // 返回 Status

End;

{

在glInit()中设置OpenGL的渲染方式。这里不打算使用深度测试,

如果您使用第一课的代码的话,

请确认是否已经去掉了 glDepthFunc(GL_LEQUAL)和 glEnable(GL_DEPTH_TEST)。

否则,您所见到的效果将会一团糟。

这里我们使用了纹理映射,

因此请您确认您已经加上了这些第一课中所没有的代码。

您会注意到我们通过混色来启用了纹理映射。

}

Procedure glInit();

Begin

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

exit; // 如果未能载入,退出( 新增 )

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

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

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

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

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精细的透视修正

glBlendFunc(GL_SRC_ALPHA, GL_ONE); // 设置混色函数取得半透明效果

glEnable(GL_BLEND); // 启用混色

{以下是新增的代码。

设置了每颗星星的起始角度、距离、和颜色。

您会注意到修改结构的属性有多容易。

全部50颗星星都会被循环设置。

要改变star[1]的角度我们所要做的只是star[1].angle=某个数值;

就这么简单!}

For loop := 0 To 49 Do // 创建循环设置全部星星

Begin

star[loop].angle := 0.0; // 所有星星都从零角度开始

{第loop颗星星离中心的距离是将loop的值除以星星的总颗数,然后乘上5.0。

基本上这样使得后一颗星星比前一颗星星离中心更远一点。

这样当loop为50时(最后一颗星星),loop 除以 num正好是1.0。

之所以要乘以5.0是因为1.0*5.0 就是 5.0。

『CKER:废话,废话!这老外怎么跟孔乙己似的!:)』

5.0已经很接近屏幕边缘。我不想星星飞出屏幕,5.0是最好的选择了。

当然如果如果您将场景设置的更深入屏幕里面的话,

也许可以使用大于5.0的数值,但星星看起来就更小一些(都是透视的缘故)。

您还会注意到每颗星星的颜色都是从0~255之间的一个随机数。

也许您会奇怪为何这里的颜色得取值范围不是OpenGL通常的0.0~1.0之间。

这里我们使用的颜色设置函数是glColor4ub,而不是以前的glColor4f。

ub意味着参数是Unsigned Byte型的。

一个byte的取值范围是0~255。

这里使用byte值取随机整数似乎要比取一个浮点的随机数更容易一些。

}

star[loop].dist := (Trunc(loop) / 50) * 5.0; // 计算星星离中心的距离

star[loop].r := random(256); // 为star[loop]设置随机红色分量

star[loop].g := random(256); // 为star[loop]设置随机红色分量

star[loop].b := random(256); // 为star[loop]设置随机红色分量

End;

End;

{

现在我们转入glDraw()绘图代码。

如果您使用第一课的代码,删除旧的DrawGLScene代码,只需将下面的代码复制过去就行了。

实际上,第一课的代码只有两行,所以没太多东西要删掉的。

}

Procedure glDraw();

Begin

glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存

glBindTexture(GL_TEXTURE_2D, texture[0]); // 选择纹理

For loop := 0 To 49 Do // 循环设置所有的星星

Begin

glLoadIdentity(); // 绘制每颗星星之前,重置模型观察矩阵

glTranslatef(0.0, 0.0, zoom); // 深入屏幕里面 (使用 'zoom'的值)

glRotatef(tilt, 1.0, 0.0, 0.0); // 倾斜视角(使用 'tilt'的值)

{

现在我们来移动星星。

星星开始时位于屏幕的中心。

我们要做的第一件事是把场景沿Y轴旋转。

如果我们旋转90度的话,X轴不再是自左至右的了,他将由里向外穿出屏幕。

为了让大家更清楚些,举个例子。假想您站在房子中间。

再设想您左侧的墙上写着-x,前面的墙上写着-z,

右面墙上就是+x咯,您身后的墙上则是+z。

加入整个房子向右转90度,但您没有动,那么前面的墙上将是-x而不再是-z了。

所有其他的墙也都跟着移动。-z出现在右侧,+z出现在左侧,+x出现在您背后。

神经错乱了吧?通过旋转场景,我们改变了x和z平面的方向。

第二行代码沿x轴移动一个正值。

通常x轴上的正值代表移向了屏幕的右侧(也就是通常的x轴的正向),

但这里由于我们绕y轴旋转了坐标系,x轴的正向可以是任意方向。

如果我们转180度的话,屏幕的左右侧就镜像反向了。

因此,当我们沿 x轴正向移动时,可能向左,向右,向前或向后。

}

glRotatef(star[loop].angle, 0.0, 1.0, 0.0); //旋转至当前所画星星的角度

glTranslatef(star[loop].dist, 0.0, 0.0); // 沿X轴正向移动

{

接着的代码带点小技巧。

星星实际上是一个平面的纹理。

现在您在屏幕中心画了个平面的四边形然后贴上纹理,这看起来很不错。

一切都如您所想的那样。但是当您当您沿着y轴转上个90度的话,

纹理在屏幕上就只剩右侧和左侧的两条边朝着您。 看起来就是一条细线。

这不是我们所想要的。我们希望星星永远正面朝着我们,而不管屏幕如何旋转或倾斜。

我们通过在绘制星星之前,抵消对星星所作的任何旋转来实现这个愿望。

您可以采用逆序来抵消旋转。当我们倾斜屏幕时,我们实际上以当前角度旋转了星星。

通过逆序,我们又以当前角度"反旋转"星星。也就是以当前角度的负值来旋转星星。

就是说,

如果我们将星星旋转了10度的话,又将其旋转-10度来使星星在那个轴上重新面对屏幕。

下面的第一行抵消了沿y轴的旋转。然后,我们还需要抵消掉沿x轴的屏幕倾斜。

要做到这一点,我们只需要将屏幕再旋转-tilt倾角。

在抵消掉x和y轴的旋转后,星星又完全面对着我们了。

}

glRotatef(-star[loop].angle, 0.0, 1.0, 0.0); // 取消当前星星的角度

glRotatef(-tilt, 1.0, 0.0, 0.0); // 取消屏幕倾斜

{如果 twinkle 为 TRUE,我们在屏幕上先画一次不旋转的星星:

将星星总数(num) 减去当前的星星数(loop)再减去1,

来提取每颗星星的不同颜色(这么做是因为循环范围从0到num-1)。

举例来说,结果为10的时候,我们就使用10号星星的颜色。

这样相邻星星的颜色总是不同的。这不是个好法子,但很有效。

最后一个值是alpha通道分量。这个值越小,这颗星星就越暗。

由于启用了twinkle,每颗星星最后会被绘制两遍。

程序运行起来会慢一些,这要看您的机器性能如何了。

但两遍绘制的星星颜色相互融合,会产生很棒的效果。

同时由于第一遍的星星没有旋转,启用twinkle后的星星看起来有一种动画效果。

(如果您这里看不懂得话,就自己去看程序的运行效果吧。)

值得注意的是给纹理上色是件很容易的事。

尽管纹理本身是黑白的,纹理将变成我们在绘制它之前选定的任意颜色。

此外,同样值得注意的是我们在这里使用的颜色值是byte型的,

而不是通常的浮点数。甚至alpha通道分量也是如此。}

If (twinkle) Then // 启用闪烁效果

Begin

// 使用byte型数值指定一个颜色

glColor4ub(star[(50 - loop) - 1].r, star[(50 - loop) - 1].g,

star[(50 - loop) - 1].b, 255);

glBegin(GL_QUADS); // 开始绘制纹理映射过的四边形

glTexCoord2f(0.0, 0.0);

glVertex3f(-1.0, -1.0, 0.0);

glTexCoord2f(1.0, 0.0);

glVertex3f(1.0, -1.0, 0.0);

glTexCoord2f(1.0, 1.0);

glVertex3f(1.0, 1.0, 0.0);

glTexCoord2f(0.0, 1.0);

glVertex3f(-1.0, 1.0, 0.0);

glEnd(); // 四边形绘制结束

End;

{

现在绘制第二遍的星星。

唯一和前面的代码不同的是这一遍的星星肯定会被绘制,并且这次的星星绕着z轴旋转。

}

glRotatef(spin, 0.0, 0.0, 1.0); // 绕z轴旋转星星

// 使用byte型数值指定一个颜色

glColor4ub(star[loop].r, star[loop].g, star[loop].b, 255);

glBegin(GL_QUADS); // 开始绘制纹理映射过的四边形

glTexCoord2f(0.0, 0.0);

glVertex3f(-1.0, -1.0, 0.0);

glTexCoord2f(1.0, 0.0);

glVertex3f(1.0, -1.0, 0.0);

glTexCoord2f(1.0, 1.0);

glVertex3f(1.0, 1.0, 0.0);

glTexCoord2f(0.0, 1.0);

glVertex3f(-1.0, 1.0, 0.0);

glEnd(); // 四边形绘制结束

{以下的代码代表星星的运动。

我们增加spin的值来旋转所有的星星(公转)。

然后,将每颗星星的自转角度增加loop/num。

这使离中心更远的星星转的更快。最后减少每颗星星离屏幕中心的距离。

这样看起来,星星们好像被不断地吸入屏幕的中心。}

spin := spin + 0.01; // 星星的公转

star[loop].angle := star[loop].angle + Trunc(loop) / 50; // 改变星星的自转角度

star[loop].dist := star[loop].dist - 0.01; // 改变星星离中心的距离

{接着几行检查星星是否已经碰到了屏幕中心。

当星星碰到屏幕中心时,我们为它赋一个新颜色,然后往外移5个单位,

这颗星星将踏上它回归屏幕中心的旅程。}

If (star[loop].dist < 0.0) Then // 星星到达中心了么

Begin

star[loop].dist := star[loop].dist + 5.0; // 往外移5个单位

star[loop].r := random(256); // 赋一个新红色分量

star[loop].g := random(256); // 赋一个新绿色分量

star[loop].b := random(256); // 赋一个新蓝色分量

End;

End;

End;

{

现在我们添加监视键盘的代码。

下移到WinMain()。找到SwapBuffers(hDC)一行。

我们就在这一行后面增加键盘监视代码。

代码将检查T键是否已按下。

如果T键按下过,并且又放开了,if块内的代码将被执行。

如果twinkle为FALSE,他将变为TRUE。

反之亦然。只要T键按下, tp就变为TRUE。

这样处理可以防止如果您一直按着T键的话,块内的代码被反复执行。

}

If (keys[ord('T')] And Not tp) Then // 是否T 键已按下并且 tp值为 FALSE

Begin

tp := TRUE; // 若是,将tp设为TRUE

twinkle := Not twinkle; // 翻转 twinkle的值

End;

{

下面的代码检查是否松开了T键。

若是,使 tp=FALSE。

除非tp的值为FALSE,

否则按着T键时什么也不会发生。所以这行代码很重要。

}

If (Not keys[Ord('T')]) Then // T 键已松开了么?

Begin

tp := FALSE; // 若是 ,tp为 FALSE

End;

{余下的代码检查上、下方向键,向上翻页键或向下翻页键是否按下。}

If (keys[VK_UP]) Then // 上方向键按下了么?

tilt := tilt - 0.5; // 屏幕向上倾斜

If (keys[VK_DOWN]) Then // 下方向键按下了么?

tilt := tilt + 0.5; // 屏幕向下倾斜

If (keys[VK_PRIOR]) Then // 向上翻页键按下了么

zoom := zoom - 0.2; // 缩小

If (keys[VK_NEXT]) Then // 向下翻页键按下了么?

zoom := zoom + 0.2; // 放大

{

这一课我尽我所能来解释如何加载一个灰阶位图纹理,

(使用混色)去掉它的背景色后,再给它上色,最后让它在3D场景中移动。

我已经向您展示了如何创建漂亮的颜色与动画效果。

实现原理是在原始位图上再重叠一份位图拷贝。

到现在为止,只要您很好的理解了我所教您的一切,

您应该已经能够毫无问题的制作您自己的3D Demo了。

所有的基础知识都已包括在内!}

//========myling :

//1-9课已经翻译完了,就象NEHE说的,基本的知识已经基本说完了

//我看了下后面的教程,好像是出自其他人之手,如果有好的例子,我会选择性的继

//续贴的,好累,睡一会:) ,下次见

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
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- 王朝網路 版權所有