青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

OpenGL Tutorial Fonts 翻譯

我知道每個人都或許厭惡字體。目前為止我寫的文字教程不僅能顯示文字,還能顯示3D文字,有紋理貼圖的文字,以及處理變量。但是當你將你的作品移植到不支持位圖或是輪廓字體的機器上會發生什么事呢?

  由于Giuseppe D'Agata我們有了另一篇字體教程。你還會問什么?如果你記得在第一篇字體教程中我提到使用紋理在屏幕上繪制文字。通常當你使用紋理繪制文字時你會調用你最喜歡的圖像處理程序,選擇一種字體,然后輸入你想顯示的文字或段落。然后你保存位圖并把它作為紋理讀入到你的程序里。對一個需要很多文字或是文字在不停變化的程序來說這么做效率并不高。

  本教程只使用有一個紋理來顯示任意256個不同的字符。記住平均一個字符只有16個像素寬,大概16個像素高。如果你使用標準的256x256的紋理那么很明顯你可以放入交叉的16個文字(即一個X),且最多16行16列。如果你需要一個更詳細的解釋:紋理是256個像素寬,一個字符是16個像素寬,256除以16得16:)

  現在讓我們來創建一個2D紋理字體demo!這課的程序基于第一課的代碼。在程序的第一段,我們包括數學(math)和標準輸入輸出庫(stdio)。我們需要數學庫來使用正弦和余弦函數在屏幕上移動我們的文字,我們需要標準輸入輸出庫來保證在我們制作紋理前要使用的位圖實際存在。

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

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

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

我們將要加入一個變量base來指向我們的顯示列表。我們還加入texture[2]來保存我們將要創建的兩個紋理。Texture 1將是字體紋理,texture 2將是用來創建簡單3D物體的凹凸紋理。

我們加入用來執行循環的變量loop。最后我們加入用來繞屏幕移動文字和旋轉3D物體的cnt1和cnt2。

GLuint base; // Base Display List For The Font
GLuint texture[2]; // Storage For Our Font Texture
GLuint loop; // Generic Loop Variable

GLfloat cnt1; // 1st Counter Used To Move Text & For Coloring
GLfloat cnt2; // 2nd Counter Used To Move Text & For Coloring

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

接下來是讀取紋理代碼。這跟前面紋理影射教程中的一模一樣。

AUX_RGBImageRec *LoadBMP(char *Filename) // Loads A Bitmap Image
{
    FILE *File=NULL; // File Handle
    if (!Filename) // Make Sure A Filename Was Given
    {
        return NULL; // If Not Return NULL
    }
    File=fopen(Filename,"r"); // Check To See If The File Exists
    if (File) // Does The File Exist?
    {
        fclose(File); // Close The Handle
        return auxDIBImageLoad(Filename); // Load The Bitmap And Return A Pointer
    }
    return NULL; // If Load Failed Return NULL
}

下面的代碼同樣對之前教程的代碼改動很小。如果你不清楚下面每行的用途,回頭復習一下。
注意TextureImage[ ]將保存2個rgb圖像記錄。復查處理讀取或存儲紋理的紋理很重要。一個錯誤的數字可能導致內存溢出或崩潰!

