|
以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 13-lesson 14 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=53880) |
|
-- 作者:一分之千 -- 发布时间:10/16/2007 12:01:00 PM -- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 13-lesson 14
这一课我们将创建一些基于2D图像的字体,它们可以缩放,但不能旋转,并且总是面向前方,但作为基本的显示来说,我想已经够了。 当然,你可以载入一段美术程序,把文字写在一个图片上,再把这幅图片载入你的OpenGL程序中,打开混合选项,从而在屏幕上显示出文字。但是这种做法非常耗时。而且根据你选择的滤波类型,最终结果常常会显得很模糊,或者有很多马赛克。另外,除非你的图像包含一个Alpha通道,否则一旦绘制在屏幕上,那些文字就会不透明(与屏幕中的其它物体混合)。 如果你使用过记事本、微软的Word或者其它文字处理软件,你会注意到所有不同的字体都是可用的。这课就会教你如何在自己的OpenGL程序中使用和原样相同的字体。事实上,任何安装在你的计算机中的字体都可以使用在演示中(中文不行)。 使用位图字体比起使用图形字体(贴图)看起来不止强100倍。你可以随时改变显示在屏幕上的文字,而且用不着为它们逐个制作贴图。只需要将文字定位,再使用我最新的gl命令就可以在屏幕上显示文字了。 我尽可能试着将命令做的简单。你只需要敲入glPrint("Hello") 。它是那么简单。不管怎样,从这段长长的介绍就可以看出,我对这课教程是多么的满意。写这段代码大概花了我一个半小时,为什么这么长的时间呢?那是因为在使用位图字体方面完全没有可用的资料,除非你愿意使用MFC中的代码。为了使代码简单,我想,如果我把它全部重写为容易理解的C语言代码,那一定会好些 :) 一个小注释,这段代码是专门针对Windows写的,它使用了Windows的wgl函数来创建字体,显然,Apple机系统有agl,X系统有glx来支持做同样事情的,不幸的是,我不能保证这些代码也是容易使用的。如果那位有能在屏幕上显示文字且独立于平台的代码,请告诉我,我将重写一个有关字体的教程。 我们从第一课的典型代码开始,添加上stdio.h头文件以便进行标准输入/输出操作,另外,stdarg.h头文件用来解析文字以及把变量转换为文字。最后加上math.h头文件,这样我们就可以使用SIN和COS函数在屏幕中移动文字了。 #include <stdarg.h> // 用来定义可变参数的头文件 然后添加两个计数器(cnt1 和 cnt2),它们采用不用的累加速度,通过SIN和COS函数来改变文字在屏幕上的位置。在屏幕上创造出一种看起来像是半随机的移动方式。同时,我们用这两个计数器来改变文字的颜色(后面会进一步解释)。 GLuint base; // 绘制字体的显示列表的开始位置 GLvoid BuildFont(GLvoid) // 创建位图字体 base = glGenLists(96); // 创建96个显示列表 font = CreateFont( -24, // 字体高度 0, // 字体宽度 0, // 字体的旋转角度 Angle Of Escapement FW_BOLD, // 字体的重量 FALSE, // 是否使用斜体 ANSI_CHARSET, // 设置字符集 OUT_TT_PRECIS, // 输出精度 CLIP_DEFAULT_PRECIS, // 裁剪精度 ANTIALIASED_QUALITY, // 输出质量 FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch "Courier New"); // 字体名称 oldfont = (HFONT)SelectObject(hDC, font); // 选择我们需要的字体 GLvoid KillFont(GLvoid) // 删除显示列表 GLvoid glPrint(const char *fmt, ...) // 自定义GL输出字体函数 char text[256]; // 保存文字串 if (fmt == NULL) // 如果无输入则返回 va_start(ap, fmt); // 分析可变参数 glPushAttrib(GL_LIST_BIT); // 把显示列表属性压入属性堆栈 也许你想知道为什么字符不会彼此重叠堆积在一起。那时因为每个字符的显示列表都知道字符的右边缘在那里,在写完一个字符后,OpenGL自动移动到刚写过的字符的右边,在写下一个字或画下一个物体时就会从GL移动到的最后的位置开始,也就是最后一个字符的右边。 最后,我们将GL_LIST_BIT属性弹出堆栈,将GL恢复到我们使用glListBase(base-32)设置base那时的状态。 glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // 调用显示列表绘制字符串 BuildFont(); // 创建字体 int DrawGLScene(GLvoid) // 此过程中包括所有的绘制代码 // 根据字体位置设置颜色 // 设置光栅化位置,即字体的位置 我知道如果你是一个有经验的C程序员,这是个很基础的问题。不过也许也有人没有用过pringf函数。如果你想了解更多的字符,那就买本书或者查阅MSDN。 glPrint("Active OpenGL Text With NeHe - %7.2f", cnt1); // 输出文字到屏幕 cnt1+=0.051f; // 增加计数器值 KillFont(); // 删除字体 |
|
-- 作者:一分之千 -- 发布时间:10/16/2007 12:02:00 PM -- Lesson 13 Welcome to yet another Tutorial. This time on I'll be teaching you how to use Bitmap Fonts. You may be saying to yourself "what's so hard about putting text onto the screen". If you've ever tried it, it's not that easy! Sure you can load up an art program, write text onto an image, load the image into your OpenGL program, turn on blending then map the text onto the screen. But this is time consuming, the final result usually looks blurry or blocky depending on the type of filtering you use, and unless your image has an alpha channel your text will end up transparent (blended with the objects on the screen) once it's mapped to the screen. If you've ever used Wordpad, Microsoft Word or some other Word Processor, you may have noticed all the different types of Font's avaialable. This tutorial will teach you how to use the exact same fonts in your own OpenGL programs. As a matter of fact... Any font you install on your computer can be used in your demos. Not only do Bitmap Fonts looks 100 times better than graphical fonts (textures). You can change the text on the fly. No need to make textures for each word or letter you want to write to the screen. Just position the text, and use my handy new gl command to display the text on the screen. I tried to make the command as simple as possible. All you do is type glPrint("Hello"). It's that easy. Anyways. You can tell by the long intro that I'm pretty happy with this tutorial. It took me roughly 1 1/2 hours to create the program. Why so long? Because there is literally no information available on using Bitmap Fonts, unless of course you enjoy MFC code. In order to keep the code simple I decided it would be nice if I wrote it all in simple to understand C code :) A small note, this code is Windows specific. It uses the wgl functions of Windows to build the font. Apparently Apple has agl support that should do the same thing, and X has glx. Unfortunately I can't guarantee this code is portable. If anyone has platform independant code to draw fonts to the screen, send it my way and I'll write another font tutorial. We start off with the typical code from lesson 1. We'll be adding the stdio.h header file for standard input/output operations; the stdarg.h header file to parse the text and convert variables to text, and finally the math.h header file so we can move the text around the screen using SIN and COS. #include <windows.h> // Header File For Windows HDC hDC=NULL; // Private GDI Device Context Next we add two counters (cnt1 & cnt2). These counters will count up at different rates, and are used to move the text around the screen using SIN and COS. This creates a semi-random looking movement on the screen. We'll also use the counters to control the color of the letters (more on this later). GLuint base; // Base Display List For The Font Set bool keys[256]; // Array Used For The Keyboard Routine LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc Next we define base. We do this by creating a group of 96 display lists using glGenLists(96). After the display lists are created, the variable base will hold the number of the first list. GLvoid BuildFont(GLvoid) // Build Our Bitmap Font base = glGenLists(96); // Storage For 96 Characters ( NEW ) font = CreateFont( -24, // Height Of Font ( NEW ) 0, // Width Of Font 0, // Angle Of Escapement FW_BOLD, // Font Weight FALSE, // Italic If you're interested in using a font such as Webdings or Wingdings, you need to use SYMBOL_CHARSET instead of ANSI_CHARSET. ANSI_CHARSET, // Character Set Identifier OUT_TT_PRECIS, // Output Precision CLIP_DEFAULT_PRECIS, // Clipping Precision ANTIALIASED_QUALITY, // Output Quality FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch "Courier New"); // Font Name oldfont = (HFONT)SelectObject(hDC, font); // Selects The Font We Want GLvoid KillFont(GLvoid) // Delete The Font List GLvoid glPrint(const char *fmt, ...) // Custom GL "Print" Routine char text[256]; // Holds Our String if (fmt == NULL) // If There's No Text va_start(ap, fmt); // Parses The String For Variables The command glListBase(base-32) is a little hard to explain. Say we draw the letter 'A', it's represented by the number 65. Without glListBase(base-32) OpenGL wouldn't know where to find this letter. It would look for it at display list 65, but if base was equal to 1000, 'A' would actually be stored at display list 1065. So by setting a base starting point, OpenGL knows where to get the proper display list from. The reason we subtract 32 is because we never made the first 32 display lists. We skipped them. So we have to let OpenGL know this by subtracting 32 from the base value. I hope that makes sense. glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits ( NEW ) The line below does the following. First it tells OpenGL we're going to be displaying lists to the screen. strlen(text) finds out how many letters we're going to send to the screen. Next it needs to know what the largest list number were sending to it is going to be. We're not sending any more than 255 characters. The lists parameter is treated as an array of unsigned bytes, each in the range 0 through 255. Finally we tell it what to display by passing text (pointer to our string). In case you're wondering why the letters don't pile on top of eachother. Each display list for each character knows where the right side of the letter is. After the letter is drawn, OpenGL translates to the right side of the drawn letter. The next letter or object drawn will be drawn starting at the last location GL translated to, which is to the right of the last letter. Finally we pop the GL_LIST_BIT setting GL back to how it was before we set our base setting using glListBase(base-32). glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // Draws The Display List Text ( NEW ) int InitGL(GLvoid) // All Setup For OpenGL Goes Here BuildFont(); // Build The Font return TRUE; // Initialization Went OK You'll notice that if you translate even deeper into the screen the size of the font does not shrink like you'd expect it to. What actually happens when you translate deeper is that you have more control over where the text is on the screen. If you tranlate 1 unit into the screen, you can place the text anywhere from -0.5 to +0.5 on the X axis. If you tranlate 10 units into the screen, you place the text from -5 to +5. It just gives you more control instead of using decimal places to position the text at exact locations. Nothing will change the size of the text. Not even glScalef(x,y,z). If you want the font bigger or smaller, make it bigger or smaller when you create it! int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing In this case I'm using the two counters we made to move the text around the screen to change the red, green and blue colors. Red will go from -1.0 to 1.0 using COS and counter 1. Green will also go from -1.0 to 1.0 using SIN and counter 2. Blue will go from 0.5 to 1.5 using COS and counter 1 and 2. That way blue will never be 0, and the text should never completely fade out. Stupid, but it works :) // Pulsing Colors Based On Text Position The fancy(?) math does pretty much the same thing as the color setting math does. It moves the text on the x axis from -0.50 to -0.40 (remember, we subtract 0.45 right off the start). This keeps the text on the screen at all times. It swings left and right using COS and counter 1. It moves from -0.35 to +0.35 on the Y axis using SIN and counter 2. // Position The Text On The Screen Shawn T. sent me modified code that allows glPrint to pass variables to the screen. This means that you can increase a counter and display the results on the screen! It works like this... In the line below you see our normal text. Then there's a space, a dash, a space, then a "symbol" (%7.2f). Now you may look at %7.2f and say what the heck does that mean. It's very simple. % is like a marker saying don't print 7.2f to the screen, because it represents a variable. The 7 means a maximum of 7 digits will be displayed to the left of the decimal place. Then the decimal place, and right after the decimal place is a 2. The 2 means that only two digits will be displayed to the right of the decimal place. Finally, the f. The f means that the number we want to display is a floating point number. We want to display the value of cnt1 on the screen. As an example, if cnt1 was equal to 300.12345f the number we would end up seeing on the screen would be 300.12. The 3, 4, and 5 after the decimal place would be cut off because we only want 2 digits to appear after the decimal place. I know if you're an experienced C programmer, this is absolute basic stuff, but there may be people out there that have never used printf. If you're interested in learning more about symbols, buy a book, or read through the MSDN. glPrint("Active OpenGL Text With NeHe - %7.2f", cnt1); // Print GL Text To The Screen cnt1+=0.051f; // Increase The First Counter if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class KillFont(); // Destroy The Font Jeff Molofee (NeHe) |
|
-- 作者:一分之千 -- 发布时间:10/16/2007 12:03:00 PM -- 第十四课 在一课我们将教你绘制3D的图形字体,它们可像一般的3D模型一样被变换。 一个小注释,这段代码是专门针对Windows写的,它使用了Windows的wgl函数来创建字体,显然,Apple机系统有agl,X系统有glx来支持做同样事情的,不幸的是,我不能保证这些代码也是容易使用的。如果哪位有能在屏幕上显示文字且独立于平台的代码,请告诉我,我将重写一个有关字体的教程。 我们从第一课的典型代码开始,添加上stdio.h头文件以便进行标准输入/输出操作,另外,stdarg.h头文件用来解析文字以及把变量转换为文字。最后加上math.h头文件,这样我们就可以使用SIN和COS函数在屏幕中移动文字了。 GLuint base; // 绘制字体的显示列表的开始位置 GLYPHMETRICSFLOAT gmf[256]; // 记录256个字符的信息 base = glGenLists(256); // 创建256个显示列表 The parameter WGL_FONT_POLYGONS tells OpenGL to create a solid font using polygons. If we use WGL_FONT_LINES instead, the font will be wireframe (made of lines). It's also important to note that if you use GL_FONT_LINES, normals will not be generated so lighting will not work properly. The last parameter gmf points to the address buffer for the display list data. 0.0f, // 字体的光滑度,越小越光滑,0.0为最光滑的状态 GLvoid KillFont(GLvoid) // 删除显示列表 GLvoid glPrint(const char *fmt, ...) // 自定义GL输出字体函数 float length=0; // 查询字符串的长度 if (fmt == NULL) // 如果无输入则返回 va_start(ap, fmt); // 分析可变参数 给出我们每个字符的长度的代码是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++) // 查找整个字符串的长度 glTranslatef(-length/2,0.0f,0.0f); // 把字符串置于最左边 glPushAttrib(GL_LIST_BIT); // 把显示列表属性压入属性堆栈 也许你想知道为什么字符不会彼此重叠堆积在一起。那时因为每个字符的显示列表都知道字符的右边缘在那里,在写完一个字符后,OpenGL自动移动到刚写过的字符的右边,在写下一个字或画下一个物体时就会从GL移动到的最后的位置开始,也就是最后一个字符的右边。 最后,我们将GL_LIST_BIT属性弹出堆栈,将GL恢复到我们使用glListBase(base)设置base之前的状态。 glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // 调用显示列表绘制字符串 int DrawGLScene(GLvoid) // 此过程中包括所有的绘制代码 glRotatef(rot,1.0f,0.0f,0.0f); // 沿X轴旋转 // 根据字体位置设置颜色 glPrint("NeHe - %3.2f",rot/50); // 输出文字到屏幕 rot+=0.5f; // 增加旋转变量 |
|
-- 作者:一分之千 -- 发布时间:10/16/2007 12:03:00 PM -- Lesson 14 This tutorial is a sequel to the last tutorial. In tutorial 13 I taught you how to use Bitmap Fonts. In this tutorial I'll teach you how to use Outline Fonts. The way we create Outline fonts is fairly similar to the way we made the Bitmap font in lesson 13. However... Outline fonts are about 100 times more cool! You can size Outline fonts. Outline font's can move around the screen in 3D, and outline fonts can have thickness! No more flat 2D characters. With Outline fonts, you can turn any font installed on your computer into a 3D font for OpenGL, complete with proper normals so the characters light up really nice when light shines on them. A small note, this code is Windows specific. It uses the wgl functions of Windows to build the font. Apparently Apple has agl support that should do the same thing, and X has glx. Unfortunately I can't guarantee this code is portable. If anyone has platform independant code to draw fonts to the screen, send it my way and I'll write another font tutorial. We start off with the typical code from lesson 1. We'll be adding the stdio.h header file for standard input/output operations; the stdarg.h header file to parse the text and convert variables to text, and finally the math.h header file so we can move the text around the screen using SIN and COS. #include <windows.h> // Header File For Windows HDC hDC=NULL; // Private GDI Device Context Next we add a variable called rot. rot will be used to spin the text around on the screen using both SIN and COS. It will also be used to pulse the colors. GLuint base; // Base Display List For The Font Set ( ADD ) bool keys[256]; // Array Used For The Keyboard Routine GLYPHMETRICSFLOAT gmf[256]; // Storage For Information About Our Font LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc 'HFONT font' will hold our Windows font ID. Next we define base. We do this by creating a group of 256 display lists using glGenLists(256). After the display lists are created, the variable base will hold the number of the first list. GLvoid BuildFont(GLvoid) // Build Our Bitmap Font base = glGenLists(256); // Storage For 256 Characters font = CreateFont( -12, // Height Of Font 0, // Width Of Font 0, // Angle Of Escapement FW_BOLD, // Font Weight FALSE, // Italic If you're interested in using a font such as Webdings or Wingdings, you need to use SYMBOL_CHARSET instead of ANSI_CHARSET. ANSI_CHARSET, // Character Set Identifier OUT_TT_PRECIS, // Output Precision CLIP_DEFAULT_PRECIS, // Clipping Precision ANTIALIASED_QUALITY, // Output Quality FF_DONTCARE|DEFAULT_PITCH, // Family And Pitch "Comic Sans MS"); // Font Name SelectObject(hDC, font); // Selects The Font We Created wglUseFontOutlines( hDC, // Select The Current DC The parameter WGL_FONT_POLYGONS tells OpenGL to create a solid font using polygons. If we use WGL_FONT_LINES instead, the font will be wireframe (made of lines). It's also important to note that if you use GL_FONT_LINES, normals will not be generated so lighting will not work properly. The last parameter gmf points to the address buffer for the display list data. 0.0f, // Deviation From The True Outlines GLvoid KillFont(GLvoid) // Delete The Font GLvoid glPrint(const char *fmt, ...) // Custom GL "Print" Routine float length=0; // Used To Find The Length Of The Text if (fmt == NULL) // If There's No Text va_start(ap, fmt); // Parses The String For Variables We start off by making a loop that goes through all the text character by character. strlen(text) gives us the length of our text. After we've set up the loop, we will increase the value of length by the width of each character. When we are done the value stored in length will be the width of our entire string. So if we were printing "hello" and by some fluke each character was exactly 10 units wide, we'd increase the value of length by the width of the first letter 10. Then we'd check the width of the second letter. The width would also be 10, so length would become 10+10 (20). By the time we were done checking all 5 letters length would equal 50 (5*10). The code that gives us the width of each character is gmf[text[loop]].gmfCellIncX. remember that gmf stores information out each display list. If loop is equal to 0 text[loop] will be the first character in our string. If loop is equal to 1 text[loop] will be the second character in our string. gmfCellIncX tells us how wide the selected character is. gmfCellIncX is actually the distance that our display moves to the right after the character has been drawn so that each character isn't drawn on top of eachother. Just so happens that distance is our width :) You can also find out the character height with the command gmfCellIncY. This might come in handy if you're drawing text vertically on the screen instead of horizontally. for (unsigned int loop=0;loop<(strlen(text));loop++) // Loop To Find Text Length glTranslatef(-length/2,0.0f,0.0f); // Center Our Text On The Screen The command glListBase(base) tells OpenGL where to find the proper display list for each character. glPushAttrib(GL_LIST_BIT); // Pushes The Display List Bits The line below does the following. First it tells OpenGL we're going to be displaying lists to the screen. strlen(text) finds out how many letters we're going to send to the screen. Next it needs to know what the largest list number were sending to it is going to be. We're still not sending any more than 255 characters. So we can use an UNSIGNED_BYTE. (a byte represents a number from 0 - 255 which is exactly what we need). Finally we tell it what to display by passing the string text. In case you're wondering why the letters don't pile on top of eachother. Each display list for each character knows where the right side of the character is. After the letter is drawn to the screen, OpenGL translates to the right side of the drawn letter. The next letter or object drawn will be drawn starting at the last location GL translated to, which is to the right of the last letter. Finally we pop the GL_LIST_BIT setting GL back to how it was before we set our base setting using glListBase(base). glCallLists(strlen(text), GL_UNSIGNED_BYTE, text); // Draws The Display List Text There are a few new lines at the end of the InitGL code. The line BuildFont() from lesson 13 is still there, along with new code to do quick and dirty lighting. Light0 is predefined on most video cards and will light up the scene nicely with no effort on my part :) I've also added the command glEnable(GL_Color_Material). Because the characters are 3D objects you need to enable Material Coloring, otherwise changing the color with glColor3f(r,g,b) will not change the color of the text. If you're drawing shapes of your own to the screen while you write text enable material coloring before you write the text, and disable it after you've drawn the text, otherwise all the object on your screen will be colored. int InitGL(GLvoid) // All Setup For OpenGL Goes Here BuildFont(); // Build The Font ( ADD ) return TRUE; // Initialization Went OK Outline fonts can also be manipulated by using the glScalef(x,y,z) command. If you want the font 2 times taller, use glScalef(1.0f,2.0f,1.0f). the 2.0f is on the y axis, which tells OpenGL to draw the list twice as tall. If the 2.0f was on the x axis, the character would be twice as wide. int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing glRotatef(rot,1.0f,0.0f,0.0f); // Rotate On The X Axis // Pulsing Colors Based On The Rotation In the code below we'll print NeHe, a space, a dash, a space, and then whatever number is stored in rot divided by 50 (to slow down the counter a bit). If the number is larger that 999.99 the 4th digit to the left will be cut off (we're requesting only 3 digits to the left of the decimal place). Only 2 digits will be displayed after the decimal place. glPrint("NeHe - %3.2f",rot/50); // Print GL Text To The Screen rot+=0.5f; // Increase The Rotation Variable if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class KillFont(); // Destroy The Font Jeff Molofee (NeHe) |
|
-- 作者:ririyeye -- 发布时间:1/8/2010 10:33:00 AM -- 绝对的好东西 |
|
-- 作者:ririyeye -- 发布时间:1/8/2010 10:34:00 AM -- 严重支持! |
|
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
171.875ms |