以文本方式查看主题

-  计算机科学论坛  (http://bbs.xml.org.cn/index.asp)
--  『 C/C++编程思想 』  (http://bbs.xml.org.cn/list.asp?boardid=61)
----  [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 15-lesson 16  (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=53881)


--  作者:一分之千
--  发布时间:10/16/2007 12:15:00 PM

--  [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 15-lesson 16

第十五十六课源码


第十五课   

按此在新窗口浏览图片图形字体的纹理映射:

这一课,我们将在上一课的基础上创建带有纹理的字体,它真的很简单。

  
   
   
在发布了前两篇关于位图字体和轮廓字体的教程以后,我收到很多邮件,很多读者都想知道如何才能给字体赋予纹理贴图。你可以使用自动纹理坐标生成器。它会为字体上的每一个多边形生成纹理坐标。
一个小注释,这段代码是专门针对Windows写的,它使用了Windows的wgl函数来创建字体,显然,Apple机系统有agl,X系统有glx来支持做同样事情的,不幸的是,我不能保证这些代码也是容易使用的。如果哪位有能在屏幕上显示文字且独立于平台的代码,请告诉我,我将重写一个有关字体的教程。

我们将使用第14课的代码来创作纹理字体的演示。如果程序中哪部分的代码有变化,我会重写那部分的所有代码以便看出我做的改动。

  
   
   
我们还要添加一个叫做texture[]的整型变量。它用于保存纹理。后面3行是第14课中的代码,本课不做改动。
  
   
   
下面的部分做了一些小改动。我打算在这课使用wingdings字体来显示一个海盗旗(骷髅头和十字骨头)的标志。如果你想显示文字的话,就不用改动第14课中的代码了,也可以选择另一种字体。
有些人想知道如何使用wingdings字体,这也是我不用标准字体的一个原因。wingdings是一种符号字体,使用它时需要做一些改动。告诉Windows使用wingdings字体并不太简单。如果你把字体的名字改为wingdings,你会注意到字体其实并没有选到。你必须告诉Windows这种字体是一种符号字体而不是一种标准字符字体。后面会继续解释。

  
   

GLvoid BuildFont(GLvoid)      // 创建位图字体
{
 GLYPHMETRICSFLOAT gmf[256];     // 记录256个字符的信息
 HFONT font;      // 字体句柄

 base = glGenLists(256);     // 创建256个显示列表
 font = CreateFont( -12,     // 字体高度
    0,    // 字体宽度
    0,    // 字体的旋转角度 Angle Of Escapement
    0,    // 字体底线的旋转角度Orientation Angle
    FW_BOLD,    // 字体的重量
    FALSE,    // 是否使用斜体
    FALSE,    // 是否使用下划线
    FALSE,    // 是否使用删除线

   
这就是有魔力的那一行!不使用第14课中的ANSI_CHARSET,我们将使用SYMBOL_CHARSET。这会告诉Windows我们创建的字体并不是由标准字符组成的典型字体。所谓符号字体通常是由一些小图片(符号)组成的。如果你忘了改变这行,wingdings,webdings以及你想用的其它符号字体就不会工作。
  
   

    SYMBOL_CHARSET,   // 设置字符集

   
下面几行没有变化。
  
   

    OUT_TT_PRECIS,   // 输出精度
    CLIP_DEFAULT_PRECIS,  // 裁剪精度
    ANTIALIASED_QUALITY,  // 输出质量
    FF_DONTCARE|DEFAULT_PITCH,  // Family And Pitch

   
既然我们已经选择了符号字符集标识符,我们就可以选择wingdings字体了  
   

    "Wingdings");   // 字体名称

   
剩下几行代码没有变化  
   

 SelectObject(hDC, font);     // 选择创建的字体

 wglUseFontOutlines( hDC,    // 设置当前窗口设备描述表的句柄
    0,    // 用于创建显示列表字体的第一个字符的ASCII值
    255,    // 字符数
    base,    // 第一个显示列表的名称

   
我们允许有更多的误差,这意味着GL不会严格的遵守字体的轮廓线。如果你把误差设置为0.0f,你就会发现严格地在曲面上贴图存在一些问题。但是如果你允许一定的误差,很多问题都可以避免。
  
   

    0.1f,    // 字体的光滑度,越小越光滑,0.0为最光滑的状态

   
下面三行代码还是相同的  
   

    0.2f,    // 在z方向突出的距离
    WGL_FONT_POLYGONS,   // 使用多边形来生成字符,每个顶点具有独立的法线
    gmf);    // 一个接收字形度量数据的数组的地址,每个数组元素用它对应的显示列表字符的数据填充
}

   
在ReSizeGLScene()函数之前,我们要加上下面一段代码来读取纹理。你可能会认得这些前几课中的代码。我们创建一个保存位图的地方,读取位图,告诉Windows生成一个纹理,并把它保存在texture[0]中。
我们创建一种细化纹理(mipmapped texture),这样会看起来好些。纹理的名字叫做lights.bmp。
  
   

if (TextureImage[0]=LoadBMP("Data/Lights.bmp"))   // 载入位图

   
下面四行代码将为我们绘制在屏幕上的任何物体自动生成纹理坐标。函数glTexGen非常强大,而且复杂,如果要完全讲清楚它的数学原理需要再写一篇教程。不过,你只要知道GL_S和GL_T是纹理坐标就可以了。默认状态下,它被设置为提取物体此刻在屏幕上的x坐标和y坐标,并把它们转换为顶点坐标。你会发现到物体在z平面没有纹理,只显示一些斑纹。正面和反面都被赋予了纹理,这些都是由glTexGen函数产生的。(X(GL_S)用于从左到右映射纹理,Y(GL_T)用于从上到下映射纹理。
GL_TEXTURE_GEN_MODE允许我们选择我们想在S和T纹理坐标上使用的纹理映射模式。你有3种选择:

GL_EYE_LINEAR - 纹理会固定在屏幕上。它永远不会移动。物体将被赋予处于它通过的地区的那一块纹理。

GL_OBJECT_LINEAR - 这种就是我们使用的模式。纹理被固定于在屏幕上运动的物体上。
GL_SPHERE_MAP - 每个人都喜欢。创建一种有金属质感的物体。

  
   

  // 设置纹理映射模式
  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glEnable(GL_TEXTURE_GEN_S);    // 使用自动生成纹理
  glEnable(GL_TEXTURE_GEN_T);  
   
在InitGL()的最后有几行新代码。BuildFont()被放到了读取纹理的代码之后。glEnable(GL_COLOR_MATERIAL) 这行被删掉了,如果你想使用glColor3f(r,g,b)来改变纹理的颜色,那么就把glEnable(GL_COLOR_MATERIAL)这行重新加到这部分代码中。  
   

int InitGL(GLvoid)       // 此处开始对OpenGL进行所有设置
{
 if (!LoadGLTextures())     // 载入纹理
 {
  return FALSE;     // 失败则返回
 }
 BuildFont();      // 创建字体显示列表

 glShadeModel(GL_SMOOTH);     // 启用阴影平滑
 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // 黑色背景
 glClearDepth(1.0f);     // 设置深度缓存
 glEnable(GL_DEPTH_TEST);     // 启用深度测试
 glDepthFunc(GL_LEQUAL);     // 所作深度测试的类型
 glEnable(GL_LIGHT0);     // 使用第0号灯
 glEnable(GL_LIGHTING);     // 使用光照
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);  // 告诉系统对透视进行修正

   
启动2D纹理映射,并选择第一个纹理。这样就把第一个纹理映射到我们绘制在屏幕上的3D物体上了。如果你想加入更多的操作,可以按自己的意愿启动或禁用纹理映射。  
   

 glEnable(GL_TEXTURE_2D);     // 使用二维纹理
 glBindTexture(GL_TEXTURE_2D, texture[0]);   // 选择使用的纹理
 return TRUE;      // 初始化成功
}

   
重置大小的代码没有变化,但DrawGLScene这部分代码有变化。  
   
   
这里是第一处变动。我们打算使用COS和SIN让物体绕着屏幕旋转而不是把它固定在屏幕中间。我们将把物体向屏幕里移动3个单位。在x轴,我们将移动范围限制在-1.1到+1.1之间。我们使用rot变量来控制左右移动。我们把上下移动的范围限制在+0.8到-0.8之间。同样使用rot变量来控制上下移动(最好充分利用你的变量)。  
   

 // 设置字体的位置
 glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);

   
下面做常规的旋转。这会使符号在X,Y和Z轴旋转。  
   

 glRotatef(rot,1.0f,0.0f,0.0f);    // 沿X轴旋转
 glRotatef(rot*1.2f,0.0f,1.0f,0.0f);    // 沿Y轴旋转
 glRotatef(rot*1.4f,0.0f,0.0f,1.0f);    // 沿Z轴旋转

   
我们将物体相对观察点向左向下移动一点,以便于把符号定位于每个轴的中心。否则,当我们旋转它的时候,看起来就不像是在围绕它自己的中心在旋转。-0.35只是一个能让符号正确显示的数。我也试过一些其它数,因为我不知道这种字体的宽度是多少,可以适情况作出调整。我不知道为什么这种字体没有一个中心。  
   

 glTranslatef(-0.35f,-0.35f,0.1f);    // 移动到可以显示的位置

   
最后,我们绘制海盗旗的符号,然后增加rot变量,从而使这个符号在屏幕中旋转和移动。如果你不知道我是如何从字母‘N’中得到海盗旗符号的,那就打开Microsoft Word或是写字板。在字体下拉菜单中选择Wingdings字体。输入大写字母‘N’,就会显示出海盗旗符号了。  
   

 glPrint("N");      // 绘制海盗旗符号
 rot+=0.1f;      // 增加旋转变量

   
最后要做的事就是在KillGLWindow()的最后添加KillFont()函数,如下所示。添加这行代码很重要。它将在我们退出程序之前做清理工作。
  
   
   
尽管我没有讲的细致入微,但我想你应该很好的理解了如何让OpenGL为你生成纹理坐标。在给你的字体或者是同类物体赋予纹理映射时,应该没有问题了,而且只需要改变两行代码,你就可以启用球体映射了,它的效果简直酷毙了!



--  作者:一分之千
--  发布时间:10/16/2007 12:15:00 PM

--  
Lesson 15
   
After posting the last two tutorials on bitmap and outlined fonts, I received quite a few emails from people wondering how they could texture map the fonts. You can use autotexture coordinate generation. This will generate texture coordinates for each of the polygons on the font.

A small note, this code is Windows specific. It uses the wgl functions of Windows to build the font. Apparently Apple has agl support that should do the same thing, and X has glx. Unfortunately I can't guarantee this code is portable. If anyone has platform independant code to draw fonts to the screen, send it my way and I'll write another font tutorial.

We'll build our Texture Font demo using the code from lesson 14. If any of the code has changed in a particular section of the program, I'll rewrite the entire section of code so that it's easier to see the changes that I have made.

The following section of code is similar to the code in lesson 14, but this time we're not going to include the stdarg.h file.   
   

#include <windows.h>    // Header File For Windows
#include <math.h>    // Header File For Windows Math Library
#include <stdio.h>    // Header File For Standard Input/Output
#include <gl\gl.h>    // Header File For The OpenGL32 Library
#include <gl\glu.h>    // Header File For The GLu32 Library
#include <gl\glaux.h>    // Header File For The GLaux Library

HDC  hDC=NULL;    // Private GDI Device Context
HGLRC  hRC=NULL;    // Permanent Rendering Context
HWND  hWnd=NULL;    // Holds Our Window Handle
HINSTANCE hInstance;    // Holds The Instance Of The Application

bool  keys[256];    // Array Used For The Keyboard Routine
bool  active=TRUE;    // Window Active Flag Set To TRUE By Default
bool  fullscreen=TRUE;   // Fullscreen Flag Set To Fullscreen Mode By Default

   
We're going to add one new integer variable here called texture[ ]. It will be used to store our texture. The last three lines were in tutorial 14 and have not changed in this tutorial.   
   

GLuint texture[1];     // One Texture Map ( NEW )
GLuint base;      // Base Display List For The Font Set

GLfloat rot;      // Used To Rotate The Text

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc

   
The following section of code has some minor changes. In this tutorial I'm going to use the wingdings font to display a skull and crossbones type object. If you want to display text instead, you can leave the code the same as it was in lesson 14, or change to a font of your own.

A few of you were wondering how to use the wingdings font, which is another reason I'm not using a standard font. Wingdings is a SYMBOL font, and requires a few changes to make it work. It's not as easy as telling Windows to use the wingdings font. If you change the font name to wingdings, you'll notice that the font doesn't get selected. You have to tell Windows that the font is a symbol font and not a standard character font. More on this later.   
   

GLvoid BuildFont(GLvoid)     // Build Our Bitmap Font
{
 GLYPHMETRICSFLOAT gmf[256];   // Address Buffer For Font Storage
 HFONT font;      // Windows Font ID

 base = glGenLists(256);     // Storage For 256 Characters
 font = CreateFont( -12,    // Height Of Font
    0,    // Width Of Font
    0,    // Angle Of Escapement
    0,    // Orientation Angle
    FW_BOLD,   // Font Weight
    FALSE,    // Italic
    FALSE,    // Underline
    FALSE,    // Strikeout

   
This is the magic line! Instead of using ANSI_CHARSET like we did in tutorial 14, we're going to use SYMBOL_CHARSET. This tells Windows that the font we are building is not your typical font made up of characters. A symbol font is usually made up of tiny pictures (symbols). If you forget to change this line, wingdings, webdings and any other symbol font you may be trying to use will not work.   
   

    SYMBOL_CHARSET,   // Character Set Identifier ( Modified )

   
The next few lines have not changed.   
   

    OUT_TT_PRECIS,   // Output Precision
    CLIP_DEFAULT_PRECIS,  // Clipping Precision
    ANTIALIASED_QUALITY,  // Output Quality
    FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch

   
Now that we've selected the symbol character set identifier, we can select the wingdings font!   
   

    "Wingdings");   // Font Name ( Modified )

   
The remaining lines of code have not changed.   
   

 SelectObject(hDC, font);    // Selects The Font We Created

 wglUseFontOutlines( hDC,    // Select The Current DC
    0,    // Starting Character
    255,    // Number Of Display Lists To Build
    base,    // Starting Display Lists

   
I'm allowing for more deviation. This means GL will not try to follow the outline of the font as closely. If you set deviation to 0.0f, you'll notice problems with the texturing on really curved surfaces. If you allow for some deviation, most of the problems will disappear.   
   

    0.1f,    // Deviation From The True Outlines

   
The next three lines of code are still the same.   
   

    0.2f,    // Font Thickness In The Z Direction
    WGL_FONT_POLYGONS,  // Use Polygons, Not Lines
    gmf);    // Address Of Buffer To Recieve Data
}

   
Right before ReSizeGLScene() we're going to add the following section of code to load our texture. You might recognize the code from previous tutorials. We create storage for the bitmap image. We load the bitmap image. We tell OpenGL to generate 1 texture, and we store this texture in texture[0].

I'm creating a mipmapped texture only because it looks better. The name of the texture is lights.bmp.   
   

AUX_RGBImageRec *LoadBMP(char *Filename)   // Loads A Bitmap Image
{
 FILE *File=NULL;     // File Handle

 if (!Filename)      // Make Sure A Filename Was Given
 {
  return NULL;     // If Not Return NULL
 }

 File=fopen(Filename,"r");    // Check To See If The File Exists

 if (File)      // Does The File Exist?
 {
  fclose(File);     // Close The Handle
  return auxDIBImageLoad(Filename);  // Load The Bitmap And Return A Pointer
 }

 return NULL;      // If Load Failed Return NULL
}

int LoadGLTextures()      // Load Bitmaps And Convert To Textures
{
 int Status=FALSE;     // Status Indicator

 AUX_RGBImageRec *TextureImage[1];   // Create Storage Space For The Texture

 memset(TextureImage,0,sizeof(void *)*1);  // Set The Pointer To NULL

 if (TextureImage[0]=LoadBMP("Data/Lights.bmp"))  // Load The Bitmap
 {
  Status=TRUE;     // Set The Status To TRUE

  glGenTextures(1, &texture[0]);   // Create The Texture

  // Build Linear Mipmapped Texture
  glBindTexture(GL_TEXTURE_2D, texture[0]);
  gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

   
The next four lines of code will automatically generate texture coordinates for any object we draw to the screen. The glTexGen command is extremely powerful, and complex, and to get into all the math involved would be a tutorial on it's own. All you need to know is that GL_S and GL_T are texture coordinates. By default they are set up to take the current x location on the screen and the current y location on the screen and come up with a texture vertex. You'll notice the objects are not textured on the z plane... just stripes appear. The front and back faces are textured though, and that's all that matters. X (GL_S) will cover mapping the texture left to right, and Y (GL_T) will cover mapping the texture up and down.

GL_TEXTURE_GEN_MODE lets us select the mode of texture mapping we want to use on the S and T texture coordinates. You have 3 choices:

GL_EYE_LINEAR - The texture is fixed to the screen. It never moves. The object is mapped with whatever section of the texture it is passing over.

GL_OBJECT_LINEAR - This is the mode we are using. The texture is fixed to the object moving around the screen.

GL_SPHERE_MAP - Everyones favorite. Creates a metalic reflective type object.

It's important to note that I'm leaving out alot of code. We should be setting the GL_OBJECT_PLANE as well, but by default it's set to the parameters we want. Buy a good book if you're interested in learning more, or check out the MSDN help CD / DVD.   
   

  // Texturing Contour Anchored To The Object
  glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  // Texturing Contour Anchored To The Object
  glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);
  glEnable(GL_TEXTURE_GEN_S);   // Auto Texture Generation
  glEnable(GL_TEXTURE_GEN_T);   // Auto Texture Generation
 }

 if (TextureImage[0])     // If Texture Exists
 {
  if (TextureImage[0]->data)   // If Texture Image Exists
  {
   free(TextureImage[0]->data);  // Free The Texture Image Memory
  }

  free(TextureImage[0]);    // Free The Image Structure
 }

 return Status;      // Return The Status
}

   
There are a few new lines at the end of the InitGL() code. BuildFont() has been moved underneath our texture loading code. The line glEnable(GL_COLOR_MATERIAL) has been removed. If you plan to apply colors to the texture using glColor3f(r,g,b) add the line glEnable(GL_COLOR_MATERIAL) back into this section of code.   
   

int InitGL(GLvoid)      // All Setup For OpenGL Goes Here
{
 if (!LoadGLTextures())     // Jump To Texture Loading Routine
 {
  return FALSE;     // If Texture Didn't Load Return FALSE
 }
 BuildFont();      // Build The Font

 glShadeModel(GL_SMOOTH);    // Enable Smooth Shading
 glClearColor(0.0f, 0.0f, 0.0f, 0.5f);   // Black Background
 glClearDepth(1.0f);     // Depth Buffer Setup
 glEnable(GL_DEPTH_TEST);    // Enables Depth Testing
 glDepthFunc(GL_LEQUAL);     // The Type Of Depth Testing To Do
 glEnable(GL_LIGHT0);     // Quick And Dirty Lighting (Assumes Light0 Is Set Up)
 glEnable(GL_LIGHTING);     // Enable Lighting
 glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // Really Nice Perspective Calculations

   
Enable 2D Texture Mapping, and select texture one. This will map texture one onto any 3D object we draw to the screen. If you want more control, you can enable and disable texture mapping yourself.   
   

 glEnable(GL_TEXTURE_2D);    // Enable Texture Mapping
 glBindTexture(GL_TEXTURE_2D, texture[0]);  // Select The Texture
 return TRUE;      // Initialization Went OK
}

   
The resize code hasn't changed, but our DrawGLScene code has.   
   

int DrawGLScene(GLvoid)      // Here's Where We Do All The Drawing
{
 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
 glLoadIdentity();     // Reset The View

   
Here's our first change. Instead of keeping the object in the middle of the screen, we're going to spin it around the screen using COS and SIN (no surprise). We'll translate 3 units into the screen (-3.0f). On the x axis, we'll swing from -1.1 at far left to +1.1 at the right. We'll be using the rot variable to control the left right swing. We'll swing from +0.8 at top to -0.8 at the bottom. We'll use the rot variable for this swinging motion as well. (might as well make good use of your variables).   
   

 // Position The Text
 glTranslatef(1.1f*float(cos(rot/16.0f)),0.8f*float(sin(rot/20.0f)),-3.0f);

   
Now we do the normal rotations. This will cause the symbol to spin on the X, Y and Z axis.   
   

 glRotatef(rot,1.0f,0.0f,0.0f);    // Rotate On The X Axis
 glRotatef(rot*1.2f,0.0f,1.0f,0.0f);   // Rotate On The Y Axis
 glRotatef(rot*1.4f,0.0f,0.0f,1.0f);   // Rotate On The Z Axis

   
We translate a little to the left, down, and towards the viewer to center the symbol on each axis. Otherwise when it spins it doesn't look like it's spinning around it's own center. -0.35 is just a number that worked. I had to play around with numbers for a bit because I'm not sure how wide the font is, could vary with each font. Why the fonts aren't built around a central point I'm not sure.   
   

 glTranslatef(-0.35f,-0.35f,0.1f);   // Center On X, Y, Z Axis

   
Finally we draw our skull and crossbones symbol then increase the rot variable so our symbol spins and moves around the screen. If you can't figure out how I get a skull and crossbones from the letter 'N', do this: Run Microsoft Word or Wordpad. Go to the fonts drop down menu. Select the Wingdings font. Type and uppercase 'N'. A skull and crossbones appears.   
   

 glPrint("N");      // Draw A Skull And Crossbones Symbol
 rot+=0.1f;      // Increase The Rotation Variable
 return TRUE;      // Keep Going
}

   
The last thing to do is add KillFont() to the end of KillGLWindow() just like I'm showing below. It's important to add this line. It cleans things up before we exit our program.   
   

 if (!UnregisterClass("OpenGL",hInstance))  // Are We Able To Unregister Class
 {
  MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);
  hInstance=NULL;     // Set hInstance To NULL
 }

 KillFont();      // Destroy The Font
}

   
Even though I never went into extreme detail, you should have a pretty good understanding on how to make OpenGL generate texture coordinates for you. You should have no problems mapping textures to fonts of your own, or even other objects for that matter. And by changing just two lines of code, you can enable sphere mapping, which is a really cool effect.