int LoadGLTextures() // Load Bitmaps And Convert To Textures
{
    int Status=FALSE; // Status Indicator
    AUX_RGBImageRec *TextureImage[2]; // Create Storage Space For The Textures


下一行十分重要。如果你用別的數字替換2將發生嚴重問題。再查一次!這個數字應該與你在設置TextureImages[ ]時的數字相匹配。

我們將讀取的紋理是font.bmp 和bumps.bmp。第二個紋理可用任何你想用的紋理替換。我不是特別有創造性,所以我使的紋理可能有些單調。

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

    if ((TextureImage[0]=LoadBMP("Data/Font.bmp")) && // Load The Font Bitmap
        (TextureImage[1]=LoadBMP("Data/Bumps.bmp"))) // Load The Texture Bitmap
    {
        Status=TRUE; // Set The Status To TRUE


另一十分重要,要檢查兩遍的行。我無法開始告訴你我收到多少email問“為什么我只看到一個紋理,或為什么我的紋理是全白的!?!”通常問題都出在這行。如果你用1替換2,那么將只創建一個紋理,第二個紋理將顯示為全白。如果你用3替換2,你的程序可能崩潰!

你應該只調用glGenTextures()一次。調用glGenTextures()后你應該創建你的所有紋理。我曾見過有人在每創建一個紋理前都加上一行glGenTextures()。這通常導致新建的紋理覆蓋了你之前創建的。決定你需要創建多少個紋理是個好主意,調用glGenTextures()一次,然后創建所有的紋理。把glGenTextures()放進循環是不明智的,除非你有自己的理由。

        glGenTextures(2, &texture[0]); // Create Two Texture

        for (loop=0; loop<2; loop++) // Loop Through All The Textures
        {
            // Build All The Textures
            glBindTexture(GL_TEXTURE_2D, texture[loop]);
            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, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
        }
    }

下面的幾行代碼檢查我們讀取的位圖數據是否在內存里。如果是,釋放內存。注意我們還要檢查并釋放rgb圖像記錄。如果我們使用了3個不同的圖像來創建紋理,我們要檢查并釋放3個rgb圖像記錄。

    for (loop=0; loop<2; loop++)
    {
        if (TextureImage[loop]) // If Texture Exists
        {
            if (TextureImage[loop]->data) // If Texture Image Exists
            {
                free(TextureImage[loop]->data); // Free The Texture Image Memory
            }
            free(TextureImage[loop]); // Free The Image Structure
        }
    }
    return Status; // Return The Status
}

現在我們將創建字體。我將以同樣的細節來解釋這段代碼。這并沒那么復雜,但是有些數學要了解,我知道不是每個人都喜歡數學。

GLvoid BuildFont(GLvoid) // Build Our Font Display List
{


下面兩個變量將用來保存字體紋理中每個字的位置。cx將用來保存紋理中水平方向的位置,cy將用來保存紋理中豎直方向的位置。

    float cx; // Holds Our X Character Coord
    float cy; // Holds Our Y Character Coord

接著我們告訴OpenGL我們要建立256個顯示列表。變量base將指向第一個顯示列表的位置。第二個顯示列表將是base+1,第三個是base+2,以此類推。

下面的第二行代碼選擇我們的字體紋理(texture[0])。

    base=glGenLists(256); // Creating 256 Display Lists
    glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Font Texture

現在我們開始循環。循環間創建所有的256個字符,每個存在它自己的顯示列表里。


    for (loop=0; loop<256; loop++) // Loop Through All 256 Lists
    {

下面的第一行或許看上去讓人有點困惑。%符號表示loop除以16的余數。cx將我們通過字體紋理從左至右移動。你將注意到在后面的代碼中我們用1減去cy從而從上到下而不是從下到上移動我們。%符號很難解釋,但我將嘗試去解釋。
我們真正關心的是(loop%16)。/16只是將結果轉化為紋理坐標。所以如果loop等于16,cx將等于16/16的余數也就是0。但cy將等于16/16也就是1。所以我們將下移一個字符的高度,且我們將不往右移。如果loop等于17,cx將等于17/16也就是1.0625。余數0.625也等于1/16。意味著我們將右移一個字符。cy將仍是1因為我們只關心小數點左邊的數字。18/16將右移2個字符,但仍下移一個字符。如果loop是32,cx將再次等于0,因為32除以16沒有余數,但cy將等于2。因為小數點左邊的數字現在是2,將下移2個字符。這么講清楚嗎?

        cx=float(loop%16)/16.0f; // X Position Of Current Character
        cy=float(loop/16)/16.0f; // Y Position Of Current Character

Whew:)Ok。現在我們通過從字體紋理中依據cx和cy的值選擇一個單獨的字符創建了2D字體。在下面的行里我們給base的值加上loop,若不這么做,每個字都將建在第一個顯示列表里。我們當然不想要那樣的事發生,所以通過給base加上loop,我們創建的每個字都被存在下個可用的顯示列表里。

        glNewList(base+loop,GL_COMPILE); // Start Building A List

現在我們已選擇了我們要創建的顯示列表,我們創建字符。這是通過繪制四邊形,然后給他貼上字體紋理中的單個字符的紋理來完成的。

        glBegin(GL_QUADS); // Use A Quad For Each Character

cx和cy應該保存一個從0.0到1.0的非常小的浮點數。如果cx和cy同時為0,下面第一行的代碼將為:glTexCoord2f(0.0f,1-0.0f-0.0625f)。記得0.0625正是我們紋理的1/16,或者說是一個字符的寬/高。下面的紋理坐標將是我們紋理的左下角。

注意我們使用glVertex2i(x,y)而不是glVertex3f(x,y,z)。我們的字體是2D字體,所以我們不需要z值。因為我們使用的是正交投影,我們不需要移進屏幕。在一個正交投影平面繪圖你所需的是指定x和y坐標。因為我們的屏幕是以像素形式從0到639(寬)從0到479(高),我們既不需用浮點數也不用負數:)

