• <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>

            OpenGL OutLine Fonts 翻譯

            輪廓字體(Outline Fonts)

              這節(jié)課繼續(xù)上一節(jié)課課的內(nèi)容。在第13課我們學(xué)習(xí)了如何使用位圖字體,這節(jié)課,我們將學(xué)習(xí)如何使用輪廓字體。

              創(chuàng)建輪廓字體的方法類似于在第13課中我們創(chuàng)建位圖字體的方法。但是,輪廓字體看起來要酷100倍!你可以指定輪廓字體的大小。輪廓字體可以在屏幕中以3D方式運動,而且輪廓字體還可以有一定的厚度!而不是平面的2D字符。使用輪廓字體,你可以將你的計算機中的任何字體轉(zhuǎn)換為OpenGL中的3D字體,加上合適的法線,在有光照的時候,字符就會被很好的照亮了。

              一個小注釋,這段代碼是專門針對Windows寫的,它使用了Windows的wgl函數(shù)來創(chuàng)建字體,顯然,Apple機系統(tǒng)有agl,X系統(tǒng)有g(shù)lx來支持做同樣事情的,不幸的是,我不能保證這些代碼也是容易使用的。如果哪位有能在屏幕上顯示文字且獨立于平臺的代碼,請告訴我,我將重寫一個有關(guān)字體的教程。

              我們從第一課的典型代碼開始,添加上stdio.h頭文件以便進行標(biāo)準(zhǔn)輸入/輸出操作,另外,stdarg.h頭文件用來解析文字以及把變量轉(zhuǎn)換為文字。最后加上math.h頭文件,這樣我們就可以使用SIN和COS函數(shù)在屏幕中移動文字了。

            #include <windows.h> // Header File For Windows
            #include <math.h> // Header File For Windows Math Library
            #include <stdio.h> // Header File For Standard Input/Output
            #include <stdarg.h> // Header File For Variable Argument Routines
            #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


              另外,我們還要添加2個變量。base將保存我們創(chuàng)建的第一個顯示列表的編號。每個字符都需要有自己的顯示列表。例如,字符‘A’在顯示列表中是65,‘B’是66,‘C’是67,等等。所以,字符‘A’應(yīng)保存在顯示列表中的base + 65這個位置。

              我們再添加一個叫做rot的變量。用它配合SIN和COS函數(shù)在屏幕上旋轉(zhuǎn)文字。我們同時用它來改變文字的顏色。

            GLuint base; // Base Display List For The Font Set
            GLfloat rot; // Used To Rotate The Text

            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


              GLYPHMETRICSFLOAT gmf[256]用來保存256個輪廓字體顯示列表中對應(yīng)的每一個列表的位置和方向的信息。我們通過gmf[num]來選擇字母。num就是我們想要了解的顯示列表的編號。在稍后的代碼中,我將說明如何如何檢查每個字符的寬度,以便自動將文字定位在屏幕中心。切記,每個字符的寬度可以不相同。Glyphmetrics會大大簡化我們的工作。

            GLYPHMETRICSFLOAT gmf[256];// Storage For Information About Our Outline Font Characters

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


            下面這段用來構(gòu)建真正的字體的代碼類似于我們創(chuàng)建位圖字體的方法。和13課一樣,這段代碼是最難解釋的部分。

            ‘HFONT font’用來保存我們使用的Windows字體的ID。

            接下來我們定義base。我們使用glGenLists(256)創(chuàng)建一組共256個顯示列表。在顯示列表創(chuàng)建好以后,變量base將保存第一個顯示列表的編號。

            GLvoid BuildFont(GLvoid) // Build Our Bitmap Font
            {
                HFONT font; // Windows Font ID
                base = glGenLists(256); // Storage For 256 Characters


            還有更多有趣的部分,我們將創(chuàng)建屬于自己的輪廓字體。我們從指定字體的大小開始,你會注意到它是一個負數(shù),我們通過加上一個負號來告訴Windows尋找一個基于CHARACTER高度的字體。如果我們使用一個正數(shù),就是尋找一個與基于CELL的高度相匹配的字體。

                font = CreateFont( -12, // Height Of Font

            然后我們指定每個單元的寬度,你會注意到我把它定義為0,這樣,Windows就會使用默認值。如果你愿意的話,可以改變它的值,比如更寬一點,等等。

                0, // Width Of Font

            Angle Of Escapement會將字體旋轉(zhuǎn)。MSDN幫助中解釋Orientation Angle用于指定每個字的底邊和顯示設(shè)備的X軸之間的角度,每個單位是十分之一個角度,不幸的是我對這個沒有概念。

                0, // Angle Of Escapement
                0, // Orientation Angle


            字體重量是一個很重要的參數(shù),你可以設(shè)置一個0-1000之間的值或使用一個已定義的值。FW_DONTCARE是0, FW_NORMAL是400, FW_BOLD是700 and FW_BLACK是900。還有許多預(yù)先定義的值,但是這四個的效果比較好。值越大,字體就越粗。

                FW_BOLD, // Font Weight

            Italic(斜體),Underline(下劃線)和Strikeout(刪除線)可以是TRUE或FALSE。如果將Underline設(shè)置為TRUE,那么字體就會帶有下劃線,否則就沒有,非常簡單。

                FALSE, // Italic
                FALSE, // Underline
                FALSE, // Strikeout


            Character Set Identifier(字符集標(biāo)識符)用來描述你要使用的字符集(內(nèi)碼)類型。有太多需要說明的類型了。CHINESEBIG5_CHARSET,GREEK_CHARSET,RUSSIAN_CHARSET,DEFAULT_CHARSET ,等等。我使用的是ANSI,盡管DEFAULT也是很好用的。

            如果你有興趣使用Webdings或Wingdings等字體,你必須使用SYMBOL_CHARSET而不是ANSI_CHARSET。

                ANSI_CHARSET, // Character Set Identifier

            Output Precision(輸出精度)非常重要。它告訴Windows在有多種字符集的情況下使用哪類字符集。OUT_TT_PRECIS告訴Windows如果一個名字對應(yīng)多種不同的選擇字體,那么選擇字體的TRUETYPE類型。Truetype字體通常看起來要好些,尤其是你把它們放大的時候。你也可以使用OUT_TT_ONLY_PRECIS,它將會一直嘗試使用一種TRUETYPE類型的字體

                OUT_TT_PRECIS, // Output Precision

            裁剪精度是一種當(dāng)字體落在裁剪范圍之外時使用的剪輯類型,不用多說,只要把它設(shè)置為DEFAULT就可以了。

                CLIP_DEFAULT_PRECIS, // Clipping Precision

            輸出質(zhì)量非常重要。你可以使用PROOF,DRAFT,NONANTIALIASED,DEFAULT或ANTIALISED。
            我們都知道,ANTIALIASED字體看起來很好,將一種字體Antialiasing(反鋸齒)可以實現(xiàn)在Windows下打開字體平滑時同樣的效果,它使任何東西看起來都要少些鋸齒,也就是更平滑。

                ANTIALIASED_QUALITY, // Output Quality

            下面是Family和Pitch設(shè)置。Pitch屬性有DEFAULT_PITCH,F(xiàn)IXED_PITCH和VARIABLE_PITCH,F(xiàn)amily有FF_DECORATIVE,FF_MODERN,FF_ROMAN,FF_SCRIPT,FF_SWISS,FF_DONTCARE.嘗試一下這些值,你就會知道它們到底有什么功能。我把它們都設(shè)置為默認值。

                FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch

            最后,是我們要用的字體的確切的名字。打開Microsoft Word或其它什么文字處理軟件,點擊字體下拉菜單,找一個你喜歡的字體。將‘Comic Sans MS’替換為你想用的字體的名字,你就可以使用它了。(中文還不行,需要別的方法)

                "Comic Sans MS"); // Font Name

            現(xiàn)在,將剛才創(chuàng)建的字體選入我們的DC。

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

            }


            下面是新的代碼。我們使用一個新的命令wglUseFontOutlines來創(chuàng)建輪廓字體。我們選擇我們的DC,首字符,將要創(chuàng)建的字符的個數(shù)和‘base’顯示列表值。所有一切都與我們創(chuàng)建位圖字體的方法類似。

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


            但是,這還不是全部。接下來我們設(shè)置偏差等級。這個值越接近0.0f, 字體看起來就越平滑。設(shè)置完偏差值后,我們設(shè)置字體的厚度。它用來描述字體在Z軸上的厚度。0.0f會產(chǎn)生一個平面的2D字體,1.0f會產(chǎn)生一個有一定厚度的字體。

            參數(shù)WGL_FONT_POLYGONS告訴OpenGL使用多邊形來創(chuàng)建一個實心字體。如果我們使用WGL_FONT_LINES的話,字體就變成了輪廓線(由線組成),值得注意的是,如果使用GL_FONT_LINES,就不能設(shè)置法線,光照也就不能正常工作。

            最后一個參數(shù)gmf指向顯示列表數(shù)據(jù)的地址緩存。

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


            接下來的代碼很簡單。它在內(nèi)存中從base開始刪除256個顯示列表。我不知道Windows是否會做這些工作,但還是保險為好。

            GLvoid KillFont(GLvoid) // Delete The Font List
            {
                glDeleteLists(base, 256); // Delete All 256 Characters
            }

            下面就是我優(yōu)異的GL文字程序了。你可以通過調(diào)用glPrint(“需要寫的文字”)來調(diào)用這段代碼。與第13課中繪制位圖字體的方法完全相同,文字被存儲在字符串 * fmt中。

            GLvoid glPrint(const char *fmt, ...) // Custom GL "Print" Routine
            {

            下面的第一行定義了一個叫做length的變量。我們使用這個變量來查詢字符串的長度。第二行創(chuàng)建了一個大小為256個字符的字符數(shù)組,里面保存我們想要的文字串。第三行創(chuàng)建了一個指向一個變量列表的指針,我們在傳遞字符串的同時也傳遞了這個變量列表。如果我們傳遞文字時也傳遞了變量,這個指針將指向它們。

                float length = 0; // Used To Find The Length Of The Text
                char text[256]; // Holds Our String
                va_list ap; // Pointer To List Of Arguments


            下面兩行代碼檢查是否有需要顯示的內(nèi)容,如果什么也沒有,fmt就等于空(NULL),屏幕上也就什么都沒有。

                if (fmt == NULL) // If There's No Text
                    return; // Do Nothing


            接下來三行代碼將文字中的所有符號轉(zhuǎn)換為它們的字符編號。最后,文字和轉(zhuǎn)換的符號被存儲在一個叫做“text”的字符串中。以后我會多解釋一些有關(guān)字符的細節(jié)。

                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


            感謝Jim Williams對下面一段代碼的建議。以前我是用手工將文字置于中心的,而他的辦法要好的多。

              我們從一個循環(huán)開始,它將逐個檢查文本中的字符。我們通過strlen(text)得到文本的長度。設(shè)置好了循環(huán)以后,我們將通過加上每個字符的長度來增加length的值。當(dāng)循環(huán)結(jié)束以后,被保存在length中的值就是整個字符串的長度。所以,如果我們要寫的是“hello”,假設(shè)每個字符的長度都為10個單位,我們先給length的值加上第一個字母的長度10。然后,我們檢查第二個字母的長度,它的長度也是10,所以length就變成10 + 10(20)。當(dāng)我們檢查完所有5個字母以后,length的值就會等于50(5 *10)。

              給出我們每個字符的長度的代碼是gmf[text[loop]].gmfCellIncX。記住,gmf存儲了我們每個顯示列表的信息。如果loop等于0,text[loop]就是我們的字符串中的第一個字符。如果loop等于1,text[loop]就是我們的字符串中的第二個字符。gmfCellIncX告訴我們被選擇的字符的長度。GmfCellIncX表示顯示位置從已繪制上的上一個字符向右移動的真正距離,這樣,字符之間就不會重疊在一起。同時,這個距離就是我們想得到的字符的寬度。你還可以通過gmfCelllncY命令來得到字符的高度。如果你是在垂直方向繪制文本而不是在水平方向時,這會很方便。

            for (unsigned int loop=0;loop<(strlen(text));loop++)// Loop To Find Text Length
            {
                length+=gmf[text[loop]].gmfCellIncX;//IncreaseLength By Each Characters Width
            }

            最后我們?nèi)〕鲇嬎愫蟮玫降膌ength,并把它變成負數(shù)(因為我們要將文本從屏幕中心左移從而把整個文本置于屏幕中間)。然后我們把length除以2。我們并不想移動整個文本的長度,只需要一半!

                glTranslatef(-length/2,0.0f,0.0f); // Center Our Text On The Screen

            然后我們將GL_LIST_BIT壓入屬性堆棧,它會防止glListBase影響到我們的程序中的其它顯示列表。

                glListBase(base)函數(shù)用來告訴OpenGL到哪里去找每個字符對應(yīng)的顯示列表。

                glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits
                glListBase(base); // Sets The Base Character to 32


            現(xiàn)在OpenGL知道字符的存放位置了,我們就可以讓它在屏幕上顯示文字了。GlCallLists會調(diào)用多個顯示列表從而把整個文字的內(nèi)容同時顯示在屏幕上。

              下面的代碼做后續(xù)工作。首先,它告訴OpenGL我們將要在屏幕上顯示出顯示列表中的內(nèi)容。Strlen(text)函數(shù)用來計算我們將要顯示在屏幕上的文字的長度。然后,OpenGL需要知道我們允許發(fā)送給它的列表的最大值。我們依然不能發(fā)送長度大于255的字符串。所以我們使用UNSIGNED_BYTE。(用0 - 255來表示我們需要的字符)。最后,我們通過傳遞字符串文字告訴OpenGL顯示什么內(nèi)容。

              也許你想知道為什么字符不會彼此重疊堆積在一起。那時因為每個字符的顯示列表都知道字符的右邊緣在那里,在寫完一個字符后,OpenGL自動移動到剛寫過的字符的右邊,在寫下一個字或畫下一個物體時就會從GL移動到的最后的位置開始,也就是最后一個字符的右邊。

              最后,我們將GL_LIST_BIT屬性彈出堆棧,將GL恢復(fù)到我們使用glListBase(base)設(shè)置base之前的狀態(tài)。

                glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);//DrawsThe Display ListText
                glPopAttrib(); // Pops The Display List Bits
            }


            重置尺寸的代碼與第一課中的代碼完全相同,所以在這里省略它。

              在InitGL代碼的最后有幾行新的代碼。第13課中的BuildFont()還在,這些新的代碼用來制造快速并且不很鮮明的光線。Light0是大多數(shù)顯卡中預(yù)先定義過的,它可以很好的照亮場景而且不影響我的代碼。

              我還添加了glEnable(GL_Color_Material)命令.因為字是3D對象,因此需要打開材質(zhì)色彩,否則使用glColor3f(r,g,b)改變顏色時就不能改變文字的顏色。如果你要在屏幕上繪制文本,那么再此之前先打開材質(zhì)色彩,寫完之后,在將它關(guān)閉,不然屏幕中的所有物體都會被上色。

            int InitGL(GLvoid) // All Setup For OpenGL Goes Here
            {
                glShadeModel(GL_SMOOTH); // Enable Smooth Shading
                glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
                glClearDepth(1.0f); // Depth Buffer Setup
                glEnable(GL_DEPTH_TEST); // Enables Depth Testing
                glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
                glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//Really Nice Perspective Calculations
                glEnable(GL_LIGHT0) // Enable Default Light (Quick And Dirty)
                glEnable(GL_LIGHTING); // Enable Lighting
                glEnable(GL_COLOR_MATERIAL); // Enable Coloring Of Material

                BuildFont(); // Build The Font

                return TRUE; // Initialization Went OK
            }


            下面就是畫圖的代碼了。我們從清除屏幕和深度緩存開始。我們調(diào)用glLoadIdentity()來重置所有東西。然后我們將坐標(biāo)系向屏幕里移動十個單位。輪廓字體在透視圖模式下表現(xiàn)非常好。你將文字移入屏幕越深,文字開起來就更小。文字離你越近,它看起來就更大。

            也可以使用glScalef(x,y,z)命令來操作輪廓字體。如果你想把字體放大兩倍,可以使用glScalef(1.0f,2.0f,1.0f). 2.0f 作用在y軸, 它告訴OpenGL將顯示列表的高度繪制為原來的兩倍。如果2.0f作用在x軸,那么文本的寬度將變成原來的兩倍。

            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,-10.0f); // Move One Unit Into The Screen

            在向屏幕里移動以后,我們希望文本能旋轉(zhuǎn)起來。下面3行代碼用來在3個軸上旋轉(zhuǎn)屏幕。我將rot乘以不同的數(shù),以便每個方向上的旋轉(zhuǎn)速度不同。

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

            下面是令人興奮的顏色循環(huán)了。照常,我們使用唯一遞增的變量(rot)。顏色通過使用COS和SIN來循環(huán)變化。我將rot除以不同的數(shù),這樣每種顏色會以不同的速度遞增。最終的效果非常好。

                // Pulsing Colors Based On The Rotation
                glColor3f(1.0f*float(cos(rot/20.0f)),1.0f*float(sin(rot/25.0f)),1.0f-0.5f * float(cos(rot/17.0f)));


            我最喜歡的部分,將文字寫到屏幕上。我使用同將位圖字體寫到屏幕上相同的函數(shù)。將文字寫在屏幕上,所有你要做的就是glPrint(“你想寫的文字”)。很簡單。

            在下面的代碼中,我們要寫的是NeHe,空格,破折號,空格,然后是rot的值除以50后的結(jié)果(為了減慢計數(shù)器)。如果這個數(shù)大于999.99,左邊第四個數(shù)將被去掉(我們要求只顯示小數(shù)點左邊3位數(shù)字)。只顯示小數(shù)點右邊的兩位數(shù)字。

                glPrint("NeHe - %3.2f",rot/50); // Print GL Text To The Screen

            然后增大旋轉(zhuǎn)變量從而改變顏色并旋轉(zhuǎn)文字。

                rot+=0.5f; // Increase The Rotation Variable
                return TRUE; // Everything Went OK
            }

            最后,如下所示,就是在KillGLWindow()函數(shù)最后增加KillFont()函數(shù),這很重要,它在我們退出程序之前做清理工作。

            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


              在這節(jié)課結(jié)束的時候,你應(yīng)該已經(jīng)學(xué)會在你的OpenGL程序中使用輪廓字體了。就像第13課,我曾在網(wǎng)上尋找一篇與這一課相似的教程,但是也沒有找到。或許我的網(wǎng)站是第一個涉及這個主題同時又把它解釋的簡單易懂的C代碼的網(wǎng)站吧。享用這篇教程,快樂編碼!

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


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            婷婷久久综合九色综合绿巨人| 伊人久久大香线蕉AV色婷婷色| 色综合合久久天天综合绕视看| 久久综合给合久久狠狠狠97色| 久久精品国产亚洲av麻豆小说| 欧美777精品久久久久网| 99久久国产综合精品网成人影院 | 亚洲欧美国产日韩综合久久| 久久精品免费全国观看国产| 一本久久a久久精品vr综合| 波多野结衣中文字幕久久| 久久99精品久久久久久齐齐| 欧美喷潮久久久XXXXx| 曰曰摸天天摸人人看久久久| 一本色道久久综合| 久久国产精品成人免费 | 亚洲精品99久久久久中文字幕| 伊人久久大香线蕉AV色婷婷色| 国产精自产拍久久久久久蜜| 72种姿势欧美久久久久大黄蕉| 久久妇女高潮几次MBA| 亚洲人成无码久久电影网站| 97久久精品午夜一区二区| 超级97碰碰碰碰久久久久最新| 亚洲国产成人久久综合野外| 99国产精品久久| 午夜精品久久久久久99热| 亚洲αv久久久噜噜噜噜噜| 久久久久国产精品三级网| 国产亚洲色婷婷久久99精品91| 久久精品国产清自在天天线| 久久人搡人人玩人妻精品首页 | 国产精品久久久久影视不卡| 国产精品亚洲综合久久| 国产亚州精品女人久久久久久 | 狠狠综合久久综合88亚洲 | 四虎国产精品免费久久| 久久e热在这里只有国产中文精品99 | 四虎国产精品免费久久| 亚洲国产成人久久综合区| 久久影视国产亚洲|