Jeff Molofee (NeHe)


--  作者:一分之千
--  发布时间:10/16/2007 12:16:00 PM

--  

第十六课


按此在新窗口浏览图片雾:

这一课是基于第7课的代码的,你将学会三种不同的雾的计算方法,以及怎样设置雾的颜色和雾的范围。

  
   
   
这篇教程由Chris Aliotta编写。
你想给你的OpenGL程序添加雾效?我将在这篇教程中教你如何去做。这是我第一次写教程,我也只是OpenGL/C++编程的新手,所以如果你发现了什么错误请告诉我而不是叱责我。本课的代码基于第七课的代码编写。

Data Setup:

我们将从设置保存雾的信息的变量开始。变量fogMode用来保存三种类型的雾:GL_EXP,GL_EXP2和GL_LINEAR。我将在稍后解释这三种类型的区别。这些变量位于程序开头GLuint texture[3]这行后。变量fogfilter将用来表示我们使用的是哪种雾类型。变量fogColor保存雾的颜色。在程序的顶部我还加了一个布尔类型的变量gp用来记录'g'键是否被按下。

  
   

bool   gp;      // G健是否按下
GLuint filter;      // 使用哪一个纹理过滤器
GLuint fogMode[]= { GL_EXP, GL_EXP2, GL_LINEAR };  // 雾气的模式
GLuint fogfilter= 0;     // 使用哪一种雾气
GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 1.0f};  // 雾的颜色设为白色

   
现在我们已经设置了变量,接下来我们来看InitGL函数。为了获得更好的效果,glClearColor()这行已经被修改为将屏幕清为同雾相同的颜色。使用雾效只需很少的代码。总之你将发现这很容易。  
   