我們設置正交投影屏幕的方式是,(0,0)將是屏幕的左下角,(640,480)是屏幕的右上角。x軸上0是屏幕的左邊界,639是右邊界。y軸上0時下便捷,479是上便捷。基本上我們避免了負坐標。對那些不在乎透視,更愿意同像素而不是單元打交道的人來說更方便:)
        glTexCoord2f(cx,1-cy-0.0625f); // Texture Coord (Bottom Left)
        glVertex2i(0,0); // Vertex Coord (Bottom Left)

下一個紋理坐標現在是上個紋理坐標右邊1/16(剛好一個字符寬)。所以這將是紋理的右下角。

        glTexCoord2f(cx+0.0625f,1-cy-0.0625f); // Texture Coord (Bottom Right)
        glVertex2i(16,0); // Vertex Coord (Bottom Right)

第三個紋理坐標在我們的字符的最右邊,但上移了紋理的1/16(剛好一個字符高)。這將是一個單獨字符的右上角。

        glTexCoord2f(cx+0.0625f,1-cy); // Texture Coord (Top Right)
        glVertex2i(16,16); // Vertex Coord (Top Right)

最后我們左移來設置字符左上角的最后一個紋理坐標。

        glTexCoord2f(cx,1-cy); // Texture Coord (Top Left)
        glVertex2i(0,16); // Vertex Coord (Top Left)
        glEnd(); // Done Building Our Quad (Character)

最終,我們右移了10個像素,置于紋理的右邊。如果我們不平移,文字將被繪制到各自的上面。由于我們的字體太窄,我們不想右移16個像素。如果那樣的話,每個字之間將有很大間隔。只移動10個像素去除了間隔。

        glTranslated(10,0,0); // Move To The Right Of The Character
        glEndList(); // Done Building The Display List
    } // Loop Until All 256 Are Built
}

下面這段代碼與我們在其它字體教程中用來在程序退出前釋放顯示列表的相同。所有自base開始的256個顯示列表都將被銷毀(這樣做很好!)。

GLvoid KillFont(GLvoid) // Delete The Font From Memory
{
    glDeleteLists(base,256); // Delete All 256 Display Lists
}
下一段代碼將完成繪圖。一切都幾乎是新的,所以我將盡可能詳細的解釋每一行。一個小提示:很多都可加入這段代碼,像是變量的支持,字體大小、間距的調整,和很多為恢復到我們決定打印前的狀況所做的檢查。

