新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 43-lesson 44 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 18155 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 43-lesson 44 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客楼主
    发贴心情 [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 43-lesson 44


    第四十三四十四课   

    第四十三课

    按此在新窗口浏览图片在OpenGL中使用FreeType库

    使用FreeType库可以创建非常好看的反走样的字体,记住暴雪公司就是使用这个库的,就是那个做魔兽世界的。尝试一下吧,我只告诉你了基本的使用方式,你可以走的更远。

      
       
       
    在OpenGL中使用FreeType库

    这里是一个快速的介绍,它告诉你如何在OpenGL中使用FreeType渲染TrueType字体。使用这个库我们可以渲染反走样的文本,它看起来更加的漂亮。

    动机

    这里我将给你两个例子,一个是用WGL的bitmap字体渲染得文字,另一个是用FreeType渲染得文字。

    按此在新窗口浏览图片

    使用WGl渲染得文字是一些图像,当你放大它们时看起来如下:

    按此在新窗口浏览图片

    如果你使用GNU的FreeType库(暴雪公司也在它们的游戏中使用这个库),它将看起来更漂亮,如下所示,它具有了反走样:

    按此在新窗口浏览图片

    创建程序

    第一步你需要从下面的网站上下载FreeType库:http://gnuwin32.sourceforge.net/packages/freetype.htm

    接着在你使用它创建一个新的程序时,你需要链接libfreetype.lib库,并包含FreeType的头文件。

     
      
       
       
    现在我们已经能创建基于FreeType的程序了,但我们还不能运行它,因为我们需要freetype-6.dll文件。
    好了,现在我们可以开始编写我们的程序了,我们从13课的代码开始,我们添加两个新的文件"freetype.cpp"和"freetype.h"。我们把和FreeType相关的内容放在这两个文件里。

    好了,让我们从freetype.h开始吧。

    按惯例我们包含一些需要的头文件
      
       

    #ifndef FREE_NEHE_H#define FREE_NEHE_H
    //FreeType 头文件
    #include <ft2build.h>
    #include <freetype/freetype.h>
    #include <freetype/ftglyph.h>
    #include <freetype/ftoutln.h>
    #include <freetype/fttrigon.h>

    //OpenGL 头文件
    #include <windows.h>
    #include <GL/gl.h>
    #include <GL/glu.h>

    //STL 头文件
    #include <vector>
    #include <string>

    //STL异常类
    #include <stdexcept>
    #pragma warning(disable: 4786)


       
    我们将把每个字符需要的信息封装在一个结构中,这样就像使用WGL字体一样,我们可以分别控制每个字符的显示状态。  
       

    // 把所有的操作放在名字空间freetype中,这样可以避免与其他函数的冲突namespace freetype
    {
    // 使用vector和string名字空间
    using std::vector;
    using std::string;

    // 这个结构保存字体信息
    struct font_data
    {
    float h; // 字体的高度
    GLuint * textures; // 使用的纹理
    GLuint list_base; // 显示列表的值

    // 初始化结构
    void init(const char * fname, unsigned int h);

    // 清楚所有的资源
    void clean();
    };


       
    最后一件事是定义我们输出字符串的原形:  
       

    // 把字符输出到屏幕void print(const font_data &ft_font, float x, float y, const char *fmt, ...);
    }

    #endif


       
    上面就是FreeType的头文件,下面我们看看怎样实现它  
       

    #include "freetype.h"
    namespace freetype {


       
    我们使用纹理去显示字符,在OpenGL中纹理大小必须为2的次方,这个函数用来字符的大小近似到这个值。所以我们有了如下的方程:  
       

    // 这个函数返回比a大的,并且是最接近a的2的次方的数inline int next_p2 (int a ){ int rval=1; // rval<<=1 Is A Prettier Way Of Writing rval*=2;  while(rval<a) rval<<=1; return rval;}

       
    下面一个函数为make_dlist, 它是这个代码的核心。它包含FT_Face对象,它是FreeType用来保存字体信息的类,接着创建一个显示列表。  
       

    // 为给定的字符创建一个显示列表void make_dlist ( FT_Face face, char ch, GLuint list_base, GLuint * tex_base ) {
    // 载入给定字符的轮廓
    if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT ))
    throw std::runtime_error("FT_Load_Glyph failed");

    // 保存轮廓对象
    FT_Glyph glyph;
    if(FT_Get_Glyph( face->glyph, &glyph ))
    throw std::runtime_error("FT_Get_Glyph failed");

    // 把轮廓转化为位图
    FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
    FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

    // 保存位图
    FT_Bitmap& bitmap=bitmap_glyph->bitmap;

    }

       
    现在我们已经从FreeType中获得了位图,我们需要把它转化为一个满足OpenGL纹理要求的位图。你必须知道,在OpenGL中位图表示黑白的数据,而在FreeType中我们使用8位的颜色表示位图,所以FreeType的位图可以保存亮度信息。  
       

    // 转化为OpenGl可以使用的大小 int width = next_p2( bitmap.width ); int height = next_p2( bitmap.rows );
    // 保存位图数据
    GLubyte* expanded_data = new GLubyte[ 2 * width * height];

    // 这里我们使用8位表示亮度8位表示alpha值
    for(int j=0; j <height;j++) {
    for(int i=0; i < width; i++){
    expanded_data[2*(i+j*width)]= expanded_data[2*(i+j*width)+1] =
    (i>=bitmap.width || j>=bitmap.rows) ?
    0 : bitmap.buffer[i + bitmap.width*j];
    }
    }

       
    接下来我们选则字体纹理,并生成字体的贴图纹理  
       

    // 设置字体纹理的纹理过滤器 glBindTexture( GL_TEXTURE_2D, tex_base[ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    // 邦定纹理
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
    GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

    // 释放分配的内存
    delete [] expanded_data;


       
    接着创建一个显示列表,它用来绘制一个字符  
       

    // 创建显示列表 glNewList(list_base+ch,GL_COMPILE);
    glBindTexture(GL_TEXTURE_2D,tex_base[ch]);

    //首先我们向左移动一点
    glTranslatef(bitmap_glyph->left,0,0);

    //接着我们向下移动一点,这只队'g','y'之类的字符有用
    //它使得所有的字符都有一个基线
    glPushMatrix();
    glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

    // 计算位图中字符图像的宽度
    float x=(float)bitmap.width / (float)width,
    y=(float)bitmap.rows / (float)height;

    //绘制一个正方形,显示字符
    glBegin(GL_QUADS);
    glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);
    glTexCoord2d(0,y); glVertex2f(0,0);
    glTexCoord2d(x,y); glVertex2f(bitmap.width,0);
    glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);
    glEnd();
    glPopMatrix();
    glTranslatef(face->glyph->advance.x >> 6 ,0,0);

    //结束显示列表的绘制
    glEndList();
    }


       
    下面的函数将使用make_dlist创建一个字符集的显示列表,fname为你要使用的FreeType字符文件。  
       

    void font_data::init(const char * fname, unsigned int h) {  // 保存纹理ID. textures = new GLuint[128];
    this->h=h;

    // 创建FreeType库
    FT_Library library;
    if (FT_Init_FreeType( &library ))
    throw std::runtime_error("FT_Init_FreeType failed");

    // 在FreeType库中保存字体信息的类叫做face
    FT_Face face;

    // 使用你输入的Freetype字符文件初始化face类
    if (FT_New_Face( library, fname, 0, &face ))
    throw std::runtime_error("FT_New_Face failed (there is probably a problem with your font file)");

    // 在FreeType中使用1/64作为一个像素的高度所以我们需要缩放h来满足这个要求
    FT_Set_Char_Size( face, h << 6, h << 6, 96, 96);

    // 创建128个显示列表
    list_base=glGenLists(128);
    glGenTextures( 128, textures );
    make_dlist(face,i,list_base,textures);

    // 释放face类
    FT_Done_Face(face);

    // 释放FreeType库
    FT_Done_FreeType(library);
    }


       
    下面的函数完成释放资源的工作  
       

    void font_data::clean() { glDeleteLists(list_base,128); glDeleteTextures(128,textures); delete [] textures;}
       
    在print函数中要用到下面的两个方程,pushScreenCoordinateMatrix函数用来保存当前的矩阵,并设置视口与当前的窗口大小匹配。pop_projection_matrix函数用来返回pushScreenCoordinateMatrix保存的矩阵。reference manual.   
       

    // 保存当前的矩阵,并设置视口与当前的窗口大小匹配inline void pushScreenCoordinateMatrix() { glPushAttrib(GL_TRANSFORM_BIT); GLint   viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]); glPopAttrib();}
    //返回pushScreenCoordinateMatrix保存的矩阵
    inline void pop_projection_matrix() {
    glPushAttrib(GL_TRANSFORM_BIT);
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glPopAttrib();


       
    我们的print函数和13课的函数非常的像,但在实现上有一些不同。我们实际上是使用2通道的纹理而不是图像。  
       

    // 输出文字void print(const font_data &ft_font, float x, float y, const char *fmt, ...)  {          // 保存当前矩阵 pushScreenCoordinateMatrix();                                            GLuint font=ft_font.list_base; float h=ft_font.h/.63f;                                                  char text[256];          va_list ap;          
    if (fmt == NULL)
    *text=0;
    else {
    va_start(ap, fmt);
    vsprintf(text, fmt, ap);
    va_end(ap);
    }

    // 把输入的字符串按回车分割
    const char *start_line=text;
    vector<string> lines;
    for(const char *c=text;*c;c++) {
    if(*c=='\n') {
    string line;
    for(const char *n=start_line;n<c;n++) line.append(1,*n);
    lines.push_back(line);
    start_line=c+1;
    }
    }
    if(start_line) {
    string line;
    for(const char *n=start_line;n<c;n++) line.append(1,*n);
    lines.push_back(line);
    }

    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glListBase(font);

     float modelview_matrix[16];      glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);
    // 下面的代码完成具体的绘制过程
    for(int i=0;i<lines.size();i++) {
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(x,y-h*i,0);
    glMultMatrixf(modelview_matrix);

    //调用显示列表绘制
    glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());

    glPopMatrix();
    }

    glPopAttrib();

    pop_projection_matrix();
    }

    }


    }

       
    FreeType库我们就写好了,现我们在13课的代码上来做一些修改,当然首先我们需要包含freetype.h的头文件  
       

    #include "freetype.h"
       
    现在我们就可以调用freetype库绘制字符串了  
       

    // 保存我们创建的字体的信息freetype::font_data our_font;
       
    接下来使用test.ttf文件初始化字体  
       

    our_font.init("Test.ttf", 16);
       
    在程序结束时记得释放内存资源  
       

    our_font.clean();
       
    下面是我们具体的绘制函数  
       

    int DrawGLScene(GLvoid)          { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      glLoadIdentity();          glTranslatef(0.0f,0.0f,-1.0f);       
    // 蓝色文字
    glColor3ub(0,0,0xff);

    // 绘制WGL文字
    glRasterPos2f(-0.40f, 0.35f);
    glPrint("Active WGL Bitmap Text With NeHe - %7.2f", cnt1);

    // 红色文字
    glColor3ub(0xff,0,0);

    glPushMatrix();
    glLoadIdentity();
    glRotatef(cnt1,0,0,1);
    glScalef(1,.8+.3*cos(cnt1/5),1);
    glTranslatef(-180,0,0);
    //绘制freetype文字
    freetype::print(our_font, 320, 200, "Active FreeType Text - %7.2f", cnt1);
    glPopMatrix();

    cnt1+=0.051f;
    cnt2+=0.005f;
    return TRUE; // 成功返回
    }

       
    最后我们介绍一些实用的创建字体的相关站点

    OGLFT 非常漂亮的基于FreeType2的字体库,下面是它的站点http://oglft.sourceforge.net.

    FTGL 是为OS X设计的第三方字体库. http://homepages.paradise.net.nz/henryj/code/#FTGL.

    FNT 一个非FreeType库,它使用自己定义的字体格式,但它具有非常好的界面http://plib.sourceforge.net/fnt.


       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 21:01:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客2
    发贴心情 
    Lesson: 43
       
    Tutorial on using FreeType Fonts in OpenGL

    So here's a quick tutorial to show you how to use the FreeType font rendering library in OpenGL. By using the FreeType library we can create anti-aliased text that looks better than text made using bitmap fonts (as in Lesson 13). Our text will also have some other advantages over bitmap fonts - it will be easy to rotate and works well with OpenGL's picking functions.


    Motivation

    Here I've printed out the same text using both WGL bitmap fonts and fonts rendered with FreeType as per this tutorial (both are Arial Black Italic).


    按此在新窗口浏览图片

    The basic problem with using bitmaps fonts is that in OpenGL bitmaps are by definition binary images. This means that an OpenGL bitmap stores only one bit of information per-pixel. If you zoom in on the text created using WGL bitmaps, the result looks like this:


    按此在新窗口浏览图片

    Because bitmaps are binary images, there are no gray pixels in the image above, and it means that the text looks.

    Luckily it is fairly easy to make better looking fonts using the GNU FreeType library. FreeType is the same library that Blizzard uses to render the fonts in their games, so you know it has to be good!

    Here's a close up of the text that I've created with the help of the FreeType library:

    按此在新窗口浏览图片

    You can see that there are a fair number of gray pixels around the edges of the text; this is typical of an anti-aliased font, the gray pixels make the font look smoother from a distance.

    Creating the Program

    The first step is to get yourself a copy of the GNU FreeType library. Go to http://gnuwin32.sourceforge.net/packages/freetype.htm and download the binaries and developer files. When you install it make sure to notice the licensing terms, which basically say that if you are going to use FreeType in your own programs, you need to give them credit somewhere in your documentation.

    Now we need to setup MSVC to use FreeType. So create new project as in Lesson 1, but when you go to Project->Settings->Link make sure you add libfreetype.lib to Object Modules / libraries along with opengl32.lib, glu32.lib and glaux.lib (if required).

    Next we need to go to Tools->Options->Directories and add the FreeType library directories. Under "Show Directories For" select "Include Files", then double click on the empty line at the bottom of the directory list, after you double click a "..." button will appear that you can use to browse for a directory. In this way add
      
       

    C:\PROGRAM FILES\GNUWIN32\INCLUDE\FREETYPE2
       
    and  
       

    C:\PROGRAM FILES\GNUWIN32\INCLUDE
       
    To you list of header directories.
    Now under "Show Directories For" select "Library Files", and this time add
      
       

    C:\PROGRAM FILES\GNUWIN32\LIB
       
    At this point we should be able to compile programs using FreeType, but they won't run unless they can access the freetype-6.dll. Right now you have a copy of that DLL file in your GNUWIN32\BIN directory, and if you put it somewhere where all your programs can see it (Program Files\Microsoft Visual Studio\VC98\Bin is a good choice), you will be able to run programs using FreeType. But remember that if you distribute a program that you have made using FreeType, you will need to also distribute copies of the DLL along with it.
    Ok, now we can finally start writing code. I've decided to work off the Lesson 13 source, so grab a copy of lesson 13 if you don't have one already. Copy lesson13.cpp to your project directory, and add the file to the project.

    Now add and create two new files called "freetype.cpp" and "freetype.h". We will put all our FreeType specific code into these files, and then modify lesson13 a little to show off the functions that we've written. When we are done, we will have created a very simple OpenGL FreeType library that could theoretically be used in any OpenGL project.

    We will start with freetype.h.

    Naturally, we need include the FreeType and OpenGL headers. We will also include some handy parts of the Standard Template Library, including STL's exception classes, which will make it easier for us create nice debugging messages.
      
       

    #ifndef FREE_NEHE_H#define FREE_NEHE_H
    // FreeType Headers
    #include <ft2build.h>
    #include <freetype/freetype.h>
    #include <freetype/ftglyph.h>
    #include <freetype/ftoutln.h>
    #include <freetype/fttrigon.h>

    // OpenGL Headers
    #include <windows.h> // (The GL Headers Need It)
    #include <GL/gl.h>
    #include <GL/glu.h>

    // Some STL Headers
    #include <vector>
    #include <string>

    // Using The STL Exception Library Increases The
    // Chances That Someone Else Using Our Code Will Correctly
    // Catch Any Exceptions That We Throw.
    #include <stdexcept>

    // MSVC Will Spit Out All Sorts Of Useless Warnings If
    // You Create Vectors Of Strings, This Pragma Gets Rid Of Them.
    #pragma warning(disable: 4786)


       
    We will put all the information that each font needs into one structure (this will make managing multiple fonts a little easier). As we learned in Lesson 13, when WGL creates a font, it generates a set of consecutive display lists. This is nifty, because it means that you can use glCallLists to print out a string of characters with just one command. When we create our font we will set things up the same way, which means that the list_base field will store the first of 128 consecutive display lists. Because we are going to use textures to draw our text, so we will also need storage for the 128 associated textures. The last bit of info that we will need is the height, in pixels, of the font that we have created (this will make it possible to handle newlines in our print function).  
       

    // Wrap Everything In A Namespace, That Way We Can Use A Common// Function Name Like "print" Without Worrying About// Overlapping With Anyone Else's Code.namespace freetype {
    // Inside Of This Namespace, Give Ourselves The Ability
    // To Write Just "vector" Instead Of "std::vector"
    using std::vector;

    // Ditto For String.
    using std::string;

    // This Holds All Of The Information Related To Any
    // FreeType Font That We Want To Create.
    struct font_data {
    float h; // Holds The Height Of The Font.
    GLuint * textures; // Holds The Texture Id's
    GLuint list_base; // Holds The First Display List Id

    // The Init Function Will Create A Font With
    // The Height h From The File fname.
    void init(const char * fname, unsigned int h);

    // Free All The Resources Associated With The Font.
    void clean();
    };


       
    The last thing we need is a prototype for our print function:  
       

    // The Flagship Function Of The Library - This Thing Will Print// Out Text At Window Coordinates X, Y, Using The Font ft_font.// The Current Modelview Matrix Will Also Be Applied To The Text. void print(const font_data &ft_font, float x, float y, const char *fmt, ...);
    } // Close The Namespace

    #endif


       
    And that's the end of the header file! Time to open up freetype.cpp.  
       

    // Include Our Header File.#include "freetype.h"
    namespace freetype {


       
    We are using textures to display each character in our font. OpenGL textures need to have dimensions that are powers of two, so we need to pad the font bitmaps created by FreeType to make them a legal size. That's why we need this function:  
       

    // This Function Gets The First Power Of 2 >= The// Int That We Pass It.inline int next_p2 (int a ){ int rval=1; // rval<<=1 Is A Prettier Way Of Writing rval*=2;  while(rval<a) rval<<=1; return rval;}

       
    The next function that we need is make_dlist, it is really the heart of this code. It takes in an FT_Face, which is an object that FreeType uses to store information about a font, and creates a display list coresponding to the character which we pass in.   
       

    // Create A Display List Corresponding To The Given Character.void make_dlist ( FT_Face face, char ch, GLuint list_base, GLuint * tex_base ) {
    // The First Thing We Do Is Get FreeType To Render Our Character
    // Into A Bitmap. This Actually Requires A Couple Of FreeType Commands:

    // Load The Glyph For Our Character.
    if(FT_Load_Glyph( face, FT_Get_Char_Index( face, ch ), FT_LOAD_DEFAULT ))
    throw std::runtime_error("FT_Load_Glyph failed");

    // Move The Face's Glyph Into A Glyph Object.
    FT_Glyph glyph;
    if(FT_Get_Glyph( face->glyph, &glyph ))
    throw std::runtime_error("FT_Get_Glyph failed");

    // Convert The Glyph To A Bitmap.
    FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1 );
    FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph)glyph;

    // This Reference Will Make Accessing The Bitmap Easier.
    FT_Bitmap& bitmap=bitmap_glyph->bitmap;


    }

       
    Now that we have a bitmap created by FreeType, we need to pad it with empty pixels to make it a legal source for an OpenGL texture. It's important to remember that while OpenGL uses the term "bitmap" to mean binary images, in FreeType bitmaps store 8 bits of information per pixel, so FreeType's bitmaps can store the grays that we need to create anti-aliased text.   
       

    // Use Our Helper Function To Get The Widths Of // The Bitmap Data That We Will Need In Order To Create // Our Texture. int width = next_p2( bitmap.width ); int height = next_p2( bitmap.rows );
    // Allocate Memory For The Texture Data.
    GLubyte* expanded_data = new GLubyte[ 2 * width * height];

    // Here We Fill In The Data For The Expanded Bitmap.
    // Notice That We Are Using A Two Channel Bitmap (One For
    // Channel Luminosity And One For Alpha), But We Assign
    // Both Luminosity And Alpha To The Value That We
    // Find In The FreeType Bitmap.
    // We Use The ?: Operator To Say That Value Which We Use
    // Will Be 0 If We Are In The Padding Zone, And Whatever
    // Is The FreeType Bitmap Otherwise.
    for(int j=0; j <height;j++) {
    for(int i=0; i < width; i++){
    expanded_data[2*(i+j*width)]= expanded_data[2*(i+j*width)+1] =
    (i>=bitmap.width || j>=bitmap.rows) ?
    0 : bitmap.buffer[i + bitmap.width*j];
    }
    }


       
    With the padding done, we can get onto creating the OpenGL texture. We are including an alpha channel so that the black parts of the bitmap will be transparent, and so that the edges of the text will be slightly translucent (which should make them look right against any background).   
       

    // Now We Just Setup Some Texture Parameters. glBindTexture( GL_TEXTURE_2D, tex_base[ch]); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    // Here We Actually Create The Texture Itself, Notice
    // That We Are Using GL_LUMINANCE_ALPHA To Indicate That
    // We Are Using 2 Channel Data.
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
    GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded_data );

    // With The Texture Created, We Don't Need The Expanded Data Anymore.
    delete [] expanded_data;


       
    We use texture mapped quads to draw our text. This means that it will be easy to rotate and scale text, and it will also make fonts inherit their color from the current OpenGL color (none of which would be true if we used pixmaps).  
       

    // Now We Create The Display List glNewList(list_base+ch,GL_COMPILE);
    glBindTexture(GL_TEXTURE_2D,tex_base[ch]);

    // First We Need To Move Over A Little So That
    // The Character Has The Right Amount Of Space
    // Between It And The One Before It.
    glTranslatef(bitmap_glyph->left,0,0);

    // Now We Move Down A Little In The Case That The
    // Bitmap Extends Past The Bottom Of The Line
    // This Is Only True For Characters Like 'g' Or 'y'.
    glPushMatrix();
    glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);

    // Now We Need To Account For The Fact That Many Of
    // Our Textures Are Filled With Empty Padding Space.
    // We Figure What Portion Of The Texture Is Used By
    // The Actual Character And Store That Information In
    // The x And y Variables, Then When We Draw The
    // Quad, We Will Only Reference The Parts Of The Texture
    // That Contains The Character Itself.
    float x=(float)bitmap.width / (float)width,
    y=(float)bitmap.rows / (float)height;

    // Here We Draw The Texturemapped Quads.
    // The Bitmap That We Got From FreeType Was Not
    // Oriented Quite Like We Would Like It To Be,
    // But We Link The Texture To The Quad
    // In Such A Way That The Result Will Be Properly Aligned.
    glBegin(GL_QUADS);
    glTexCoord2d(0,0); glVertex2f(0,bitmap.rows);
    glTexCoord2d(0,y); glVertex2f(0,0);
    glTexCoord2d(x,y); glVertex2f(bitmap.width,0);
    glTexCoord2d(x,0); glVertex2f(bitmap.width,bitmap.rows);
    glEnd();
    glPopMatrix();
    glTranslatef(face->glyph->advance.x >> 6 ,0,0);

    // Increment The Raster Position As If We Were A Bitmap Font.
    // (Only Needed If You Want To Calculate Text Length)
    // glBitmap(0,0,0,0,face->glyph->advance.x >> 6,0,NULL);

    // Finish The Display List
    glEndList();
    }


       
    The next function that we are going to create will use make_dlist to create a set of display lists corresponding to a given font file and pixel height.
    FreeType uses truetype fonts, so you will want to find yourself some truetype font files to feed into this function. Truetype font files are very common, and there are a bunch of sites out there where you can download lots of different truetype fonts for free. Windows 98 used truetype for nearly all of it's fonts, so if you can find an old computer running windows98, you can get all of the standard fonts in truetype format from its windows/fonts directory.
      
       

    void font_data::init(const char * fname, unsigned int h) { // Allocate Some Memory To Store The Texture Ids. textures = new GLuint[128];
    this->h=h;

    // Create And Initilize A FreeType Font Library.
    FT_Library library;
    if (FT_Init_FreeType( &library ))
    throw std::runtime_error("FT_Init_FreeType failed");

    // The Object In Which FreeType Holds Information On A Given
    // Font Is Called A "face".
    FT_Face face;

    // This Is Where We Load In The Font Information From The File.
    // Of All The Places Where The Code Might Die, This Is The Most Likely,
    // As FT_New_Face Will Fail If The Font File Does Not Exist Or Is Somehow Broken.
    if (FT_New_Face( library, fname, 0, &face ))
    throw std::runtime_error("FT_New_Face failed (there is probably a problem with your font file)");

    // For Some Twisted Reason, FreeType Measures Font Size
    // In Terms Of 1/64ths Of Pixels. Thus, To Make A Font
    // h Pixels High, We Need To Request A Size Of h*64.
    // (h << 6 Is Just A Prettier Way Of Writing h*64)
    FT_Set_Char_Size( face, h << 6, h << 6, 96, 96);

    // Here We Ask OpenGL To Allocate Resources For
    // All The Textures And Display Lists Which We
    // Are About To Create.
    list_base=glGenLists(128);
    glGenTextures( 128, textures );

    // This Is Where We Actually Create Each Of The Fonts Display Lists.
    for(unsigned char i=0;i<128;i++)
    make_dlist(face,i,list_base,textures);

    // We Don't Need The Face Information Now That The Display
    // Lists Have Been Created, So We Free The Assosiated Resources.
    FT_Done_Face(face);

    // Ditto For The Font Library.
    FT_Done_FreeType(library);
    }


       
    Now we need a function to cleanup all the displaylist and textures associated with a font.  
       

    void font_data::clean() { glDeleteLists(list_base,128); glDeleteTextures(128,textures); delete [] textures;
       
    Here are two little functions that we are going to define in anticipation of our print function. The print function is going to want to think in pixel coordinates (also called window coordinates), so we are going to need to switch to a projection matrix that makes everything measured in pixel coordinates.
    We are using two very handy OpenGL functions here, glGet to get the window dimensions, and glPush / PopAttrib to make sure that leave the matrix mode in the same state as we found it. If you are not familiar with these functions, it's probably worth your time to look them up in your favorite OpenGL reference manual.
      
       

    // A Fairly Straightforward Function That Pushes// A Projection Matrix That Will Make Object World // Coordinates Identical To Window Coordinates.inline void pushScreenCoordinateMatrix() { glPushAttrib(GL_TRANSFORM_BIT); GLint   viewport[4]; glGetIntegerv(GL_VIEWPORT, viewport); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); gluOrtho2D(viewport[0],viewport[2],viewport[1],viewport[3]); glPopAttrib();}
    // Pops The Projection Matrix Without Changing The Current
    // MatrixMode.
    inline void pop_projection_matrix() {
    glPushAttrib(GL_TRANSFORM_BIT);
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glPopAttrib();


       
    Our printing function looks very much like the one from lesson 13, but there are a couple of important differences. The OpenGL enable flags that we set are different, which reflects the fact that we are actually using 2 channel textures rather than bitmaps. We also do a little extra processing on the line of text that we get in order to properly handle newlines. Because we are such good samaritans, we take care to use OpenGL's matrix and attribute stacks to ensure that the function undoes any changes that it makes to OpenGL's internal state (this will prevent anyone using the function from suddenly finding that, say, the modelview matrix had mysteriously changed).   
       

    // Much Like NeHe's glPrint Function, But Modified To Work// With FreeType Fonts.void print(const font_data &ft_font, float x, float y, const char *fmt, ...)  {         // We Want A Coordinate System Where Distance Is Measured In Window Pixels. pushScreenCoordinateMatrix();                                            GLuint font=ft_font.list_base; // We Make The Height A Little Bigger.  There Will Be Some Space Between Lines. float h=ft_font.h/.63f;                                                  char text[256];         // Holds Our String va_list ap;          // Pointer To List Of Arguments
    if (fmt == NULL) // If There's No Text
    *text=0; // Do Nothing
    else {
    va_start(ap, fmt); // Parses The String For Variables
    vsprintf(text, fmt, ap); // And Converts Symbols To Actual Numbers
    va_end(ap); // Results Are Stored In Text
    }

    // Here Is Some Code To Split The Text That We Have Been
    // Given Into A Set Of Lines.
    // This Could Be Made Much Neater By Using
    // A Regular Expression Library Such As The One Available From
    // boost.org (I've Only Done It Out By Hand To Avoid Complicating
    // This Tutorial With Unnecessary Library Dependencies).
    const char *start_line=text;
    vector<string> lines;
    for(const char *c=text;*c;c++) {
    if(*c=='\n') {
    string line;
    for(const char *n=start_line;n<c;n++) line.append(1,*n);
    lines.push_back(line);
    start_line=c+1;
    }
    }
    if(start_line) {
    string line;
    for(const char *n=start_line;n<c;n++) line.append(1,*n);
    lines.push_back(line);
    }

    glPushAttrib(GL_LIST_BIT | GL_CURRENT_BIT | GL_ENABLE_BIT | GL_TRANSFORM_BIT);
    glMatrixMode(GL_MODELVIEW);
    glDisable(GL_LIGHTING);
    glEnable(GL_TEXTURE_2D);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

    glListBase(font);


       
    Because we are using texture mapped quads, any transformations that we apply to the modelview matrix before making our glCallLists call will apply to the text itself. This means that there is the potential to rotate or scale the text (another advantage over using WGL bitmaps). The most natural way to take advantage of this fact would be to leave the current modelview matrix alone, thus letting any transformation made before the print function apply to the text. But because of the way that we are using the modelview matrix to set font position, this won't work. Our next best option is to save a copy of the modelview matrix that is passed in, and apply it between the glTranslate and the glCallLists. This is easy enough to do, but because we need to draw the text using a special projection matrix the effects of the modelview matrix will be a little different than one might expect- everything will be will be interpreted on scale of pixels. We could get around this issue entirely by not resetting the projection matrix inside of print. This is probably a good idea in some situations - but if you try it make sure to scale the fonts to an appropriate size (they tend to be something like 32x32, and you probably want something on the order of 0.01x0.01).   
       

     float modelview_matrix[16];      glGetFloatv(GL_MODELVIEW_MATRIX, modelview_matrix);
    // This Is Where The Text Display Actually Happens.
    // For Each Line Of Text We Reset The Modelview Matrix
    // So That The Line's Text Will Start In The Correct Position.
    // Notice That We Need To Reset The Matrix, Rather Than Just Translating
    // Down By h. This Is Because When Each Character Is
    // Drawn It Modifies The Current Matrix So That The Next Character
    // Will Be Drawn Immediately After It.
    for(int i=0;i<lines.size();i++) {
    glPushMatrix();
    glLoadIdentity();
    glTranslatef(x,y-h*i,0);
    glMultMatrixf(modelview_matrix);

    // The Commented Out Raster Position Stuff Can Be Useful If You Need To
    // Know The Length Of The Text That You Are Creating.
    // If You Decide To Use It Make Sure To Also Uncomment The glBitmap Command
    // In make_dlist().
    // glRasterPos2f(0,0);
    glCallLists(lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str());
    // float rpos[4];
    // glGetFloatv(GL_CURRENT_RASTER_POSITION ,rpos);
    // float len=x-rpos[0]; (Assuming No Rotations Have Happend)

    glPopMatrix();
    }

    glPopAttrib();

    pop_projection_matrix();
    }

    } // Close The Namespace


    }

       
    The library is now finished. Open up lesson13.cpp and we will make some minor modifications to show off the functions we just wrote.
    Underneth the other headers, add in the freetype.h header.
      
       

    #include "freetype.h"
       
    And while we are here, let's create a global font_data object.   
       

    // This Holds All The Information For The Font That We Are Going To Create.freetype::font_data our_font;
       
    Now we need to see about creating and destroying the resources for our font. So add the following line to the end of InitGL   
       

    our_font.init("Test.ttf", 16);
       
    And add this line to the start of KillGLWindow to destroy the font when we are finished  
       

    our_font.clean();
       
    Now we need to modify the function DrawGLScene function so that it uses our print function. This could have been as simple to adding a single line "hello world" command to the end of the function, but I got a little more creative because I wanted to show off rotations and scaling.   
       

    int DrawGLScene(GLvoid)          // Here's Where We Do All The Drawing{ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);     // Clear Screen And Depth Buffer glLoadIdentity();         // Reset The Current Modelview Matrix glTranslatef(0.0f,0.0f,-1.0f);        // Move One Unit Into The Screen
    // Blue text
    glColor3ub(0,0,0xff);

    // Position the WGL Text On The Screen
    glRasterPos2f(-0.40f, 0.35f);
    glPrint("Active WGL Bitmap Text With NeHe - %7.2f", cnt1); // Print GL Text To The Screen

    // Here We Print Some Text Using Our Freetype Font
    // The Only Really Important Command Is The Actual Print() Call,
    // But For The Sake Of Making The Results A Bit More Interesting
    // I Have Put In Some Code To Rotate And Scale The Text.

    // Red Text
    glColor3ub(0xff,0,0);

    glPushMatrix();
    glLoadIdentity();
    glRotatef(cnt1,0,0,1);
    glScalef(1,.8+.3*cos(cnt1/5),1);
    glTranslatef(-180,0,0);
    freetype::print(our_font, 320, 200, "Active FreeType Text - %7.2f", cnt1);
    glPopMatrix();

    // Uncomment This To Test Out Print's Ability To Handle Newlines.
    // freetype::print(our_font, 320, 200, "Here\nthere\nbe\n\nnewlines\n.", cnt1);

    cnt1+=0.051f; // Increase The First Counter
    cnt2+=0.005f; // Increase The First Counter
    return TRUE; // Everything Went OK
    }


       
    The last thing to do is put in a little exception handling code. Go to WinMain and open up a try { .. } statement at the beginning of the function.   
       

    MSG msg;          // Windows Message Structure BOOL done=FALSE;         // Bool Variable To Exit Loop
    try { // Use Exception Handling


       
    Then modify the end of the function to have a catch {} command.  
       

     // Shutdown KillGLWindow();          // Kill The Window
    // Catch Any Exceptions That Were Thrown
    } catch (std::exception &e) {
    MessageBox(NULL,e.what(),"CAUGHT AN EXCEPTION",MB_OK | MB_ICONINFORMATION);
    }

    return (msg.wParam);

       
    Now if we ever hit an exception, we will get a little message box telling us what happened. Note that exception handling can slow down your code, so when you are compiling a release version of your program, you may want to go to Project->Settings->C/C++, switch to the "C++ Language" category, and turn off exception handling.
    So that's it! Compile the program and you should see some nice FreeType rendered text moving around underneath the original bitmapped text from lesson 13.


    General Notes

    There are a number of improvements that you might want to make to this library. For one thing using the font data objects directly tends to be a little awkward, so you might want to create a standard cache of fonts to hide font resource management from users. You might also want to take a tip from the OpenGL library itself and create a font stack, which would let you avoid passing in references to font objects each time you called the print function. (These are all things that I currently do things in my own code, but decided to leave out of the tutorial for the sake of simplicity.) You might also want to make a version of print that centers the text- to do that you will probably need to use the techniques discussed below.

    Right now I have the text spinning around it's center. However, to get an effect like this to work for arbitrary text, you would need to have some way of knowing exactly how long the line of text was - this can be a little tricky. One way to get the length of the text is to put glBitmap commands into the font's display lists in order modify the raster position as well as the modelview matrix (I've left the necessary line in the code, but it is commented out). Then you can set the raster position to x,y before using glCallLists, and use glGet to find the raster position after the text is drawn - the difference in raster positions will give you the length of the text in pixels.

    You should be aware that FreeType fonts take up much more memory than WGL's bitmap fonts (that's one the advantages of binary images, they use very little space). If for some reason you really need to conserve your texture memory, you might want to stick with the code from lesson 13.

    Another interesting advantage of using texture mapped quads to represent fonts is that quads, unlike bitmaps, work well with the OpenGL picking functions (see Lesson 32 ). This makes life much easier if you want to create text that responds when someone holds the mouse over it or clicks on it. (Making WGL fonts work well with picking functions is possible, again the key trick is to use raster coordinates to figure out the length of the text in pixels).

    And finally, here are some links to OpenGL font libraries. Depending on your goals and compiler you may want to use one of them instead of this code (there are many more of them out there, I've generally only included things that I have some amount of experience with myself).

    GLTT This library is an old library that doesn't seem to still be maintained, but it has gotten some very positive reviews. Based on FreeType1. I think you will need to find a copy of the old FreeType1 source distribution to compile it in MSVC6. Download available from http://www.opengl.org/developers/faqs/technical/fonts.htm.

    OGLFT A nice FreeType2 based font library, it takes a bit of work to compile under MSVC though (mostly just typical for-loop scope problems). Seems to be targeted at linux machines... http://oglft.sourceforge.net.

    FTGL Yet a third FreeType based font library, this one was clearly developed for OS X. http://homepages.paradise.net.nz/henryj/code/#FTGL.

    FNT A non-FreeType library that is part of PLIB. Seems to have a nice interface, uses its own font format, compiles under MSVC6 with a minimum amount of fuss... http://plib.sourceforge.net/fnt.

    Sven Olsen

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 21:15:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客3
    发贴心情 
    3D 光晕

    当镜头对准太阳的时候就会出现这种效果,模拟它非常的简单,一点数学和纹理贴图就够了。好好看看吧。

      
       
       
    大家好,欢迎来到新的一课,在这一课中我们将扩展glCamera类,来实现镜头光晕的效果。在日常生活中,当我们对着光源看时,会发现强烈的反光。
    为了完成这个效果,我们需要一些数学知识。首先,我们需要一些函数,用来检测某个点或球是否在当前的视景体内。接着我们需要一些纹理作为我们的光晕效果,我们可以把它贴在显示面上。
    在我的上一个摄像机类里把下面函数写错了,现在修正如下:
      
       

    void glCamera::SetPrespective(){ GLfloat Matrix[16];             glVector v;                               
    // 根据当前的偏转角旋转视线
    glRotatef(m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(m_PitchDegrees, 1.0f, 0.0f, 0.0f);

    // 返回模型变换矩阵
    glGetFloatv(GL_MODELVIEW_MATRIX, Matrix);

    // 获得视线的方向
    m_DirectionVector.i = Matrix[8];
    m_DirectionVector.j = Matrix[9];
    m_DirectionVector.k = -Matrix[10];

    // 重置矩阵
    glLoadIdentity();

    // 旋转场景
    glRotatef(m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glRotatef(m_HeadingDegrees, 0.0f, 1.0f, 0.0f);

    // 设置当前摄像机的位置
    v = m_DirectionVector;
    v *= m_ForwardVelocity;
    m_Position.x += v.i;
    m_Position.y += v.j;
    m_Position.z += v.k;

    // 变换到新的位置
    glTranslatef(-m_Position.x, -m_Position.y, -m_Position.z);
    }


       
    好了,我们现在开始吧。我将使用4个对立的纹理来制造我们的镜头光晕,第一和二个光晕图像被放置在光源处,第三和第四个图像将根据视点的位置和方向动态的生成。纹理的图像如下所示:

    Big Glow

    按此在新窗口浏览图片   

       Streaks
    按此在新窗口浏览图片

    Glow

    按此在新窗口浏览图片   

    Halo

    按此在新窗口浏览图片

    现在你在头脑里应该有了一个大慨地图像了吧。我们来说说何时我们应该绘制光晕,一般来说平时我们是看不见这些光晕的,只有当我们对准光源的时候才能看见这些。所以我们首先要获得视景体的数据,下面的函数可以帮我们完成这个功能。
      
       

    // 获得当前视景体的6个平面方程的参数void glCamera::UpdateFrustum(){    GLfloat   clip[16]; GLfloat   proj[16];    GLfloat   modl[16];    GLfloat   t;
    //返回投影矩阵
    glGetFloatv( GL_PROJECTION_MATRIX, proj );

    //返回模型变换矩阵
    glGetFloatv( GL_MODELVIEW_MATRIX, modl );

    //计算剪切矩阵,即上面两个矩阵的乘积
    clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
    clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
    clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
    clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];

    clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
    clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
    clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
    clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];

    clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
    clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
    clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
    clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];

    clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
    clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
    clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
    clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];

    //提取右面的平面方程系数
    m_Frustum[0][0] = clip[ 3] - clip[ 0];
    m_Frustum[0][1] = clip[ 7] - clip[ 4];
    m_Frustum[0][2] = clip[11] - clip[ 8];
    m_Frustum[0][3] = clip[15] - clip[12];
    t = GLfloat(sqrt( m_Frustum[0][0] * m_Frustum[0][0] + m_Frustum[0][1] * m_Frustum[0][1] + m_Frustum[0][2] * m_Frustum[0][2] ));
    m_Frustum[0][0] /= t;
    m_Frustum[0][1] /= t;
    m_Frustum[0][2] /= t;
    m_Frustum[0][3] /= t;

    //提取左面的平面方程系数
    m_Frustum[1][0] = clip[ 3] + clip[ 0];
    m_Frustum[1][1] = clip[ 7] + clip[ 4];
    m_Frustum[1][2] = clip[11] + clip[ 8];
    m_Frustum[1][3] = clip[15] + clip[12];
    t = GLfloat(sqrt( m_Frustum[1][0] * m_Frustum[1][0] + m_Frustum[1][1] * m_Frustum[1][1] + m_Frustum[1][2] * m_Frustum[1][2] ));
    m_Frustum[1][0] /= t;
    m_Frustum[1][1] /= t;
    m_Frustum[1][2] /= t;
    m_Frustum[1][3] /= t;

    //提取下面的平面方程系数
    m_Frustum[2][0] = clip[ 3] + clip[ 1];
    m_Frustum[2][1] = clip[ 7] + clip[ 5];
    m_Frustum[2][2] = clip[11] + clip[ 9];
    m_Frustum[2][3] = clip[15] + clip[13];
    t = GLfloat(sqrt( m_Frustum[2][0] * m_Frustum[2][0] + m_Frustum[2][1] * m_Frustum[2][1] + m_Frustum[2][2] * m_Frustum[2][2] ));
    m_Frustum[2][0] /= t;
    m_Frustum[2][1] /= t;
    m_Frustum[2][2] /= t;
    m_Frustum[2][3] /= t;

    //提取上面的平面方程系数
    m_Frustum[3][0] = clip[ 3] - clip[ 1];
    m_Frustum[3][1] = clip[ 7] - clip[ 5];
    m_Frustum[3][2] = clip[11] - clip[ 9];
    m_Frustum[3][3] = clip[15] - clip[13];
    t = GLfloat(sqrt( m_Frustum[3][0] * m_Frustum[3][0] + m_Frustum[3][1] * m_Frustum[3][1] + m_Frustum[3][2] * m_Frustum[3][2] ));
    m_Frustum[3][0] /= t;
    m_Frustum[3][1] /= t;
    m_Frustum[3][2] /= t;
    m_Frustum[3][3] /= t;

    //提取远面的平面方程系数
    m_Frustum[4][0] = clip[ 3] - clip[ 2];
    m_Frustum[4][1] = clip[ 7] - clip[ 6];
    m_Frustum[4][2] = clip[11] - clip[10];
    m_Frustum[4][3] = clip[15] - clip[14];
    t = GLfloat(sqrt( m_Frustum[4][0] * m_Frustum[4][0] + m_Frustum[4][1] * m_Frustum[4][1] + m_Frustum[4][2] * m_Frustum[4][2] ));
    m_Frustum[4][0] /= t;
    m_Frustum[4][1] /= t;
    m_Frustum[4][2] /= t;
    m_Frustum[4][3] /= t;

    //提取近面的平面方程系数
    m_Frustum[5][0] = clip[ 3] + clip[ 2];
    m_Frustum[5][1] = clip[ 7] + clip[ 6];
    m_Frustum[5][2] = clip[11] + clip[10];
    m_Frustum[5][3] = clip[15] + clip[14];
    t = GLfloat(sqrt( m_Frustum[5][0] * m_Frustum[5][0] + m_Frustum[5][1] * m_Frustum[5][1] + m_Frustum[5][2] * m_Frustum[5][2] ));
    m_Frustum[5][0] /= t;
    m_Frustum[5][1] /= t;
    m_Frustum[5][2] /= t;
    m_Frustum[5][3] /= t;
    }


       
    现在我们可以测试一个点或圆是否在视景体内了。下面的函数可以测试一个点是否在视景体内。  
       

    BOOL glCamera::PointInFrustum(glPoint p){ int i; for(i = 0; i < 6; i++) {  if(m_Frustum[i][0] * p.x + m_Frustum[i][1] * p.y + m_Frustum[i][2] * p.z + m_Frustum[i][3] <= 0)  {   return(FALSE);  } } return(TRUE);}

       
    下面的函数用来测试某个点是否位于当前场景物体的前面:
      
       

    bool glCamera::IsOccluded(glPoint p){ GLint viewport[4];       GLdouble mvmatrix[16], projmatrix[16];   GLdouble winx, winy, winz;     GLdouble flareZ;       GLfloat bufferZ;      
    glGetIntegerv (GL_VIEWPORT, viewport);
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);

    // 返回顶点p在单位立方体中的位置
    gluProject(p.x, p.y, p.z, mvmatrix, projmatrix, viewport, &winx, &winy, &winz);
    flareZ = winz;

    // 读取点(winx,winy)的深度坐标
    glReadPixels(winx, winy,1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &bufferZ);

    // 如果深度坐标小于点的坐标,则返回true
    if (bufferZ < flareZ)
    return true;
    //否则返回false
    else
    return false;
    }


       
    我们通过检测光源是否正对我们的视线来决定是否绘制光晕,但如果你的视点超过了光源的位置,则会发生看不见光晕的现象。为了避免这种现象,我们在移动视点的使用,也相应的移动我们的光源。为了在视点和光源之间绘制多个光晕,我们需要计算之间的向量,下面的代码完成这个功能:  
       

    //下面的函数完成具体的渲染光晕的任务void glCamera::RenderLensFlare(){ GLfloat Length = 0.0f;
    // 如果我们的光源在我们的视线范围内,则绘制它
    if(SphereInFrustum(m_LightSourcePos, 1.0f) == TRUE)
    {
    vLightSourceToCamera = m_Position - m_LightSourcePos; // 计算光源到我们视线的距离
    Length = vLightSourceToCamera.Magnitude();

    //下面三个函数计算光源位置到光晕结束位置之间的向量
    ptIntersect = m_DirectionVector * Length;
    ptIntersect += m_Position;
    vLightSourceToIntersect = ptIntersect - m_LightSourcePos;
    Length = vLightSourceToIntersect.Magnitude();
    vLightSourceToIntersect.Normalize();

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);

       
    首先我们需要找到光源位置和视点位置之间的向量,接下来我们需要在视线的方向设置一个插值点,这个点的距离必须和光源位置和视点位置之间的距离相等。完成以后,我们找出可以产生光晕的方向,即下图红线的方向,在这个线上我们可以绘制我们的光晕。


    按此在新窗口浏览图片


      
       

      if (!IsOccluded(m_LightSourcePos)) //如果光晕可见  {   // 渲染中间的光晕   RenderBigGlow(0.60f, 0.60f, 0.8f, 1.0f, m_LightSourcePos, 16.0f);   RenderStreaks(0.60f, 0.60f, 0.8f, 1.0f, m_LightSourcePos, 16.0f);   RenderGlow(0.8f, 0.8f, 1.0f, 0.5f, m_LightSourcePos, 3.5f);
    //绘制到光晕结束位置的0.1处的光晕
    pt = vLightSourceToIntersect * (Length * 0.1f);
    pt += m_LightSourcePos;
    RenderGlow(0.9f, 0.6f, 0.4f, 0.5f, pt, 0.6f);

    //绘制到光晕结束位置的0.15处的光晕
    pt = vLightSourceToIntersect * (Length * 0.15f);
    pt += m_LightSourcePos;
    RenderHalo(0.8f, 0.5f, 0.6f, 0.5f, pt, 1.7f);

    //绘制到光晕结束位置的0.175处的光晕
    pt = vLightSourceToIntersect * (Length * 0.175f);
    pt += m_LightSourcePos;
    RenderHalo(0.9f, 0.2f, 0.1f, 0.5f, pt, 0.83f);

    //绘制到光晕结束位置的0.285处的光晕
    pt = vLightSourceToIntersect * (Length * 0.285f);
    pt += m_LightSourcePos;
    RenderHalo(0.7f, 0.7f, 0.4f, 0.5f, pt, 1.6f);

    //绘制到光晕结束位置的0.2755处的光晕
    pt = vLightSourceToIntersect * (Length * 0.2755f);
    pt += m_LightSourcePos;
    RenderGlow(0.9f, 0.9f, 0.2f, 0.5f, pt, 0.8f);

    //绘制到光晕结束位置的0.4755处的光晕
    pt = vLightSourceToIntersect * (Length * 0.4775f);
    pt += m_LightSourcePos;
    RenderGlow(0.93f, 0.82f, 0.73f, 0.5f, pt, 1.0f);

    //绘制到光晕结束位置的0.49处的光晕
    pt = vLightSourceToIntersect * (Length * 0.49f);
    pt += m_LightSourcePos;
    RenderHalo(0.7f, 0.6f, 0.5f, 0.5f, pt, 1.4f);

    //绘制到光晕结束位置的0.65处的光晕
    pt = vLightSourceToIntersect * (Length * 0.65f);
    pt += m_LightSourcePos;
    RenderGlow(0.7f, 0.8f, 0.3f, 0.5f, pt, 1.8f);

    //绘制到光晕结束位置的0.63处的光晕
    pt = vLightSourceToIntersect * (Length * 0.63f);
    pt += m_LightSourcePos;
    RenderGlow(0.4f, 0.3f, 0.2f, 0.5f, pt, 1.4f);

    //绘制到光晕结束位置的0.8处的光晕
    pt = vLightSourceToIntersect * (Length * 0.8f);
    pt += m_LightSourcePos;
    RenderHalo(0.7f, 0.5f, 0.5f, 0.5f, pt, 1.4f);

    //绘制到光晕结束位置的0.7825处的光晕
    pt = vLightSourceToIntersect * (Length * 0.7825f);
    pt += m_LightSourcePos;
    RenderGlow(0.8f, 0.5f, 0.1f, 0.5f, pt, 0.6f);

    //绘制到光晕结束位置的1.0处的光晕
    pt = vLightSourceToIntersect * (Length * 1.0f);
    pt += m_LightSourcePos;
    RenderHalo(0.5f, 0.5f, 0.7f, 0.5f, pt, 1.7f);

    //绘制到光晕结束位置的0.975处的光晕
    pt = vLightSourceToIntersect * (Length * 0.975f);
    pt += m_LightSourcePos;
    RenderGlow(0.4f, 0.1f, 0.9f, 0.5f, pt, 2.0f);

    }
    glDisable(GL_BLEND );
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);
    }
    }


       
    好了,下面的函数用来绘制四种不同的光晕  
       

    //绘制Halo形的光晕void glCamera::RenderHalo(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale){ glPoint q[4];  q[0].x = (p.x - scale);            q[0].y = (p.y - scale);              q[1].x = (p.x - scale);            q[1].y = (p.y + scale);              q[2].x = (p.x + scale);            q[2].y = (p.y - scale);              q[3].x = (p.x + scale);            q[3].y = (p.y + scale);              glPushMatrix();              glTranslatef(p.x, p.y, p.z);          glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f); glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f); glBindTexture(GL_TEXTURE_2D, m_HaloTexture);      glColor4f(r, g, b, a);             glBegin(GL_TRIANGLE_STRIP);            glTexCoord2f(0.0f, 0.0f);       glVertex2f(q[0].x, q[0].y);  glTexCoord2f(0.0f, 1.0f);  glVertex2f(q[1].x, q[1].y);  glTexCoord2f(1.0f, 0.0f);  glVertex2f(q[2].x, q[2].y);  glTexCoord2f(1.0f, 1.0f);  glVertex2f(q[3].x, q[3].y); glEnd();           glPopMatrix();             }
    //绘制Gloew形的光晕
    void glCamera::RenderGlow(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    q[0].x = (p.x - scale);
    q[0].y = (p.y - scale);

    q[1].x = (p.x - scale);
    q[1].y = (p.y + scale);

    q[2].x = (p.x + scale);
    q[2].y = (p.y - scale);

    q[3].x = (p.x + scale);
    q[3].y = (p.y + scale);

    glPushMatrix();
    glTranslatef(p.x, p.y, p.z);
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_GlowTexture);
    glColor4f(r, g, b, a);

    glBegin(GL_TRIANGLE_STRIP);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix();
    }

    //绘制BigGlow形的光晕
    void glCamera::RenderBigGlow(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    q[0].x = (p.x - scale);
    q[0].y = (p.y - scale);

    q[1].x = (p.x - scale);
    q[1].y = (p.y + scale);

    q[2].x = (p.x + scale);
    q[2].y = (p.y - scale);

    q[3].x = (p.x + scale);
    q[3].y = (p.y + scale);

    glPushMatrix();
    glTranslatef(p.x, p.y, p.z);
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_BigGlowTexture);
    glColor4f(r, g, b, a);

    glBegin(GL_TRIANGLE_STRIP);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix();
    }

    //绘制Streaks形的光晕
    void glCamera::RenderStreaks(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    q[0].x = (p.x - scale);
    q[0].y = (p.y - scale);

    q[1].x = (p.x - scale);
    q[1].y = (p.y + scale);

    q[2].x = (p.x + scale);
    q[2].y = (p.y - scale);

    q[3].x = (p.x + scale);
    q[3].y = (p.y + scale);

    glPushMatrix();
    glTranslatef(p.x, p.y, p.z);
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_StreakTexture);
    glColor4f(r, g, b, a);

    glBegin(GL_TRIANGLE_STRIP);
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix();
    }

       
    你可以使用w,s,a,d变换摄像机的方向,1,2显示/关闭各种信息参数。C给摄像机一个固定的速度,X停止它。

    上面就是这个教程的全部了,所有的问题,评论和抱怨都欢迎。当然我不是第一个作这个效果的人,下面是其他方面相关的文章:
    http://www.gamedev.net/reference/articles/article874.asp
    http://www.gamedev.net/reference/articles/article813.asp
    http://www.opengl.org/developers/code/mjktips/lensflare/
    http://www.markmorley.com/opengl/frustumculling.html
    http://oss.sgi.com/projects/ogl-sample/registry/HP/occlusion_test.txt
    http://oss.sgi.com/projects/ogl-sample/registry/NV/occlusion_query.txt

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 21:29:00
     
     一分之千 帅哥哟,离线,有人找我吗?射手座1984-11-30
      
      
      威望:1
      等级:研一(随老板参加了WWW大会还和Tim Berners-Lee合了影^_^)
      文章:632
      积分:4379
      门派:XML.ORG.CN
      注册:2006/12/31

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给一分之千发送一个短消息 把一分之千加入好友 查看一分之千的个人资料 搜索一分之千在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看一分之千的博客4
    发贴心情 

    Lesson: 44
       
    Hi everyone its me again with another tutorial. In this one I will be showing you how to do lens flares by extending the glCamera class. If you look at a lens flare you will notice that they all share one thing in common. They all seem to move through the center of the screen. With this in mind you could actually just throw out the z coordinate and make your flares all 2D. The only problem with this approach is without a z coordinate how do you find out if the camera is looking at the light source or not? In this tutorial we will be making 3D lens flares so get ready for a little bit of math. We will need to add a few things to the camera class in order to pull this off. First off we need a set of functions to see if a point or sphere is inside the current viewing volume of the camera. Next we need a set of textures to use for the flares and finally we need to do this with out killing the processor!
    I'm somewhat embarrassed to admit it but there was a bug in the last Camera class that needs some fixing. Before we get started here is the code that fixes the bug. The SetPerspective function needs to be changed to the following.
      
       

    void glCamera::SetPrespective(){ GLfloat Matrix[16]; glVector v;
    // Going To Use glRotate To Calculate Our Direction Vector
    glRotatef(m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(m_PitchDegrees, 1.0f, 0.0f, 0.0f);

    // Get The Resulting Matrix From OpenGL It Will Have Our
    // Direction Vector In The 3rd Row
    glGetFloatv(GL_MODELVIEW_MATRIX, Matrix);

    // Get The Direction Vector From The Matrix. Element 10 Must
    // Be Inverted!
    m_DirectionVector.i = Matrix[8];
    m_DirectionVector.j = Matrix[9];
    m_DirectionVector.k = -Matrix[10];

    // Ok Erase The Results Of The Last Computation
    glLoadIdentity();

    // Rotate The Scene To Get The Right Orientation
    glRotatef(m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glRotatef(m_HeadingDegrees, 0.0f, 1.0f, 0.0f);

    // Scale The Direction By Our Speed
    v = m_DirectionVector;
    v *= m_ForwardVelocity;

    // Increment Our Position By The Vector
    m_Position.x += v.i;
    m_Position.y += v.j;
    m_Position.z += v.k;

    // Translate To Our New Position
    glTranslatef(-m_Position.x, -m_Position.y, -m_Position.z);
    }


       
    Ok now we can get down to business. We will be using 4 separate textures to make our lens flare. The first texture we need is what I call a Big Glow texture. Our light source will be surrounded with a hazy glow. This texture will always be located at the light source position. The next texture is the Streaks Texture. This texture surrounds our light source with streaks moving outwards. This texture will also be located at the light source position. The Glow texture (not the Big Glow) this is the more solid looking texture and will dynamically move across the screen. The Glow texture is similar to the Big Glow texture however I have noticed that using a more defined texture here looks better than simply using the Big Glow. And finally we will need a Halo texture. These are the hollow looking rings for the flare and will dynamically move across the screen according to the camera's orientation and position. There are a few other types of textures you can use for lens flares if you like its up to you. See the references at the end of the tutorial for additional information. Below are some examples of these textures.

    Big Glow
    按此在新窗口浏览图片
       Streaks
    按此在新窗口浏览图片

    Glow
    按此在新窗口浏览图片
       Halo
    按此在新窗口浏览图片

    Now that you have an idea of what we will be drawing let's talk about when we need to draw the lens flares. Obviously we do not want to draw the lens flares when we are not looking at the light source so we need to find a way to get the viewing volume or frustum from OpenGL. We could do this by combining the modelview and projection matrices then finding the clipping planes that OpenGL uses. Another approach to detecting if a point is in view is to use extensions. We could use GL_HP_occlusion_test or GL_NV_occlusion_query extensions to find out if a vertex is in view of the camera but not everyone has these extension so we will limit ourselves to the old fashioned way in this tutorial. Below is the UpdateFrustum Function.

      
       

    //void glCamera::UpdateFrustum(){ GLfloat clip[16]; GLfloat proj[16]; GLfloat modl[16]; GLfloat t;
    // Get The Current PROJECTION Matrix From OpenGL
    glGetFloatv( GL_PROJECTION_MATRIX, proj );

    // Get The Current MODELVIEW Matrix From OpenGL
    glGetFloatv( GL_MODELVIEW_MATRIX, modl );

    // Combine The Two Matrices (Multiply Projection By Modelview)
    clip[ 0] = modl[ 0] * proj[ 0] + modl[ 1] * proj[ 4] + modl[ 2] * proj[ 8] + modl[ 3] * proj[12];
    clip[ 1] = modl[ 0] * proj[ 1] + modl[ 1] * proj[ 5] + modl[ 2] * proj[ 9] + modl[ 3] * proj[13];
    clip[ 2] = modl[ 0] * proj[ 2] + modl[ 1] * proj[ 6] + modl[ 2] * proj[10] + modl[ 3] * proj[14];
    clip[ 3] = modl[ 0] * proj[ 3] + modl[ 1] * proj[ 7] + modl[ 2] * proj[11] + modl[ 3] * proj[15];

    clip[ 4] = modl[ 4] * proj[ 0] + modl[ 5] * proj[ 4] + modl[ 6] * proj[ 8] + modl[ 7] * proj[12];
    clip[ 5] = modl[ 4] * proj[ 1] + modl[ 5] * proj[ 5] + modl[ 6] * proj[ 9] + modl[ 7] * proj[13];
    clip[ 6] = modl[ 4] * proj[ 2] + modl[ 5] * proj[ 6] + modl[ 6] * proj[10] + modl[ 7] * proj[14];
    clip[ 7] = modl[ 4] * proj[ 3] + modl[ 5] * proj[ 7] + modl[ 6] * proj[11] + modl[ 7] * proj[15];

    clip[ 8] = modl[ 8] * proj[ 0] + modl[ 9] * proj[ 4] + modl[10] * proj[ 8] + modl[11] * proj[12];
    clip[ 9] = modl[ 8] * proj[ 1] + modl[ 9] * proj[ 5] + modl[10] * proj[ 9] + modl[11] * proj[13];
    clip[10] = modl[ 8] * proj[ 2] + modl[ 9] * proj[ 6] + modl[10] * proj[10] + modl[11] * proj[14];
    clip[11] = modl[ 8] * proj[ 3] + modl[ 9] * proj[ 7] + modl[10] * proj[11] + modl[11] * proj[15];

    clip[12] = modl[12] * proj[ 0] + modl[13] * proj[ 4] + modl[14] * proj[ 8] + modl[15] * proj[12];
    clip[13] = modl[12] * proj[ 1] + modl[13] * proj[ 5] + modl[14] * proj[ 9] + modl[15] * proj[13];
    clip[14] = modl[12] * proj[ 2] + modl[13] * proj[ 6] + modl[14] * proj[10] + modl[15] * proj[14];
    clip[15] = modl[12] * proj[ 3] + modl[13] * proj[ 7] + modl[14] * proj[11] + modl[15] * proj[15];

    // Extract The Numbers For The RIGHT Plane
    m_Frustum[0][0] = clip[ 3] - clip[ 0];
    m_Frustum[0][1] = clip[ 7] - clip[ 4];
    m_Frustum[0][2] = clip[11] - clip[ 8];
    m_Frustum[0][3] = clip[15] - clip[12];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[0][0] * m_Frustum[0][0] + m_Frustum[0][1] * m_Frustum[0][1] + m_Frustum[0][2] * m_Frustum[0][2] ));
    m_Frustum[0][0] /= t;
    m_Frustum[0][1] /= t;
    m_Frustum[0][2] /= t;
    m_Frustum[0][3] /= t;

    // Extract The Numbers For The LEFT Plane
    m_Frustum[1][0] = clip[ 3] + clip[ 0];
    m_Frustum[1][1] = clip[ 7] + clip[ 4];
    m_Frustum[1][2] = clip[11] + clip[ 8];
    m_Frustum[1][3] = clip[15] + clip[12];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[1][0] * m_Frustum[1][0] + m_Frustum[1][1] * m_Frustum[1][1] + m_Frustum[1][2] * m_Frustum[1][2] ));
    m_Frustum[1][0] /= t;
    m_Frustum[1][1] /= t;
    m_Frustum[1][2] /= t;
    m_Frustum[1][3] /= t;

    // Extract The BOTTOM Plane
    m_Frustum[2][0] = clip[ 3] + clip[ 1];
    m_Frustum[2][1] = clip[ 7] + clip[ 5];
    m_Frustum[2][2] = clip[11] + clip[ 9];
    m_Frustum[2][3] = clip[15] + clip[13];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[2][0] * m_Frustum[2][0] + m_Frustum[2][1] * m_Frustum[2][1] + m_Frustum[2][2] * m_Frustum[2][2] ));
    m_Frustum[2][0] /= t;
    m_Frustum[2][1] /= t;
    m_Frustum[2][2] /= t;
    m_Frustum[2][3] /= t;

    // Extract The TOP Plane
    m_Frustum[3][0] = clip[ 3] - clip[ 1];
    m_Frustum[3][1] = clip[ 7] - clip[ 5];
    m_Frustum[3][2] = clip[11] - clip[ 9];
    m_Frustum[3][3] = clip[15] - clip[13];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[3][0] * m_Frustum[3][0] + m_Frustum[3][1] * m_Frustum[3][1] + m_Frustum[3][2] * m_Frustum[3][2] ));
    m_Frustum[3][0] /= t;
    m_Frustum[3][1] /= t;
    m_Frustum[3][2] /= t;
    m_Frustum[3][3] /= t;

    // Extract The FAR Plane
    m_Frustum[4][0] = clip[ 3] - clip[ 2];
    m_Frustum[4][1] = clip[ 7] - clip[ 6];
    m_Frustum[4][2] = clip[11] - clip[10];
    m_Frustum[4][3] = clip[15] - clip[14];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[4][0] * m_Frustum[4][0] + m_Frustum[4][1] * m_Frustum[4][1] + m_Frustum[4][2] * m_Frustum[4][2] ));
    m_Frustum[4][0] /= t;
    m_Frustum[4][1] /= t;
    m_Frustum[4][2] /= t;
    m_Frustum[4][3] /= t;

    // Extract The NEAR Plane
    m_Frustum[5][0] = clip[ 3] + clip[ 2];
    m_Frustum[5][1] = clip[ 7] + clip[ 6];
    m_Frustum[5][2] = clip[11] + clip[10];
    m_Frustum[5][3] = clip[15] + clip[14];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[5][0] * m_Frustum[5][0] + m_Frustum[5][1] * m_Frustum[5][1] + m_Frustum[5][2] * m_Frustum[5][2] ));
    m_Frustum[5][0] /= t;
    m_Frustum[5][1] /= t;
    m_Frustum[5][2] /= t;
    m_Frustum[5][3] /= t;
    }


       
    This function is a beast! I'm sure you can see now why there are extensions that do this sort of thing! Although the math is really straightforward the shear length is what makes it nasty. In the above function there are 190 operations (multiplication's, additions, subtractions, divisions) plus 6 square roots. Since we will need to call this function every time we draw the scene it will be worth the effort to optimize it. Below is an optimized version of this function that has a couple of trade off's. As long as we do not rotate or translate the projection matrix the below function will work for getting the clipping planes for the viewing volume / frustum.   
       

    void glCamera::UpdateFrustumFaster(){ GLfloat   clip[16]; GLfloat   proj[16]; GLfloat   modl[16]; GLfloat   t;
    // Get The Current PROJECTION Matrix From OpenGL
    glGetFloatv( GL_PROJECTION_MATRIX, proj );

    // Get The Current MODELVIEW Matrix From OpenGL
    glGetFloatv( GL_MODELVIEW_MATRIX, modl );

    // Combine The Two Matrices (Multiply Projection By Modelview)
    // But Keep In Mind This Function Will Only Work If You Do NOT
    // Rotate Or Translate Your Projection Matrix
    clip[ 0] = modl[ 0] * proj[ 0];
    clip[ 1] = modl[ 1] * proj[ 5];
    clip[ 2] = modl[ 2] * proj[10] + modl[ 3] * proj[14];
    clip[ 3] = modl[ 2] * proj[11];

    clip[ 4] = modl[ 4] * proj[ 0];
    clip[ 5] = modl[ 5] * proj[ 5];
    clip[ 6] = modl[ 6] * proj[10] + modl[ 7] * proj[14];
    clip[ 7] = modl[ 6] * proj[11];

    clip[ 8] = modl[ 8] * proj[ 0];
    clip[ 9] = modl[ 9] * proj[ 5];
    clip[10] = modl[10] * proj[10] + modl[11] * proj[14];
    clip[11] = modl[10] * proj[11];

    clip[12] = modl[12] * proj[ 0];
    clip[13] = modl[13] * proj[ 5];
    clip[14] = modl[14] * proj[10] + modl[15] * proj[14];
    clip[15] = modl[14] * proj[11];

    // Extract The Numbers For The RIGHT Plane
    m_Frustum[0][0] = clip[ 3] - clip[ 0];
    m_Frustum[0][1] = clip[ 7] - clip[ 4];
    m_Frustum[0][2] = clip[11] - clip[ 8];
    m_Frustum[0][3] = clip[15] - clip[12];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[0][0] * m_Frustum[0][0] + m_Frustum[0][1] * m_Frustum[0][1] + m_Frustum[0][2] * m_Frustum[0][2] ));
    m_Frustum[0][0] /= t;
    m_Frustum[0][1] /= t;
    m_Frustum[0][2] /= t;
    m_Frustum[0][3] /= t;

    // Extract The Numbers For The LEFT Plane
    m_Frustum[1][0] = clip[ 3] + clip[ 0];
    m_Frustum[1][1] = clip[ 7] + clip[ 4];
    m_Frustum[1][2] = clip[11] + clip[ 8];
    m_Frustum[1][3] = clip[15] + clip[12];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[1][0] * m_Frustum[1][0] + m_Frustum[1][1] * m_Frustum[1][1] + m_Frustum[1][2] * m_Frustum[1][2] ));
    m_Frustum[1][0] /= t;
    m_Frustum[1][1] /= t;
    m_Frustum[1][2] /= t;
    m_Frustum[1][3] /= t;

    // Extract The BOTTOM Plane
    m_Frustum[2][0] = clip[ 3] + clip[ 1];
    m_Frustum[2][1] = clip[ 7] + clip[ 5];
    m_Frustum[2][2] = clip[11] + clip[ 9];
    m_Frustum[2][3] = clip[15] + clip[13];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[2][0] * m_Frustum[2][0] + m_Frustum[2][1] * m_Frustum[2][1] + m_Frustum[2][2] * m_Frustum[2][2] ));
    m_Frustum[2][0] /= t;
    m_Frustum[2][1] /= t;
    m_Frustum[2][2] /= t;
    m_Frustum[2][3] /= t;

    // Extract The TOP Plane
    m_Frustum[3][0] = clip[ 3] - clip[ 1];
    m_Frustum[3][1] = clip[ 7] - clip[ 5];
    m_Frustum[3][2] = clip[11] - clip[ 9];
    m_Frustum[3][3] = clip[15] - clip[13];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[3][0] * m_Frustum[3][0] + m_Frustum[3][1] * m_Frustum[3][1] + m_Frustum[3][2] * m_Frustum[3][2] ));
    m_Frustum[3][0] /= t;
    m_Frustum[3][1] /= t;
    m_Frustum[3][2] /= t;
    m_Frustum[3][3] /= t;

    // Extract The FAR Plane
    m_Frustum[4][0] = clip[ 3] - clip[ 2];
    m_Frustum[4][1] = clip[ 7] - clip[ 6];
    m_Frustum[4][2] = clip[11] - clip[10];
    m_Frustum[4][3] = clip[15] - clip[14];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[4][0] * m_Frustum[4][0] + m_Frustum[4][1] * m_Frustum[4][1] + m_Frustum[4][2] * m_Frustum[4][2] ));
    m_Frustum[4][0] /= t;
    m_Frustum[4][1] /= t;
    m_Frustum[4][2] /= t;
    m_Frustum[4][3] /= t;

    // Extract The NEAR Plane
    m_Frustum[5][0] = clip[ 3] + clip[ 2];
    m_Frustum[5][1] = clip[ 7] + clip[ 6];
    m_Frustum[5][2] = clip[11] + clip[10];
    m_Frustum[5][3] = clip[15] + clip[14];

    // Normalize The Result
    t = GLfloat(sqrt( m_Frustum[5][0] * m_Frustum[5][0] + m_Frustum[5][1] * m_Frustum[5][1] + m_Frustum[5][2] * m_Frustum[5][2] ));
    m_Frustum[5][0] /= t;
    m_Frustum[5][1] /= t;
    m_Frustum[5][2] /= t;
    m_Frustum[5][3] /= t;
    }


       
    It's still a beast but this function has roughly half as many operations as the first function (102). The optimization is a simple one. I just took out all the multiplication's that are usually zeroed when combining the projection and modelview matrices. If you really want to optimize this function then use an extension in its place. The extension will do the same thing but will do it much faster because the calculation will more than likely take place in the video hardware. Anyway calling either of the UpdateFrustum functions every time we draw the scene is going to give us a performance hit but we will gain one nice advantage from it. We can now tell if the camera can see an object or point. If you have several different objects in your scene it will be to your benefit to only draw the ones that are currently in the viewing volume. This is useful when you have a lot of terrain to draw so you don't bog OpenGL down by sending it every single vertex. Below is a function that checks to see if a point is in the viewing volume. There is also a SphereInFrustum function in the class but I will not list it here because the two functions are almost identical.  
       

    BOOL glCamera::PointInFrustum(glPoint p){ int i; // The Idea Behind This Algorithum Is That If The Point // Is Inside All 6 Clipping Planes Then It Is Inside Our // Viewing Volume So We Can Return True. for(i = 0; i < 6; i++) {  if(m_Frustum[i][0] * p.x + m_Frustum[i][1] * p.y + m_Frustum[i][2] * p.z + m_Frustum[i][3] <= 0)  {   return(FALSE);  } } return(TRUE);}

       
    Now we will ask OGL to project some geometry for us using the gluProject function. Practically we ask OGL to guess where a point in space will be projected in our current viewport, using arbitrary viewport and transform matrices we pass to the function. If we pass to the function the current matrices (retrievede with the glGet funcs) we will have the real position on screen where the dot will be drawn. The interesting part is that we also get a Z value back, this means that reading the REAL buffer for Z values we can discover if the flare is in front or if it's occluded by some objects.  
       

    // ########## New Stuff by rIO.Spinning Kids ##########bool glCamera::IsOccluded(glPoint p){ GLint viewport[4];       // Space For Viewport Data GLdouble mvmatrix[16], projmatrix[16];     // Space For Transform Matrix GLdouble winx, winy, winz;      // Space For Returned Projected Coords GLdouble flareZ;       // Here We Will Store The Transformed Flare Z GLfloat bufferZ;       // Here We Will Store The Read Z From The Buffer
    glGetIntegerv (GL_VIEWPORT, viewport); // Get Actual Viewport
    glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix); // Get Actual Model View Matrix
    glGetDoublev (GL_PROJECTION_MATRIX, projmatrix); // Get Actual Projection Matrix

    // This Asks OGL To Guess The 2D Position Of A 3D Point Inside The Viewport
    gluProject(p.x, p.y, p.z, mvmatrix, projmatrix, viewport, &winx, &winy, &winz);
    flareZ = winz;

    // We Read Back One Pixel From The Depth Buffer (Exactly Where Our Flare Should Be Drawn)
    glReadPixels(winx, winy,1,1,GL_DEPTH_COMPONENT, GL_FLOAT, &bufferZ);

    // If The Buffer Z Is Lower Than Our Flare Guessed Z Then Don't Draw
    // This Means There Is Something In Front Of Our Flare
    if (bufferZ < flareZ)
    return true;
    else
    return false;
    }


       
    Now we need to address another problem that we are going to have. Since we are creating 3D lens flares if we draw our flares on texture mapped quads they may not always be facing the camera. This is bad because our flares can appear flat if we are looking at the light source from the side. Instead of using texture mapped quads we could use point sprites. Point sprites are nice because instead of sending OpenGL four points with texture coordinates you only have to send a single point and you don't have to specify the texture coordinates. Point sprites are great for particle engines and they are equally great for lens flares. Since we only have to keep up with a single point the only thing we have to do is find out where we need to draw the points and call the appropriate drawing code. The disadvantage of point sprites is they are currently only implemented as an extension (GL_NV_point_sprite). To keep the tutorial so everyone can run it I will again be avoiding extensions here. One way we can make sure all our flares are always facing the camera is to simply reverse the rotations we used when setting our perspective. This works well but will break down if the camera ever gets behind the light source. To avoid this we are going to say the camera will never be allowed to get behind the light source by continually moving the light source as we move the camera. This will give us an extra side effect of making the light source appear infinitely far away and also allowing the flares to adjust a little when moving in a straight line. Enough talk below is the code for getting the necessary vectors and points.   
       

    GLfloat Length = 0.0f;
    // Draw The Flare Only If The Light Source Is In Our Line Of Sight
    if(SphereInFrustum(m_LightSourcePos, 1.0f) == TRUE)
    {
    vLightSourceToCamera = m_Position - m_LightSourcePos; // Lets Compute The Vector That Points To
    // The Camera From The Light Source.

    Length = vLightSourceToCamera.Magnitude(); // Save The Length We Will Need It In A Minute

    ptIntersect = m_DirectionVector * Length; // Now Lets Find A Point Along The Cameras Direction
    // Vector That We Can Use As An Intersection Point
    // Lets Translate Down This Vector The Same Distance
    // That The Camera Is. Away From The Light Source.
    ptIntersect += m_Position;

    vLightSourceToIntersect = ptIntersect - m_LightSourcePos; // Lets Compute The Vector That Points To The Intersect
    // Point From The Light Source
    Length = vLightSourceToIntersect.Magnitude(); // Save The Length We Will Need It Later
    vLightSourceToIntersect.Normalize(); // Normalize The Vector So Its Unit Length


       
    First we need to find out the distance between the light source and the camera. Next we will need an intersection point along the cameras direction vector. The distance between the intersection point and the camera needs to be the same distance from the light source to camera. Now that we have the intersection point we can now find a vector to draw all the lens flares by. Below is a picture representing this.

    按此在新窗口浏览图片

    Now that we have a direction vector to draw the lens flares off of all that is left is to draw the halos and glows. Below is the code that draws the flares along the vector. We create a new point by moving x number of units down the vLightSourceToIntersect vector and then add that to the Light Sources Position.

      
       

      glEnable(GL_BLEND);  glBlendFunc(GL_SRC_ALPHA, GL_ONE);  glDisable(GL_DEPTH_TEST);  glEnable(GL_TEXTURE_2D);
    // ########## New Stuff by rIO.Spinning Kids ##########

    if (!IsOccluded(m_LightSourcePos)) // Check If The Center Of The Flare Is Occluded
    {
    // Render The Large Hazy Glow
    RenderBigGlow(0.60f, 0.60f, 0.8f, 1.0f, m_LightSourcePos, 16.0f);
    // Render The Streaks
    RenderStreaks(0.60f, 0.60f, 0.8f, 1.0f, m_LightSourcePos, 16.0f);
    // Render The Small Glow
    RenderGlow(0.8f, 0.8f, 1.0f, 0.5f, m_LightSourcePos, 3.5f);

    pt = vLightSourceToIntersect * (Length * 0.1f); // Lets Compute A Point That Is 20%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.9f, 0.6f, 0.4f, 0.5f, pt, 0.6f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 0.15f); // Lets Compute A Point That Is 30%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.8f, 0.5f, 0.6f, 0.5f, pt, 1.7f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.175f); // Lets Compute A Point That Is 35%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.9f, 0.2f, 0.1f, 0.5f, pt, 0.83f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.285f); // Lets Compute A Point That Is 57%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.7f, 0.7f, 0.4f, 0.5f, pt, 1.6f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.2755f); // Lets Compute A Point That Is 55.1%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.9f, 0.9f, 0.2f, 0.5f, pt, 0.8f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 0.4775f); // Lets Compute A Point That Is 95.5%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.93f, 0.82f, 0.73f, 0.5f, pt, 1.0f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 0.49f); // Lets Compute A Point That Is 98%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.7f, 0.6f, 0.5f, 0.5f, pt, 1.4f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.65f); // Lets Compute A Point That Is 130%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.7f, 0.8f, 0.3f, 0.5f, pt, 1.8f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 0.63f); // Lets Compute A Point That Is 126%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.4f, 0.3f, 0.2f, 0.5f, pt, 1.4f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 0.8f); // Lets Compute A Point That Is 160%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.7f, 0.5f, 0.5f, 0.5f, pt, 1.4f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.7825f); // Lets Compute A Point That Is 156.5%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.8f, 0.5f, 0.1f, 0.5f, pt, 0.6f); // Render The Small Glow

    pt = vLightSourceToIntersect * (Length * 1.0f); // Lets Compute A Point That Is 200%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderHalo(0.5f, 0.5f, 0.7f, 0.5f, pt, 1.7f); // Render The Halo

    pt = vLightSourceToIntersect * (Length * 0.975f); // Lets Compute A Point That Is 195%
    pt += m_LightSourcePos; // Away From The Light Source In The
    // Direction Of The Intersection Point

    RenderGlow(0.4f, 0.1f, 0.9f, 0.5f, pt, 2.0f); // Render The Small Glow
    }

    glDisable(GL_BLEND );
    glEnable(GL_DEPTH_TEST);
    glDisable(GL_TEXTURE_2D);


       
    Below are the RenderBigGlow, RenderStreaks, RenderGlow and RenderHalo functions. The functions are identical with the exception of the texture they are binding too.   
       

    void glCamera::RenderHalo(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale){ glPoint q[4];
    // Basically We Are Just Going To Make A 2D Box
    // From Four Points We Don't Need A Z Coord Because
    // We Are Rotating The Camera By The Inverse So The
    // Texture Mapped Quads Will Always Face Us.
    q[0].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[0].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[1].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[1].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.
    q[2].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[2].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[3].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[3].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.

    glPushMatrix(); // Save The Model View Matrix
    glTranslatef(p.x, p.y, p.z); // Translate To Our Point
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_HaloTexture); // Bind To The Big Glow Texture
    glColor4f(r, g, b, a); // Set The Color Since The Texture Is A Gray Scale
    glBegin(GL_TRIANGLE_STRIP); // Draw The Big Glow On A Triangle Strip
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix(); // Restore The Model View Matrix
    }

    void glCamera::RenderGlow(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    // Basically We Are Just Going To Make A 2D Box
    // From Four Points We Don't Need A Z Coord Because
    // We Are Rotating The Camera By The Inverse So The
    // Texture Mapped Quads Will Always Face Us.
    q[0].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[0].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[1].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[1].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.
    q[2].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[2].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[3].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[3].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.

    glPushMatrix(); // Save The Model View Matrix
    glTranslatef(p.x, p.y, p.z); // Translate To Our Point
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_GlowTexture); // Bind To The Big Glow Texture
    glColor4f(r, g, b, a); // Set The Color Since The Texture Is A Gray Scale
    glBegin(GL_TRIANGLE_STRIP); // Draw The Big Glow On A Triangle Strip
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix(); // Restore The Model View Matrix
    }

    void glCamera::RenderBigGlow(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    // Basically We Are Just Going To Make A 2D Box
    // From Four Points We Don't Need A Z Coord Because
    // We Are Rotating The Camera By The Inverse So The
    // Texture Mapped Quads Will Always Face Us.
    q[0].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[0].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[1].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[1].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.
    q[2].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[2].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[3].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[3].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.

    glPushMatrix(); // Save The Model View Matrix
    glTranslatef(p.x, p.y, p.z); // Translate To Our Point
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_BigGlowTexture); // Bind To The Big Glow Texture
    glColor4f(r, g, b, a); // Set The Color Since The Texture Is A Gray Scale
    glBegin(GL_TRIANGLE_STRIP); // Draw The Big Glow On A Triangle Strip
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix(); // Restore The Model View Matrix
    }

    void glCamera::RenderStreaks(GLfloat r, GLfloat g, GLfloat b, GLfloat a, glPoint p, GLfloat scale)
    {
    glPoint q[4];

    // Basically We Are Just Going To Make A 2D Box
    // From Four Points We Don't Need A Z Coord Because
    // We Are Rotating The Camera By The Inverse So The
    // Texture Mapped Quads Will Always Face Us.
    q[0].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[0].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[1].x = (p.x - scale); // Set The x Coordinate -scale Units From The Center Point.
    q[1].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.
    q[2].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[2].y = (p.y - scale); // Set The y Coordinate -scale Units From The Center Point.
    q[3].x = (p.x + scale); // Set The x Coordinate scale Units From The Center Point.
    q[3].y = (p.y + scale); // Set The y Coordinate scale Units From The Center Point.

    glPushMatrix(); // Save The Model View Matrix
    glTranslatef(p.x, p.y, p.z); // Translate To Our Point
    glRotatef(-m_HeadingDegrees, 0.0f, 1.0f, 0.0f);
    glRotatef(-m_PitchDegrees, 1.0f, 0.0f, 0.0f);
    glBindTexture(GL_TEXTURE_2D, m_StreakTexture); // Bind To The Big Glow Texture
    glColor4f(r, g, b, a); // Set The Color Since The Texture Is A Gray Scale
    glBegin(GL_TRIANGLE_STRIP); // Draw The Big Glow On A Triangle Strip
    glTexCoord2f(0.0f, 0.0f);
    glVertex2f(q[0].x, q[0].y);
    glTexCoord2f(0.0f, 1.0f);
    glVertex2f(q[1].x, q[1].y);
    glTexCoord2f(1.0f, 0.0f);
    glVertex2f(q[2].x, q[2].y);
    glTexCoord2f(1.0f, 1.0f);
    glVertex2f(q[3].x, q[3].y);
    glEnd();
    glPopMatrix(); // Restore The Model View Matrix
    }


       
    You can use the 'W', 'S', 'A', and 'D' keys to change the direction the camera is pointed in. The '1' and '2' keys will toggle information on / off. 'Z' gives the camera a constant forward velocity. 'C' gives the camera a constant backward velocity and 'X' will stop the camera from moving at all.
    That is all for this tutorial. All questions, comments, and complaints are welcome. Just click on my name below to email me. Of course I'm not the first person to do lens flares and below are some links that I found helpful when writing this tutorial. Also before closing I need to send a thanks to Dave Steere, Cameron Tidwell, Bert Sammons, and Brannon Martindale for their feed back and helping me test out the code on different hardware. Thanks guys! Hope everyone enjoys the tutorial.

    Other Resources:
    http://www.gamedev.net/reference/articles/article874.asp
    http://www.gamedev.net/reference/articles/article813.asp
    http://www.opengl.org/developers/code/mjktips/lensflare/
    http://www.markmorley.com/opengl/frustumculling.html
    http://oss.sgi.com/projects/ogl-sample/registry/HP/occlusion_test.txt
    http://oss.sgi.com/projects/ogl-sample/registry/NV/occlusion_query.txt

    - Cheers

    Vic Hollis

    NOTES from Dario Corno a.k.a. rIO of Spinning Kids:

    I've added some test to check for occluders objects in front of the lens flare. This way the flare will be switched off when an object is in front of it.

    The new code should be well commented, and it is all marked with the # NEW STUFF # string, so if you want to check it out just do a search for "NEW STUFF" in the program.

    The modifications are:
    A new function in glCamera class, called IsOccluded, returning a boolean if the parameter point is behind an object
    Some new variables to hold the gluCylinder (used as occluder)
    Some new drawing code in glDraw to draw the occluder object
    Some new unloading code to release the quadric for the cylinder
    That's all, hope you find the modified version interesting!

    P.S: Left as an home exercise... Would be good to test more than one point near the flare position, to make it fade instead of just disappearing.

    * DOWNLOAD Visual C++ Code For This Lesson.

    * DOWNLOAD Borland C++ Builder 6 Code For This Lesson. ( Conversion by Le Thanh Cong )
    * DOWNLOAD Dev C++ Code For This Lesson. ( Conversion by Michael Small )
    * DOWNLOAD Code Warrior 5.3 Code For This Lesson. ( Conversion by Scott Lupton )
    * DOWNLOAD Visual Studio .NET Code For This Lesson. ( Conversion by Joachim Rohde )


    * DOWNLOAD Lesson 44 - With Extension Support (VC++).

    ----------------------------------------------
    越学越无知

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/31 21:48:00
     
     长风万里 美女呀,离线,快来找我吧!
      
      
      等级:大一新生
      文章:3
      积分:77
      门派:W3CHINA.ORG
      注册:2007/10/28

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给长风万里发送一个短消息 把长风万里加入好友 查看长风万里的个人资料 搜索长风万里在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看长风万里的博客5
    发贴心情 
    非常感谢~~~
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/11/1 0:42:00
     
     lanway 帅哥哟,离线,有人找我吗?
      
      
      等级:大一新生
      文章:0
      积分:55
      门派:XML.ORG.CN
      注册:2007/12/9

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给lanway发送一个短消息 把lanway加入好友 查看lanway的个人资料 搜索lanway在『 C/C++编程思想 』的所有贴子 引用回复这个贴子 回复这个贴子 查看lanway的博客6
    发贴心情 
    3Q
    辛苦了
    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/12/9 16:11:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/24 20:25:21

    本主题贴数6,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    234.375ms