glClearColor(0.5f,0.5f,0.5f,1.0f);   // 设置背景的颜色为雾气的颜色

glFogi(GL_FOG_MODE, fogMode[fogfilter]);  // 设置雾气的模式
glFogfv(GL_FOG_COLOR, fogColor);   // 设置雾的颜色
glFogf(GL_FOG_DENSITY, 0.35f);   // 设置雾的密度
glHint(GL_FOG_HINT, GL_DONT_CARE);   // 设置系统如何计算雾气
glFogf(GL_FOG_START, 1.0f);    // 雾气的开始位置
glFogf(GL_FOG_END, 5.0f);    // 雾气的结束位置
glEnable(GL_FOG);     // 使用雾气
   
让我们先看看这段代码的头三行。第一行glEnable(GL_FOG);(?这应该是最后一行)不用再解释了吧,主要是初始化雾效。

第二行glFogi(GL_FOG_MODE, fogMode[fogfilter]);建立雾的过滤模式。之前我们声明了数组fogMode,它保存了值GL_EXP, GL_EXP2, and GL_LINEAR。现在是使用他们的时候了。我来解释它们(具体见红皮书,其实这是计算雾效混合因子的三种方式):
GL_EXP - 充满整个屏幕的基本渲染的雾。它能在较老的PC上工作,因此并不是特别像雾。
GL_EXP2 - 比GL_EXP更进一步。它也是充满整个屏幕,但它使屏幕看起来更有深度。
GL_LINEAR - 最好的渲染模式。物体淡入淡出的效果更自然。