glPrint()有三個參數。第一個是屏幕上x軸上的位置(從左至右的位置),下一個是y軸上的位置(從上到下...0是底部,越往上越大)。然后是字符串(我們想打印的文字),最后是一個叫做set的變量。如果你看過Giuseppe D'Agata制作的位圖,你會注意到有兩個不同的字符集。第一個字符集是普通的,第二個是斜體的。如果set為0,第一個字符集被選中。若set為1則選擇第二個字符集。
GLvoid glPrint(GLint x, GLint y, char *string, int set) // Where The Printing Happens
{

我們要做的第一件事是確保set的值非0即1。如果set大于1,我們將使它等于1。

    if (set>1) // Is set Greater Than One?
    {
        set=1; // If So, Make Set Equal One
    }


現在我們選擇字體紋理。我們這么做是防止在我們決定往屏幕上輸出東西時選擇了不同的紋理。

    glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Font Texture

現在我們禁用深度測試。我這么做是因為混合的效果會更好。如果你不禁用深度測試,文字可能會被什么東西擋住,或得不到正確的混合效果。如果你不打算混合文字(那樣文字周圍的黑色區域就不會顯示)你可以啟用深度測試。

    glDisable(GL_DEPTH_TEST); // Disables Depth Testing

下面幾行十分重要!我們選擇投影矩陣。之后使用一個叫做glPushMatrix()的命令。glPushMatrix存儲當前矩陣(投影)。有些像計算器的存儲按鈕。

    glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
    glPushMatrix(); // Store The Projection Matrix

現在我們保存了投影矩陣,重置矩陣并設置正交投影屏幕。第一和第三個數字(0)表示屏幕的底邊和左邊。如果愿意我們可以將屏幕的左邊設為-640,但如果不需要我們為什么要設負數呢。第二和第四個數字表示屏幕的上邊和右邊。將這些值設為你當前使用的分辨率是明智的做法。我們不需要用到深度,所以我們將z值設為-1與1。

    glLoadIdentity(); // Reset The Projection Matrix
    glOrtho(0,640,0,480,-1,1); // Set Up An Ortho Screen

現在我們選擇模型視點矩陣,用glPushMatrix()保存當前設置。然后我們重置模型視點矩陣以便在正交投影視點下工作。

    glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
    glPushMatrix(); // Store The Modelview Matrix
    glLoadIdentity(); // Reset The Modelview Matrix

在保存了透視參數,設置了正交投影屏幕后,現在我們可以繪制文字了。我們從移動到繪制文字的位置開始。我們使用 glTranslated()而不是glTranslatef()因為我們處理的是像素,所以浮點值并不重要。畢竟,你不可能用半個像素:)

    glTranslated(x,y,0); // Position The Text (0,0 - Bottom Left)

下面這行選擇我們要使用的字符集。如果我們想使用第二個字符集,我們在當前的顯示列表基數上加上128(128時我們256個字符的一半)。通過加上128,我們跳過了頭128個字符。

    glListBase(base-32+(128*set)); // Choose The Font Set (0 or 1)

現在剩下的就是在屏幕上繪制文字了。我們同其它字體教程一樣來完成這步。我們使用glCallLists()。strlen(string)是字符串的長度(我們想繪制多少字符),GL_UNSIGNED_BYTE意味著每個字符被表示為一個無符號字節(一個字節是一個從0到255的值)。最后,字符串保存我們想打印的文字。

    glCallLists(strlen(string),GL_UNSIGNED_BYTE,string); // Write The Text To The Screen

現在我們所要做的是恢復透視視圖。我們選擇投影矩陣并用glPopMatrix()恢復我們先前用glPushMatrix()保存的設置。用相反的順序恢復設置很重要。

    glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
    glPopMatrix(); // Restore The Old Projection Matrix

現在我們選擇模型視點矩陣,做相同的工作。我們使用glPopMatrix()恢復模型視點矩陣到我們設置正交投影顯示之前。

    glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
    glPopMatrix(); // Restore The Old Projection Matrix

最后,我們啟用深度測試。如果你沒有在上面的代碼中關閉深度測試,你不需要這行。

    glEnable(GL_DEPTH_TEST); // Enables Depth Testing
}

我們沒有修改ReSizeGLScene(),所以我們直接跳到InitGL()。

