以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 23-lesson 24 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=54176) |
-- 作者:一分之千 -- 发布时间:10/22/2007 6:30:00 PM -- [推荐]NeHe OpenGL教程(中英文版附带VC++源码)Lesson 23-lesson 24 这一个将教会你如何把环境纹理包裹在你的3D模型上,让它看起来象反射了周围的场景一样。 在Photoshop中创建一幅球体环境映射图。 首先,你需要一幅球体环境映射图,用来把它映射到球体上。在Photoshop中打开一幅图并选择所有的像素,创建它的一个复制。 我们并没有添加任何全局变量,只是把纹理组的大小变为6,以保存6幅纹理。 GLuint texture[6]; // 保存6幅纹理 int LoadGLTextures() AUX_RGBImageRec *TextureImage[2]; // 创建纹理的保存空间 memset(TextureImage,0,sizeof(void *)*2); // 清空为0 // 载入*.bmp图像 glGenTextures(6, &texture[0]); // 创建6个纹理 for (int loop=0; loop<=1; loop++) // 创建线形过滤纹理图 // 创建线形Mipmap纹理图 return Status; GLvoid glDrawCube() glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // 设置s方向的纹理自动生成 int DrawGLScene(GLvoid) glTranslatef(0.0f,0.0f,z); glEnable(GL_TEXTURE_GEN_S); // 自动生成s方向纹理坐标 glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]); // 绑定纹理 glPopMatrix(); xrot+=xspeed; if (keys[' '] && !sp) 谢谢,并祝你好运! |
-- 作者:一分之千 -- 发布时间:10/22/2007 6:35:00 PM -- Lesson 23 Sphere Environment Mapping is a quick way to add a reflection to a metallic or reflective object in your scene. Although it is not as accurate as real life or as a Cube Environment Map, it is a whole lot faster! We'll be using the code from lesson eighteen (Quadrics) for the base of this tutorial. Also we're not using any of the same texture maps, we're going to use one sphere map, and one background image. Before we start... The "red book" defines a Sphere map as a picture of the scene on a metal ball from infinite distance away and infinite focal point. Well that is impossible to do in real life. The best way I have found to create a good sphere map image without using a Fish eye lens is to use Adobe's Photoshop program. Creating a Sphere Map In Photoshop: First you will need a picture of the environment you want to map onto the sphere. Open the picture in Adobe Photoshop and select the entire image. Copy the image and create a new PSD (Photoshop Format) the new image should be the same size as the image we just copied. Paste a copy of the image into the new window we've created. The reason we make a copy is so Photoshop can apply its filters. Instead of copying the image you can select mode from the drop down menu and choose RGB mode. All of the filters should then be available. Next we need to resize the image so that the image dimensions are a power of 2. Remember that in order to use an image as a texture the image needs to be 128x128, 256x256, etc. Under the image menu, select image size, uncheck the constraint proportions checkbox, and resize the image to a valid texture size. If your image is 100X90, it's better to make the image 128x128 than 64x64. Making the image smaller will lose alot of detail. The last thing we do is select the filter menu, select distort and apply a spherize modifier. You should see that the center of the picture is blown up like a balloon, now in normal sphere maps the outer area will be blackened out, but it doesn't really matter. Save a copy of the image as a .BMP and you're ready to code! We don't add any new global variables this time but we do modify the texture array to hold 6 textures. GLuint texture[6]; // Storage For 6 Textures int LoadGLTextures() // Load Bitmaps And Convert To Textures AUX_RGBImageRec *TextureImage[2]; // Create Storage Space For The Texture memset(TextureImage,0,sizeof(void *)*2); // Set The Pointer To NULL // Load The Bitmap, Check For Errors, If Bitmap's Not Found Quit glGenTextures(6, &texture[0]); // Create Three Textures for (int loop=0; loop<=1; loop++) // Create Linear Filtered Texture // Create MipMapped Texture return Status; // Return The Status GLvoid glDrawCube() So what the following code does is tells OpenGL how to automatically generate the S and T coordinates for us based on the sphere-mapping formula. The R and Q coordinates are usually ignored. The Q coordinate can be used for advanced texture mapping extensions, and the R coordinate may become useful once 3D texture mapping has been added to OpenGL, but for now we will ignore the R & Q Coords. The S coordinate runs horizontally across the face of our polygon, the T coordinate runs vertically across the face of our polygon. glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // Set The Texture Generation Mode For S To Sphere Mapping ( NEW ) int DrawGLScene(GLvoid) // Here's Where We Do All The Drawing glTranslatef(0.0f,0.0f,z); glEnable(GL_TEXTURE_GEN_S); // Enable Texture Coord Generation For S ( NEW ) glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]); // This Will Select A Sphere Map glPopMatrix(); glBindTexture(GL_TEXTURE_2D, texture[filter*2]); // This Will Select The BG Texture ( NEW ) glPopMatrix(); xrot+=xspeed; if (keys[' '] && !sp) Thanks, and Good Luck! |
-- 作者:一分之千 -- 发布时间:10/22/2007 6:45:00 PM -- 第二十四课 在这一课里,你将学会如何读取你显卡支持的OpenGL的扩展,并在你指定的剪裁区域把它显示出来。 #include <stdarg.h> // 处理可变参数的函数的头文件 int scroll; // 用来滚动屏幕 GLuint base; // 字符显示列表的开始值 typedef struct // 创建加载TGA图像文件结构 TextureImage textures[1]; // 保存一个纹理 bool LoadTGA(TextureImage *texture, char *filename) // 把TGA文件加载入内存 Tga图像格式 偏移 长度 描述 32位常用图像文件各个字节的值 00100000(2) 如果一切顺利,读取文件后关闭文件。 FILE *file = fopen(filename, "rb"); // 打开一个TGA文件 if( file==NULL || // 文件存在么? texture->width = header[1] * 256 + header[0]; // 记录文件高度 if( texture->width <=0 || // 宽度是否小于0 texture->bpp = header[4]; // 记录文件的位深 texture->imageData=(GLubyte *)malloc(imageSize); // 分配内存去保存TGA数据 if( texture->imageData==NULL || // 系统是否分配了足够的内存? fclose(file); // 关闭文件 for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel) // 循环所有的像素 fclose (file); // 关闭文件 // 创建纹理 glBindTexture(GL_TEXTURE_2D, texture[0].texID); // 绑定纹理 if (texture[0].bpp==24) // 是否为24位图像? glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData); return true; // 纹理绑定完成,成功返回 GLvoid BuildFont(GLvoid) // 创建字体显示列表 glNewList(base+loop1,GL_COMPILE); // 开始创建显示列表 GLvoid KillFont(GLvoid) GLvoid glPrint(GLint x, GLint y, int set, const char *fmt, ...) if (fmt == NULL) // 如果要显示的字符为空则返回 va_start(ap, fmt); // 开始分析参数,并把结果写入到text中 if (set>1) // 如果字符集大于1则使用第二个字符集 glEnable(GL_TEXTURE_2D); // 使用纹理映射 glScalef(1.0f,2.0f,1.0f); // 沿Y轴放大一倍 glCallLists(strlen(text),GL_UNSIGNED_BYTE, text); // 把字符写入到屏幕 GLvoid ReSizeGLScene(GLsizei width, GLsizei height) int InitGL(GLvoid) BuildFont(); // 创建字体 glShadeModel(GL_SMOOTH); // 使用平滑着色 return TRUE; // 成功返回 int DrawGLScene(GLvoid) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清楚背景和深度缓存 glColor3f(1.0f,0.5f,0.5f); // 设置为红色 glColor3f(1.0f,0.7f,0.4f); // 设置为橘黄色 glColor3f(0.5f,0.5f,1.0f); // 设置为蓝色 glLoadIdentity(); // 重置模型变换矩阵 glScissor(1 ,int(0.135416f*sheight),swidth-2,int(0.597916f*sheight)); // 定义剪裁区域 char* text=(char*)malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1); // 为保存OpenGL扩展的字符串分配内存空间 token=strtok(text," "); // 按空格分割text字符串,并把分割后的字符串保存在token中 当然它们不会都显示出来,记得我们使用了剪裁,只显示(0,96)-(0,96+32*9)之间的文本,其它的都被剪裁了。 更具我们上面的讲解,显示的第一个行如下: glColor3f(0.5f,1.0f,0.5f); // 设置颜色为绿色 glColor3f(1.0f,1.0f,0.5f); // 设置颜色为黄色 返回NULL时循环停止,表示已经显示完所有的扩展名。 token=strtok(NULL," "); // 查找下一个扩展名 glDisable(GL_SCISSOR_TEST); // 禁用剪裁测试 free (text); // 释放分配的内存 glFlush(); // 执行所有的渲染命令 KillFont(); // 删除字体 在WinMain()函数中我们需要加入新的按键控制 if (keys[VK_UP] && (scroll>0)) // 向上的箭头是否被按下? if (keys[VK_DOWN] && (scroll<32*(maxtokens-9))) // 向下的箭头是否被按住 |
-- 作者:一分之千 -- 发布时间:10/22/2007 6:51:00 PM -- Lesson 24 This tutorial is far from visually stunning, but you will definitely learn a few new things by reading through it. I have had quite a few people ask me about extensions, and how to find out what extensions are supported on a particular brand of video card. This tutorial will teach you how to find out what OpenGL extensions are supported on any type of 3D video card. I will also teach you how to scroll a portion of the screen without affecting any of the graphics around it using scissor testing. You will also learn how to draw line strips, and most importantly, in this tutorial we will drop the AUX library completely, along with Bitmap images. I will show you how to use Targa (TGA) images as textures. Not only are Targa files easy to work with and create, they support the ALPHA channel, which will allow you to create some pretty cool effects in future projects! The first thing you should notice in the code below is that we no longer include the glaux header file (glaux.h). It is also important to note that the glaux.lib file can also be left out! We're not working with bitmaps anymore, so there's no need to include either of these files in our project. Also, using glaux, I always received one warning message. Without glaux there should be zero errors, zero warnings. #include <windows.h> // Header File For Windows HDC hDC=NULL; // Private GDI Device Context bool keys[256]; // Array Used For The Keyboard Routine base is used to hold the font display list. swidth and sheight are used to grab the current window size. We use these two variable to help us calculate the scissor coordinates later in the code. int scroll; // Used For Scrolling The Screen GLuint base; // Base Display List For The Font The line just after the structure (TextureImage textures[1]) sets aside storage for the one texture that we will be using in this program. typedef struct // Create A Structure TextureImage textures[1]; // Storage For One Texture LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // Declaration For WndProc We pass two parameters to this section of code. The first parameter points to memory that we can store the texture in (*texture). The second parameter is the name of the file that we want to load (*filename). The first variable TGAheader[ ] holds 12 bytes. We'll compare these bytes with the first 12 bytes we read from the TGA file to make sure that the file is indeed a Targa file, and not some other type of image. TGAcompare will be used to hold the first 12 bytes we read in from the TGA file. The bytes in TGAcompare will then be compared with the bytes in TGAheader to make sure everything matches. header[ ] will hold the first 6 IMPORTANT bytes from the header file (width, height, and bits per pixel). The variable bytesPerPixel will store the result after we divide bits per pixel by 8, leaving us with the number of bytes used per pixel. imageSize will store the number of bytes required to make up the image (width * height * bytes per pixel). temp is a temporary variable that we will use to swap bytes later in the program. The last variable type is a variable that I use to select the proper texture building params depending on whether or not the TGA is 24 or 32 bit. If the texture is 24 bit we need to use GL_RGB mode when we build the texture. If the TGA is 32 bit we need to add the Alpha component, meaning we have to use GL_RGBA (By default I assume the image is 32 bit by default that is why type is GL_RGBA). bool LoadTGA(TextureImage *texture, char *filename) // Loads A TGA File Into Memory The if statement has a few jobs. First off it checks to see if the file contains any data. If there is no data, NULL will be returned, the file will be closed with fclose(file), and we return false. If the file contains information, we attempt to read the first 12 bytes of the file into TGAcompare. We break the line down like this: fread will read sizeof(TGAcompare) (12 bytes) from file into TGAcompare. Then we check to see if the number of bytes read is equal to sizeof(TGAcompare) which should be 12 bytes. If we were unable to read the 12 bytes into TGAcompare the file will close and false will be returned. If everything has gone good so far, we then compare the 12 bytes we read into TGAcompare with the 12 bytes we have stored in TGAheader. If the bytes do not match, the file will close, and false will be returned. Lastly, if everything has gone great, we attempt to read 6 more bytes into header (the important bytes). If 6 bytes are not available, again, the file will close and the program will return false. FILE *file = fopen(filename, "rb"); // Open The TGA File if( file==NULL || // Does File Even Exist? The height is calculated the same way but instead of using the values stored in header[0] and header[1] we use the values stored in header[2] and header[3]. After we have calculated the width and height we check to see if either the width or height is less than or equal to 0. If either of the two variables is less than or equal to zero, the file will be closed, and false will be returned. We also check to see if the TGA is a 24 or 32 bit image. We do this by checking the value stored at header[4]. If the value is not 24 or 32 (bit), the file will be closed, and false will be returned. In case you have not realized. A return of false will cause the program to fail with the message "Initialization Failed". Make sure your TGA is an uncompressed 24 or 32 bit image! texture->width = header[1] * 256 + header[0]; // Determine The TGA Width (highbyte*256+lowbyte) if( texture->width <=0 || // Is The Width Less Than Or Equal To Zero The value in header[4] is the bits per pixel. So we set bpp to equal header[4]. If you know anything about bits and bytes, you know that 8 bits makes a byte. To figure out how many bytes per pixel the TGA uses, all we have to do is divide bits per pixel by 8. If the image is 32 bit, bytesPerPixel will equal 4. If the image is 24 bit, bytesPerPixel will equal 3. To calculate the image size, we multiply width * height * bytesPerPixel. The result is stored in imageSize. If the image was 100x100x32 bit our image size would be 100 * 100 * 32/8 which equals 10000 * 4 or 40000 bytes! texture->bpp = header[4]; // Grab The TGA's Bits Per Pixel (24 or 32) The "if" statement has a few tasks. First it checks to see if the memory was allocated properly. If not, imageData will equal NULL, the file will be closed, and false will be returned. If the memory was allocated, we attempt to read the image data from the file into the allocated memory. The line fread(texture->imageData, 1, imageSize, file) does the trick. fread means file read. imageData points to the memory we want to store the data in. 1 is the size of data we want to read in bytes (we want to read 1 byte at a time). imageSize is the total number of bytes we want to read. Because imageSize is equal to the total amount of ram required to hold the image, we end up reading in the entire image. file is the handle for our open file. After reading in the data, we check to see if the amount of data we read in is the same as the value stored in imageSize. If the amount of data read and the value of imageSize is not the same, something went wrong. If any data was loaded, we will free it. (release the memory we allocated). The file will be closed, and false will be returned. texture->imageData=(GLubyte *)malloc(imageSize); // Reserve Memory To Hold The TGA Data if( texture->imageData==NULL || // Does The Storage Memory Exist? fclose(file); // Close The File The first thing we do is create a loop (i) that goes from 0 to imageSize. By doing this, we can loop through all of the image data. Our loop will increase by steps of 3 (0, 3, 6, 9, etc) if the TGA file is 24 bit, and 4 (0, 4, 8, 12, etc) if the image is 32 bit. The reason we increase by steps is so that the value at i is always going to be the first byte ([b]lue byte) in our group of 3 or 4 bytes. Inside the loop, we store the [b]lue byte in our temp variable. We then grab the red byte which is stored at texture->imageData[i+2] (Remember that TGAs store the colors as BGR[A]. B is i+0, G is i+1 and R is i+2) and store it where the [b]lue byte used to be. Lastly we move the [b]lue byte that we stored in the temp variable to the location where the [r]ed byte used to be (i+2), and we close the file with fclose(file). If everything went ok, the TGA should now be stored in memory as usable OpenGL texture data! for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel) // Loop Through The Image Data fclose (file); // Close The File It's important that you understand a few things before we go on. In the InitGL() code, when we call LoadTGA() we pass it two parameters. The first parameter is &textures[0]. In LoadTGA() we don't make reference to &textures[0]. We make reference to &texture[0] (no 's' at the end). When we modify &texture[0] we are actually modifying textures[0]. texture[0] assumes the identity of textures[0]. I hope that makes sense. So if we wanted to create a second texture, we would pass the parameter &textures[1]. In LoadTGA() any time we modified texture[0] we would be modifying textures[1]. If we passed &textures[2], texture[0] would assume the identity of &textures[2], etc. Hard to explain, easy to understand. Of course I wont be happy until I make it really clear :) Last example in english using an example. Say I had a box. I called it box #10. I gave it to my friend and asked him to fill it up. My friend could care less what number it is. To him it's just a box. So he fills what he calls "just a box". He gives it back to me. To me he just filled Box #10 for me. To him he just filled a box. If I give him another box called box #11 and say hey, can you fill this. He'll again think of it as just "box". He'll fill it and give it back to me full. To me he's just filled box #11 for me. When I give LoadTGA &textures[1] it thinks of it as &texture[0]. It fills it with texture information, and once it's done I am left with a working textures[1]. If I give LoadTGA &textures[2] it again thinks of it as &texture[0]. It fills it with data, and I'm left with a working textures[2]. Make sense :) Anyways... On to the code! We tell LoadTGA() to build our texture. We bind the texture, and tell OpenGL we want it to be linear filtered. // Build A Texture From The Data glBindTexture(GL_TEXTURE_2D, texture[0].texID); // Bind Our Texture if (texture[0].bpp==24) // Was The TGA 24 Bits After the texture has been built, we return true. This lets the InitGL() code know that everything went ok. glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData); return true; // Texture Building Went Ok, Return True Only real difference is that I bind to textures[0].texID. Which points to the font texture. Only real difference is that .texID has been added. GLvoid BuildFont(GLvoid) // Build Our Font Display List glNewList(base+loop1,GL_COMPILE); // Start Building A List GLvoid KillFont(GLvoid) // Delete The Font From Memory GLvoid glPrint(GLint x, GLint y, int set, const char *fmt, ...) // Where The Printing Happens if (fmt == NULL) // If There's No Text va_start(ap, fmt); // Parses The String For Variables if (set>1) // Did User Choose An Invalid Character Set? glEnable(GL_TEXTURE_2D); // Enable Texture Mapping glScalef(1.0f,2.0f,1.0f); // Make The Text 2X Taller glCallLists(strlen(text),GL_UNSIGNED_BYTE, text); // Write The Text To The Screen GLvoid ReSizeGLScene(GLsizei width, GLsizei height) // Resize And Initialize The GL Window If you wanted to load a second texture you could use the following code: if ((!LoadTGA(&textures[0],"image1.tga")) || (!LoadTGA(&textures[1],"image2.tga"))) { } After we load the TGA (creating our texture), we build our font, set shading to smooth, set the background color to black, enable clearing of the depth buffer, and select our font texture (bind to it). Lastly we return true so that our program knows that initialization went ok. int InitGL(GLvoid) // All Setup For OpenGL Goes Here BuildFont(); // Build The Font glShadeModel(GL_SMOOTH); // Enable Smooth Shading return TRUE; // Initialization Went OK We have another variable called cnt. I use this variable both for counting the number of extensions supported, and for positioning the text on the screen. cnt is reset to zero every time we call DrawGLScene. We clear the screen and depth buffer and then set the color to bright red (full red intensity, 50% green, 50% blue). at 50 on the x axis and 16 on the y axis we write teh word "Renderer". We also write "Vendor" and "Version" at the top of the screen. The reason each word does not start at 50 on the x axis is because I right justify the words (they all line up on the right side). 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 glColor3f(1.0f,0.5f,0.5f); // Set Color To Bright Red The information we get from glGetString(GL_RENDERER) will be written beside the red text "Renderer", the information we get from glGetString(GL_VENDOR) will be written to the right of "Vendor", etc. I'd like to explain casting in more detail, but I'm not really sure of a good way to explain it. If anyone has a good explanation, send it in, and I'll modify my explanation. After we have the renderer information, vendor information and version number written to the screen, we change the color to a bright blue, and write "NeHe Productions" at the bottom of the screen :) Of course you can change this to anything you want. glColor3f(1.0f,0.7f,0.4f); // Set Color To Orange glColor3f(0.5f,0.5f,1.0f); // Set Color To Bright Blue We then set the color to white, and start drawing our borders. A line strip is actually pretty easy to use. You tell OpenGL you want to draw a line strip with glBegin(GL_LINE_STRIP). Then we set the first vertex. Our first vertex will be on the far right side of the screen, and about 63 pixels up from the bottom of the screen (639 on the x axis, 417 on the y axis). Then we set the second vertex. We stay at the same location on the y axis (417), but we move to the far left side of the screen on the x axis (0). A line will be drawn from the right side of the screen (639,417) to the left side of the screen (0,417). You need to have at least two vertices in order to draw a line (common sense). From the left side of the screen, we move down, right, and then straight up (128 on the y axis). We then start another line strip, and draw a second box at the top of the screen. If you need to draw ALOT of connected lines, line strips can definitely cut down on the amount of code required as opposed to using regular lines (GL_LINES). glLoadIdentity(); // Reset The ModelView Matrix In the next line we enable scissor testing. Anything we draw OUTSIDE the scissor window will not show up. You could draw a HUGE quad on the screen from 0,0 to 639,480, and you would only see the quad inside the scissor window, the rest of the screen would be unaffected. Very nice command! The third line of code creates a variable called text that will hold the characters returned by glGetString(GL_EXTENSIONS). malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1) allocates enough memory to hold the entire string returned +1 (so if the string was 50 characters, text would be able to hold all 50 characters). The next line copies the GL_EXTENSIONS information to text. If we modify the GL_EXTENSIONS information directly, big problems will occur, so instead we copy the information into text, and then manipulate the information stored in text. Basically we're just taking a copy, and storing it in the variable text. glScissor(1 ,int(0.135416f*sheight),swidth-2,int(0.597916f*sheight)); // Define Scissor Region char* text=(char*)malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1); // Allocate Memory For Our Extension String Next we create a loop that stops once there is no more information left in text. If there is no information in text, token will be equal to nothing (NULL) and the loop will stop. We increase the counter variable (cnt) by one, and then check to see if the value in cnt is higher than the value of maxtokens. If cnt is higher than maxtokens we make maxtokens equal to cnt. That way if the counter hits 20, maxtokens will also equal 20. It's an easy way to keep track of the maximum value of cnt. token=strtok(text," "); // Parse 'text' For Words, Seperated By " " (spaces) The variable is drawn on the far left side of the screen (0 on the x axis). We start drawing at 96 on the y axis. To keep all the text from drawing to the same spot on the screen, we add (cnt*32) to 96. So if we are displaying the first extension, cnt will equal 1, and the text will be drawn at 96+(32*1) (128) on the y axis. If we display the second extension, cnt will equal 2, and the text will be drawn at 96+(32*2) (160) on the y axis. Notice I also subtract scroll. When the program first runs, scroll will be equal to 0. So our first line of text is drawn at 96+(32*1)-0. If you press the DOWN ARROW, scroll is increased by 2. If scroll was 4, the text would be drawn at 96+(32*1)-4. That means the text would be drawn at 124 instead of 128 on the y axis because of scroll being equal to 4. The top of our scissor window ends at 128 on the y axis. Any part of the text drawn from lines 124-127 on the y axis will not appear on the screen. Same thing with the bottom of the screen. If cnt was equal to 11 and scroll was equal to 0, the text would be drawn at 96+(32*11)-0 which is 448 on the y axis. Because the scissor window only allows us to draw as far as line 416 on the y axis, the text wouldn't show up at all. The final result is that we end up with a scrollable window that only allows us to look at 288/32 (9) lines of text. 288 is the height of our scissor window. 32 is the height of the text. By changing the value of scroll we can move the text up or down (offset the text). The effect is similar to a movie projector. The film rolls by the lens, and all you see is the current frame. You don't see the area above or below the frame. The lens acts as a window similar to the window created by the scissor test. After we have drawn the current count (cnt) to the screen, we change the color to yellow, move 50 pixels to the right on the x axis, and we write the text stored in the variable token to the screen. Using our example above, the first line of text displayed on the screen should look like this: 1 GL_ARB_multitexture glColor3f(0.5f,1.0f,0.5f); // Set Color To Bright Green Using our example above, the first line of text displayed on the screen should look like this: 1 GL_ARB_multitexture glColor3f(1.0f,1.0f,0.5f); // Set Color To Yellow In our example above ("GL_ARB_multitexturemarkerGL_EXT_abgr GL_EXT_bgra") there will now be a marker after the text "GL_ARB_multitexture". The line below will start search FROM the marker to the next space. Everything from the marker to the next space will be stored in token. token should end up being "GL_EXT_abgr", and text will end up being "GL_ARB_multitexturemarkerGL_EXT_abgrmarkerGL_EXT_bgra". Once strtok() has run out of text to store in token, token will become NULL and the loop will stop. token=strtok(NULL," "); // Search For The Next Token The next time DrawGLScene() is called, new memory will be allocated. A fresh copy of the information returned by glGetStrings(GL_EXTENSIONS) will be copied into the variable text and the entire process will start over. glDisable(GL_SCISSOR_TEST); // Disable Scissor Testing free (text); // Free Allocated Memory Last thing we do is return true to show that everything went ok. glFlush(); // Flush The Rendering Pipeline GLvoid KillGLWindow(GLvoid) // Properly Kill The Window if (hRC) // Do We Have A Rendering Context? if (!wglDeleteContext(hRC)) // Are We Able To Delete The RC? if (hDC && !ReleaseDC(hWnd,hDC)) // Are We Able To Release The DC if (hWnd && !DestroyWindow(hWnd)) // Are We Able To Destroy The Window? if (!UnregisterClass("OpenGL",hInstance)) // Are We Able To Unregister Class KillFont(); // Kill The Font The first change in WinMain() is the title that appears at the top of the window. It should now read "NeHe's Extensions, Scissoring, Token & TGA Loading Tutorial" int WINAPI WinMain( HINSTANCE hInstance, // Instance // Ask The User Which Screen Mode They Prefer // Create Our OpenGL Window while(!done) // Loop That Runs While done=FALSE if (keys[VK_F1]) // Is F1 Being Pressed? if (keys[VK_UP] && (scroll>0)) // Is Up Arrow Being Pressed? 32 is the number of lines that each letter takes up. Maxtokens is the total amount of extensions that your video card supports. We subtract 9, because 9 lines can be shown on the screen at once. If we did not subtract 9, we could scroll past the end of the list, causing the list to scroll completely off the screen. Try leaving the -9 out if you're not sure what I mean. if (keys[VK_DOWN] && (scroll<32*(maxtokens-9))) // Is Down Arrow Being Pressed? // Shutdown If you find any problems with the tutorial, or you find the information to hard to understand, let me know. I want the tutorials to be the best they can be. Your feedback is important! Jeff Molofee (NeHe) |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
406.250ms |