第三行glFogfv(GL_FOG_COLOR, fogcolor);设置雾的颜色。之前我们已将变量fogcolor设为(0.5f,0.5f,0.5f,1.0f),这是一个很棒的灰色。

接下来我们看看最后的四行。glFogf(GL_FOG_DENSITY, 0.35f);这行设置雾的密度。增加数字会让雾更密,减少它则雾更稀。

glHint (GL_FOG_HINT, GL_DONT_CARE); 设置修正。我使用了GL_DONT_CARE因为我不关心它的值。

Eric Desrosiers Adds:关于glHint(GL_FOG_HINT, hintval);的解释

gl_dont_care - 由OpenGL决定使用何种雾效(对每个顶点还是每个像素执行雾效计算)和一个未知的公式(?)
gl_nicest - 对每个像素执行雾效计算(效果好)
gl_fastest - 对每个顶点执行雾效计算 (更快,但效果不如上面的好)

下一行glFogf(GL_FOG_START, 1.0f);设定雾效距屏幕多近开始。你可以根据你的需要随意改变这个值。下一行类似,glFogf(GL_FOG_END, 5.0f);告诉OpenGL程序雾效持续到距屏幕多远。

现在我们建立了绘制雾的代码,我们将加入键盘指令在不同的雾效模式间循环。这段代码及其它的按键处理代码在程序的最后。

  
   