int InitGL(GLvoid) // All Setup For OpenGL Goes Here
{

我們跳到創建紋理的代碼。如果由于某種原因創建紋理失敗了,我們返回FALSE。這將讓我們的程序知道發生了一個錯誤從而關閉程序。

    if (!LoadGLTextures()) // Jump To Texture Loading Routine
    {
        return FALSE; // If Texture Didn't Load Return FALSE
    }

如果沒有錯,我們跳到創建字體的代碼。在創建字體時不會出什么錯所以我們省略了錯誤檢查。

    BuildFont(); // Build The Font

現在我們做通常的GL設置。我們將背景色設為黑色,將深度清為1.0。我們選擇一個深度測試模式和一個混合模式。我們啟用平滑著色,最后啟用2維紋理映射。

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // Clear The Background Color To Black
    glClearDepth(1.0); // Enables Clearing Of The Depth Buffer
    glDepthFunc(GL_LEQUAL); // The Type Of Depth Test To Do
    glBlendFunc(GL_SRC_ALPHA,GL_ONE); // Select The Type Of Blending
    glShadeModel(GL_SMOOTH); // Enables Smooth Color Shading
    glEnable(GL_TEXTURE_2D); // Enable 2D Texture Mapping
    return TRUE; // Initialization Went OK
}

下面這段代碼將完成繪圖。我們先繪制3D物體最后繪制文字,這樣文字將顯示在3D物體上面,而不會被3D物體遮住。我之所以加入一個3D物體是為了演示透視投影和正交投影可同時使用。

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

我們選擇bumps.bmp紋理來創建簡單的小3D物體。為了看見3D物體,我們往屏幕內移動5個單位。我們繞z軸旋轉45度。這將使我們的四邊形順時針旋轉45度,讓我們的四邊形看起來更像鉆石而不是矩形。

    glBindTexture(GL_TEXTURE_2D, texture[1]); // Select Our Second Texture
    glTranslatef(0.0f,0.0f,-5.0f); // Move Into The Screen 5 Units
    glRotatef(45.0f,0.0f,0.0f,1.0f); // Rotate On The Z Axis 45 Degrees (Clockwise)

在旋轉45度后,我們讓物體同時繞x軸和y軸旋轉cnt1x30度。這使我們的物體象在一個點上旋轉的鉆石那樣旋轉。

    glRotatef(cnt1*30.0f,1.0f,1.0f,0.0f); // Rotate On The X & Y Axis By cnt1 (Left To Right)

我們關閉混合(我們希望3D物體看上去像實心的),設置顏色為亮白色。然后我們繪制一個單獨的用了紋理映像的四邊形。

    glDisable(GL_BLEND); // Disable Blending Before We Draw In 3D
    glColor3f(1.0f,1.0f,1.0f); // Bright White
    glBegin(GL_QUADS); // Draw Our First Texture Mapped Quad
    glTexCoord2d(0.0f,0.0f); // First Texture Coord
    glVertex2f(-1.0f, 1.0f); // First Vertex
    glTexCoord2d(1.0f,0.0f); // Second Texture Coord
    glVertex2f( 1.0f, 1.0f); // Second Vertex
    glTexCoord2d(1.0f,1.0f); // Third Texture Coord
    glVertex2f( 1.0f,-1.0f); // Third Vertex
    glTexCoord2d(0.0f,1.0f); // Fourth Texture Coord
    glVertex2f(-1.0f,-1.0f); // Fourth Vertex
    glEnd(); // Done Drawing The First Quad

在畫完第一個四邊形后,我們立即同時繞x軸和y軸旋轉90度。然后我們畫下一個四邊形,。第二個四邊形從第一個四邊形的中間切過去,來形成一個好看的形狀。

    glRotatef(90.0f,1.0f,1.0f,0.0f); // Rotate On The X & Y Axis By 90 Degrees (Left To Right)
    glBegin(GL_QUADS); // Draw Our Second Texture Mapped Quad
    glTexCoord2d(0.0f,0.0f); // First Texture Coord
    glVertex2f(-1.0f, 1.0f); // First Vertex
    glTexCoord2d(1.0f,0.0f); // Second Texture Coord
    glVertex2f( 1.0f, 1.0f); // Second Vertex
    glTexCoord2d(1.0f,1.0f); // Third Texture Coord
    glVertex2f( 1.0f,-1.0f); // Third Vertex
    glTexCoord2d(0.0f,1.0f); // Fourth Texture Coord
    glVertex2f(-1.0f,-1.0f); // Fourth Vertex
    glEnd(); // Done Drawing Our Second Quad