if (keys['G'] && !gp)     // G键是否 按下
{
 gp=TRUE;      // 是
 fogfilter+=1;     // 变换雾气模式
 if (fogfilter>2)     // 模式是否大于2
 {
  fogfilter=0;    // 置零
 }
 glFogi (GL_FOG_MODE, fogMode[fogfilter]);  // 设置雾气模式
}
if (!keys['G'])      // G键是否释放
{
 gp=FALSE;      // 是,设置为释放
}

   
就是这样!大功告成!现在你的OpenGl程序有了雾效。



--  作者:一分之千
--  发布时间:10/16/2007 12:17:00 PM

--  
Lesson 16
   
This tutorial brought to you by Chris Aliotta...

So you want to add fog to your OpenGL program? Well in this tutorial I will show you how to do exactly that. This is my first time writing a tutorial, and I am still relatively new to OpenGL/C++ programming, so please, if you find anything that's wrong let me know and don't jump all over me. This code is based on the code from lesson 7.

Data Setup:

We'll start by setting up all our variables needed to hold the information for fog. The variable fogMode will be used to hold three types of fog: GL_EXP, GL_EXP2, and GL_LINEAR. I will explain the differences between these three later on. The variables will start at the beginning of the code after the line GLuint texture[3]. The variable fogfilter will be used to keep track of which fog type we will be using. The variable fogColor will hold the color we wish the fog to be. I have also added the boolean variable gp at the top of the code so we can tell if the 'g' key is being pressed later on in this tutorial.   
   

bool   gp;      // G Pressed? ( New )
GLuint filter;      // Which Filter To Use
GLuint fogMode[]= { GL_EXP, GL_EXP2, GL_LINEAR }; // Storage For Three Types Of Fog
GLuint fogfilter= 0;     // Which Fog To Use
GLfloat fogColor[4]= {0.5f, 0.5f, 0.5f, 1.0f};  // Fog Color

   
DrawGLScene Setup

Now that we have established our variables we will move down to InitGL. The glClearColor() line has been modified to clear the screen to the same same color as the fog for a better effect. There isn't much code involved to make fog work. In all you will find this to be very simplistic.   
   

glClearColor(0.5f,0.5f,0.5f,1.0f);   // We'll Clear To The Color Of The Fog ( Modified )

glFogi(GL_FOG_MODE, fogMode[fogfilter]);  // Fog Mode
glFogfv(GL_FOG_COLOR, fogColor);   // Set Fog Color
glFogf(GL_FOG_DENSITY, 0.35f);    // How Dense Will The Fog Be
glHint(GL_FOG_HINT, GL_DONT_CARE);   // Fog Hint Value
glFogf(GL_FOG_START, 1.0f);    // Fog Start Depth
glFogf(GL_FOG_END, 5.0f);    // Fog End Depth
glEnable(GL_FOG);     // Enables GL_FOG

   
Lets pick apart the first three lines of this code. The first line glEnable(GL_FOG); is pretty much self explanatory. It basically initializes the fog.