在繪制完有紋理貼圖的四邊形后,我們開啟混合并繪制文字。

    glEnable(GL_BLEND); // Enable Blending
    glLoadIdentity(); // Reset The View

我們使用同其它字體教程一樣的生成很棒的顏色的代碼。顏色會隨著文字的移動而逐漸改變。

    // Pulsing Colors Based On Text Position
    glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)));

我們來繪制文字。我們仍然使用glPrint()。第一個參數是x坐標,第二個是y坐標,第三個("NeHe")是要繪制的文字,最后一個是使用的字符集(0-普通,1-斜體)。

正如你猜的,我們使用SIN和COS連同計數器cnt1和cnt2來移動文字。如果你不清楚SIN和COS的作用,閱讀之前的教程。

    glPrint(int((280+250*cos(cnt1))),int(235+200*sin(cnt2)),"NeHe",0); // Print GL Text To The Screen

    glColor3f(1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)),1.0f*float(cos(cnt1)));
    glPrint(int((280+230*cos(cnt2))),int(235+200*sin(cnt1)),"OpenGL",1); // Print GL Text To The Screen

我們將屏幕底部作者名字的顏色設為深藍色和白色。然后用亮白色文字再次繪制他的名字。亮白色文字是有點偏藍色的文字。這創造出一種附有陰影的樣子。(如果混合沒打開則沒有這種效果)。

    glColor3f(0.0f,0.0f,1.0f); // Set Color To Blue
    glPrint(int(240+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0); // Draw Text To The Screen

    glColor3f(1.0f,1.0f,1.0f); // Set Color To White
    glPrint(int(242+200*cos((cnt2+cnt1)/5)),2,"Giuseppe D'Agata",0); // Draw Offset Text To The Screen

我們所做的最后一件事是以不同的速率遞增我們的計數器。這使得文字移動,3D物體自轉。

    cnt1+=0.01f; // Increase The First Counter
    cnt2+=0.0081f; // Increase The Second Counter
    return TRUE; // Everything Went OK
}

KillGLWindow(), CreateGLWindow()和WndProc()的代碼都沒有更改,所以我們跳過它們。

int WINAPI WinMain( HINSTANCE hInstance, // Instance
HINSTANCE hPrevInstance, // Previous Instance
LPSTR lpCmdLine, // Command Line Parameters
int nCmdShow) // Window Show State
{
    MSG msg; // Windows Message Structure
    BOOL done=FALSE; // Bool Variable To Exit Loop

    // Ask The User Which Screen Mode They Prefer
    if (MessageBox(NULL,"Would You Like To Run In Fullscreen Mode?", "Start FullScreen?",MB_YESNO|MB_ICONQUESTION)==IDNO)
    {
        fullscreen=FALSE; // Windowed Mode
    }

我們改變了窗口的標題。

    // Create Our OpenGL Window
    if (!CreateGLWindow("NeHe & Giuseppe D'Agata's 2D Font Tutorial",640,480,16,fullscreen))
    {
        return 0; // Quit If Window Was Not Created
    }

    while(!done) // Loop That Runs While done=FALSE
    {
        if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) // Is There A Message Waiting?
        {
            if (msg.message==WM_QUIT) // Have We Received A Quit Message?
            {
                done=TRUE; // If So done=TRUE
            }
            else // If Not, Deal With Window Messages
            {
                TranslateMessage(&msg); // Translate The Message
                DispatchMessage(&msg); // Dispatch The Message
            }
        }
        else // If There Are No Messages
        {
            // Draw The Scene. Watch For ESC Key And Quit Messages From DrawGLScene()
            if ((active && !DrawGLScene()) || keys[VK_ESCAPE]) // Active? Was There A Quit Received?
            {
                done=TRUE; // ESC or DrawGLScene Signalled A Quit
            }
            else // Not Time To Quit, Update Screen
            {
                SwapBuffers(hDC); // Swap Buffers (Double Buffering)
            }
        }
    }

    // Shutdown

如下所示,最后要做的是在KillGLWindow()的最后添加KillFont()。添加這行很重要,它在我們退出程序前將所有的清除干凈。

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

    KillFont(); // Destroy The Font
}

我認為現在我可以正式說我的網站已經把所有繪制文字的方法教給大家了{笑}。總之,我認為我的教程很不錯。這課的代碼可在任何能運行OpenGL的電腦上運行,它很容易使用,且這樣繪制文字對系統的資源消耗很少。

我要感謝這篇教程的原作者Giuseppe D'Agata。我做了大量的修改,并將它轉變為新式的代碼,但要是沒有他寄給我這份代碼我是不會完成這篇教程的。他的代碼有更多的選項,像是改變文字間距等等。但我用很cool的3D物體來彌補了{笑}。

我希望你們喜歡這篇教程。若有什么問題,給我或Giuseppe D'Agata發email。

posted on 2006-01-20 00:28 zmj 閱讀(868) 評論(0)  編輯 收藏 引用


只有注冊用戶登錄后才能發表評論。
網站導航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            亚洲已满18点击进入久久| 美日韩精品视频| 久久精品日产第一区二区三区| 99re6热在线精品视频播放速度| 国产精品一区二区在线观看| 欧美日韩一级片在线观看| 欧美日韩亚洲在线| 国产精品国产自产拍高清av王其| 国产精品成人一区| 国产日产高清欧美一区二区三区| 国产噜噜噜噜噜久久久久久久久 | 欧美精品日韩一本| 欧美日韩卡一卡二| 国产美女搞久久| 一区二区三区在线视频免费观看 | 亚洲视频福利| 夜夜嗨av一区二区三区网站四季av| 99ri日韩精品视频| 亚洲欧美久久久| 裸体一区二区三区| 亚洲图片欧洲图片av| 亚洲精品久久| 欧美黄色精品| 亚洲午夜一区| 麻豆成人综合网| 国产精品免费区二区三区观看| 韩国av一区二区| 亚洲午夜精品网| 在线观看视频一区| 亚洲欧美成人精品| 亚洲国产精品久久久久婷婷884| 一区二区三区四区国产精品| 久久久久久久久久久久久女国产乱| 免费亚洲电影| 午夜精品一区二区在线观看 | 亚洲大胆av| 亚洲视频欧洲视频| 久久久欧美精品sm网站| 国产精品国产三级国产专播品爱网 | 开心色5月久久精品| 国产精品第13页| 久久精彩视频| 欧美精品免费在线| 国产一区二区三区在线观看精品| 亚洲精品在线看| 久久久国产精品一区二区中文| 日韩视频精品| 欧美激情一区二区在线| 在线日韩中文字幕| 欧美一区二区国产| 亚洲少妇自拍| 欧美日韩三级电影在线| 亚洲国产一二三| 欧美a级一区| 久久久久久亚洲精品杨幂换脸| 国产伦一区二区三区色一情| 亚洲欧美日韩一区二区在线 | 国产精品欧美日韩一区| 亚洲一区二区三区高清| 亚洲日本无吗高清不卡| 欧美激情国产精品| 一本色道久久99精品综合| 亚洲国产精品va在线看黑人动漫 | 欧美日韩精品在线观看| 羞羞答答国产精品www一本| 99精品视频免费观看| 久久精品国产亚洲一区二区| 国产精品国产三级国产| 亚洲视频一区二区免费在线观看| 久久综合狠狠综合久久激情| 欧美一区二区三区日韩视频| 国产精品毛片a∨一区二区三区| 亚洲美女黄色| 99www免费人成精品| 欧美日韩国产精品一卡| 国产综合色精品一区二区三区| 久久精品色图| 久久久成人网| 日韩视频一区二区三区| 一区二区三区 在线观看视| 国产精品区免费视频| 欧美一区二区日韩| 久久久精品日韩欧美| 在线播放日韩欧美| 最新亚洲电影| 国产精品久久久久9999吃药| 亚洲欧美国产视频| 午夜精品久久| 在线观看成人av电影| 亚洲国产第一页| 欧美小视频在线| 久久精品一本| 欧美激情综合五月色丁香| 国产精品99久久不卡二区| 午夜精品免费| 亚洲精品欧洲精品| 亚洲欧美另类在线观看| 国内外成人在线视频| 亚洲国产高清在线观看视频| 欧美日韩在线三区| 美国十次了思思久久精品导航| 欧美日韩免费一区二区三区| 久久综合影音| 国产精品国产三级国产aⅴ入口| 久久久噜噜噜久久中文字免| 欧美日本在线播放| 久久免费国产精品1| 欧美性开放视频| 亚洲成色777777女色窝| 国产日韩欧美一区| 一本色道久久88综合亚洲精品ⅰ | 六月婷婷一区| 日韩网站在线| 欧美伊人久久大香线蕉综合69| 亚洲美女视频在线观看| 欧美一区二区国产| 亚洲一区视频在线观看视频| 蜜桃av综合| 玖玖综合伊人| 国产日韩欧美一二三区| 一区二区三区日韩精品视频| 美女精品国产| 国产亚洲精品福利| 在线亚洲精品| 亚洲另类视频| 亚洲久久在线| 国产日韩一区二区| 99国产精品久久久| 国产日韩1区| av不卡在线看| 久久天堂成人| 久久精品视频导航| 国产欧美日本一区二区三区| 夜夜狂射影院欧美极品| 99国产精品99久久久久久粉嫩| 久久久久久国产精品mv| 久久婷婷色综合| 国外成人免费视频| 久久久久成人精品| 欧美福利视频在线观看| 在线电影院国产精品| 看欧美日韩国产| 欧美成人官网二区| 最新国产成人av网站网址麻豆 | 亚洲国产乱码最新视频| 亚洲自拍偷拍一区| 久久久久久日产精品| 免费精品视频| 亚洲国产高清高潮精品美女| 久久嫩草精品久久久精品| 免费不卡中文字幕视频| 亚洲国产日韩在线| 欧美女激情福利| 制服丝袜亚洲播放| 欧美伊人久久久久久午夜久久久久 | 欧美性猛交视频| 亚洲一区二区在线播放| 久久精品99国产精品日本| 亚洲一区二区三区久久| 亚洲一区二区三区免费视频| 国产精品久久999| 亚洲欧美一区二区激情| 久久夜色精品国产欧美乱极品| 亚洲高清不卡av| 欧美日韩成人综合| 欧美一区二区私人影院日本| 蜜桃久久精品一区二区| 日韩视频免费观看高清在线视频| 欧美日韩综合视频网址| 欧美伊人影院| 亚洲免费观看高清完整版在线观看熊| 午夜精品国产更新| 亚洲风情亚aⅴ在线发布| 欧美性理论片在线观看片免费| 欧美在线网站| 亚洲美女淫视频| 欧美a级片一区| 亚洲欧美日韩国产精品 | aa亚洲婷婷| 久久综合中文色婷婷| 一区二区精品| 狠狠网亚洲精品| 欧美视频不卡| 欧美91精品| 午夜一区不卡| 日韩一级欧洲| 亚洲成色www久久网站| 午夜在线a亚洲v天堂网2018| 91久久香蕉国产日韩欧美9色| 国产日本欧美一区二区三区| 欧美日韩视频在线一区二区 | 欧美大香线蕉线伊人久久国产精品| 亚洲天天影视| 91久久午夜| 一区二区三区在线高清| 国产精品夜夜嗨| 欧美系列亚洲系列| 欧美区在线观看| 免费欧美日韩| 久久在线视频在线|