The second line, glFogi(GL_FOG_MODE, fogMode[fogfilter]); establishes the fog filter mode. Now earlier we declared the array fogMode. It held GL_EXP, GL_EXP2, and GL_LINEAR. Here is when these variables come into play. Let me explain each one:
GL_EXP - Basic rendered fog which fogs out all of the screen. It doesn't give much of a fog effect, but gets the job done on older PC's.
GL_EXP2 - Is the next step up from GL_EXP. This will fog out all of the screen, however it will give more depth to the scene.
GL_LINEAR - This is the best fog rendering mode. Objects fade in and out of the fog much better.
The third line, glFogfv(GL_FOG_COLOR, fogcolor); sets the color of the fog. Earlier we had set this to (0.5f,0.5f,0.5f,1.0f) using the variable fogcolor, giving us a nice grey color.

Next lets look at the last four lines of this code. The line glFogf(GL_FOG_DENSITY, 0.35f); establishes how dense the fog will be. Increase the number and the fog becomes more dense, decrease it and it becomes less dense.

The line glHint (GL_FOG_HINT, GL_DONT_CARE); establishes the hint. I used GL_DONT_CARE, because I didn't care about the hint value.

Eric Desrosiers Adds: Little explanation of glHint(GL_FOG_HINT, hintval);

hintval can be : GL_DONT_CARE, GL_NICEST or GL_FASTEST

gl_dont_care - Lets opengl choose the kind of fog (per vertex of per pixel) and an unknown formula.
gl_nicest - Makes the fog per pixel (look good)
glfastest - Makes the fog per vertex (faster, but less nice)

The next line glFogf(GL_FOG_START, 1.0f); will establish how close to the screen the fog should start. You can change the number to whatever you want depending on where you want the fog to start. The next line is similar, glFogf(GL_FOG_END, 5.0f);. This tells the OpenGL program how far into the screen the fog should go.

Keypress Events

Now that we've setup the fog drawing code we will add the keyboard commands to cycle through the different fog modes. This code goes down at the end of the program with all the other key handling code.
  
   

if (keys['G'] && !gp)     // Is The G Key Being Pressed?
{
 gp=TRUE;     // gp Is Set To TRUE
 fogfilter+=1;     // Increase fogfilter By One
 if (fogfilter>2)    // Is fogfilter Greater Than 2?
 {
  fogfilter=0;    // If So, Set fogfilter To Zero
 }
 glFogi (GL_FOG_MODE, fogMode[fogfilter]); // Fog Mode
}
if (!keys['G'])      // Has The G Key Been Released?
{
 gp=FALSE;     // If So, gp Is Set To FALSE
}

   
That's it! We are done! You now have fog in your OpenGL program. I'd have to say that was pretty painless. If you have any questions or comments feel free to contact me at chris@incinerated.com. Also please stop by my website: http://www.incinerated.com/ and http://www.incinerated.com/precursor.

Christopher Aliotta (Precursor)



--  作者:hwfchina
--  发布时间:3/18/2008 10:23:00 PM

--  
顶~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  作者:ririyeye
--  发布时间:1/8/2010 10:36:00 AM

--  
初次学习必须看
W 3 C h i n a ( since 2003 ) 旗 下 站 点
苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
109.375ms