分类存档: VC

VC下用CxShadeButton美化按钮

Introduction

With this class you can easily give a professional look to your buttons in few steps. No bitmap resources are needed - all bitmaps are generated at runtime. Parts of the code come from the CxSkinButton article.
The goal is to replace the standard buttons, check boxes and radio buttons with minimal modifications in the application code.

  1. Add "xShadeButton.cpp" and "xShadeButton.h" to the project.
  2. Include "xShadeButton.h" in the header file where the controls are defined
  3. Create (or edit) a member variable for each button you want to customize as
    CxShadeButton

    . If the Class Wizard doesn't show the

    CxShadeButton

    type, select

    CButton

    and then edit the code manually.

  4. In the window initialization add the
    CxShadeButton

    methods:

    Collapse
    BOOL CxShadeButtonDemoDlg::OnInitDialog()
    {
    	//	...
    
    	m_btn1.SetTextColor(RGB(255,0,0));
    	m_btn1.SetToolTipText("Button1");
    	m_btn1.SetShade(SHS_DIAGSHADE,8,10,5,RGB(55,255,55));
    //	...
    

CxShadeButton Class Members & Operations

CxShadeButton

is derived from

CButton

. The

BS_OWNERDRAW

style is added automatically, you don't need to set the "Owner draw" property in the resource editor. You can change some styles (flat, push-like, text alignment, group,...) using the resource editor, however not all the styles are currently supported.
If you change the aspect of the button at runtime, to avoid flicker first call the functions that don't cause invalidation (like

SetShade

,

SetIcon

or

SetFont

) and than invalidate the button, for example with

SetWindowText

, or directly with

Invalidate

.

Collapse
void SetShade(UINT shadeID=0, BYTE granularity=8,
              BYTE highlight=10,BYTE coloring=0,COLORREF color=0);
    Generates the button bitmaps.
    Important
  • <strong>shadeID</strong>

    : can be one of these effects:

    SHS_NOISE = 0
    SHS_DIAGSHADE = 1
    SHS_HSHADE = 2
    SHS_VSHADE = 3
    SHS_HBUMP = 4
    SHS_VBUMP = 5
    SHS_SOFTBUMP =6
    SHS_HARDBUMP = 7
    SHS_METAL = 8
  • <strong>granularity</strong>

    : this parameter add an uniform noise to the button bitmaps. A good value is from 5 to 20; 0 to disable the effect. The noise has a positive effect because it hides the palette steps.

  • <strong>highlight</strong>

    : sets the highlight level when the mouse is over the button. A good value is from 5 to 20; 0 to disable the effect.

  • <strong>coloring</strong>

    : sets the percentage of

    color

    to blend in the button palette. The value can range from 0 to 100; 0 to disable the effect.

  • <strong>color</strong>

    : if

    coloring

    is greater than zero,

    color

    is mixed with the standard button colors.Remarks : the

    coloring

    and

    color

    parameter should be used carefully to guarantee a good aspect in all the situations.

Collapse
void SetToolTipText(CString s, CString sDown="");
    Sets or changes the tool tip text.
    nice
  • <strong>s</strong>

    : String displayed in normal state.

  • <strong>sDown</strong>

    : (optional) Specifies a second text to be displayed when a check box or radio button is checked.

Collapse
COLORREF SetTextColor(COLORREF new_color);
    Sets or changes the button text color. Returns the previous button text color.
    nice
Collapse
void SetIcon(UINT nIcon, UINT nIconAlign=BS_CENTER, UINT nIconDown=0,
             UINT nIconHighLight=0);
    Similar to the BS_ICON style.
    nice
  • <strong>nIcon</strong>

    : ID number of the icon resource

  • <strong>nIconAlign</strong>

    : icon alignment, can be one of the following values:

    BS_CENTER
    BS_LEFT
    BS_RIGHT
  • <strong>nIconDown</strong>

    : (optional) ID number of the icon resource displayed when the button is checked.

  • <strong>nIconHighLight</strong>

    : (optional) ID number of the icon resource displayed when the mouse pointer is over the button.Remarks : the button text is automatically placed so that the icon and the text don't overlap.

Collapse
bool SetFont(CString sFontName, long lSize=0, long
             lWeight=400, BYTE bItalic=0, BYTE bUnderline=0);
bool SetFont(LOGFONT* pNewStyle); / LOGFONT* GetFont();
    Changes the text font.
    nice
  • <strong>sFontName</strong>

    : specifies the typeface name of the font.

  • <strong>lSize</strong>

    : (optional) text height

  • <strong>lWeight</strong>

    : (optional) text weight can range from 0 to 1000; 100=thin, 300=light, 400=normal, 700=bold

  • <strong>bItalic</strong>

    : (optional) italic style

  • <strong>bUnderline</strong>

    : (optional) underline style
    Remarks : use

    <strong>GetFont</strong>

    /

    <strong>SetFont</strong>

    with a LOGFONT structure to get/set the complete attributes of the font.

    <strong>GetFont</strong>

    returns NULL if the button is using the default system font.

Collapse
void SetTextAlign(UINT nTextAlign=BS_CENTER);
  • <strong>nTextAlign</strong>

    : button text alignment, can be one of the following values:

  • optional
    BS_CENTER
    BS_LEFT
    BS_RIGHT
Collapse
void SetFlat(bool bFlag);
  • <strong>bFlag</strong>

    : sets the border style:

  • optional
    FALSE

    = standard 3D border.

    TRUE

    = flat border.

VC播放声音文件

project--link -- 加入winmm.lib
程序中加入
#include <mmsystem.h>

CString file="a.wav";
sndPlaySound(file,SND_ASYNC);

VC使用VFW播放视频的范例

add to project vfw.h ,vfw32.lib
//videovw.cpp
#include "vfw.h"
add tow variable to contain the hwnd of MCIWnd
init m_videoWnd=NULL
//create m_videoWnd
CString filename("tlclouds.avi");
MCIWndCreate(this->GetSafeHwnd(),AfxGetInstanceHandle(),WS_CHILD|WS_CAPTION|WS_FISBLE|MCIWNDF_SHOWPOS|MCIWNDF_SHOWNAME,filename)
//pause
MCIWndPlay(m_videoWnd);
//play
MCIWndPause(m_videwWnd);
//destroy
MCIWndDestroy(m_videoWnd);

VC下使用OpenCV的环境设置

从http://www.opencv.org.cn 下载OpenCV安装程序。假如要将OpenCV安装到C:\Program Files\OpenCV。(下面附图为OpenCV 1.0rc1的安装界面,OpenCV 1.0安装界面与此基本一致。)在安装时选择"将\OpenCV\bin加入系统变量"(Add\OpenCV\bin to the systerm PATH)。

配置Windows环境变量

检查C:\Program Files\OpenCV\bin是否已经被加入到环境变量PATH,如果没有,请加入。加入后需要注销当前Windows用户(或重启)后重新登陆才生效。(可以在任务管理器里重启explorer.exe)

配置Visual C++ 6.0
全局设置
菜单Tools->Options->Directories:先设置lib路径,选择Library files,在下方填入路径:
C:\Program Files\OpenCV\lib
然后选择include files,在下方填入路径:
C:\Program Files\OpenCV\cxcore\include
C:\Program Files\OpenCV\cv\include
C:\Program Files\OpenCV\cvaux\include
C:\Program Files\OpenCV\ml\include
C:\Program Files\OpenCV\otherlibs\highgui
C:\Program Files\OpenCV\otherlibs\cvcam\include
项目设置
每创建一个将要使用OpenCV的VC Project,都需要给它指定需要的lib。菜单:Project->Settings,然后将Setting for选为All Configurations,然后选择右边的link标签,在Object/library modules附加上
cxcore.lib cv.lib ml.lib cvaux.lib highgui.lib cvcam.lib
如果你不需要这么多lib,你可以只添加你需要的lib。

VC CxImage的编译和使用

一。CxImage类库简介

CxImage类库是一 个优秀的图像操作类库。它可以快捷地存取、显示、转换各种图像。有的读者可能说,有那么多优秀的图形库,如OpenIL,FreeImage, PaintLib等等,它们可谓是功能强大,齐全,没必要用其它的类库。但我要说,这些类库基本上没有免费的,使用这些类库,你要被这样那样的许可协议所 束缚。在这点上,CxImage类库是完全免费的。另外,在使用上述类库时,你会遇到重重麻烦。因为它们大部分是平台无关的,且用C语言写成,有的还夹杂 着基本的C++ wrapper和成堆德编译选项的声明需要你去处理。而CxImage类库在这方面做得很好。还有让我最看好的,就是作者完全公开了源代码。相对于那些封 装好的图形库和GDI+来说,这一点使我们可以进一步学习各种编解码技术,而不再浮于各种技术的表面。

CxImage是一个可以用于MFC 的C++类,可以打开,保存,显示,转换各种格式的图像文件,比如BMP, JPEG, GIF, PNG, TIFF, MNG, ICO, PCX, TGA, WMF, WBMP, JBG, J2K 等格式的文件。可以实现BMP<->JPG,PNG <>TIFF格式等等的转换。

既可以实现图像文件的类型转换,也可以实现在内存图像数据的类型转换,并且使用很方便。

它的作者是: Davide Pizzolato ,主页: http://www.xdp.it/

首先,到http://www.codeproject.com/bitmap/CXImage.asp 下载它的源文件和Demo例子.

注: 在Codeproject下载这个类,你得先注册一下,因为这个类可是含金量比较高的,下载量比较大的,当然你也会很高兴成为CodeProject的一名成员的,她不收你的费.授权:

License

The class

CxImage

is free; as for the TIFF, JPEG, PNG and ZLIB libraries : "If you use this source code in a product, acknowledgment is not required but would be appreciated."

CxImage

is open source and licensed under the zlib license . In a nutshell, this means that you can use the code however you wish, as long as you don't claim it as your own.

由于很多人上codeproject的时候,老是上不去,所以,你也可以去这个类库的作者--Davide Pizzolato的主页去下载

他的主页还有另外的源代码,有兴趣的也可以看看.

二。编译CxImage类库

作者已经提供了整个类库配置的工程文件CxImgLib.dsw (VC++6.0),只要打开它进行编译即可。需要大家注意的是:整个CxImage类库非常大。如果你只需要能处理其中的几种格式,编译该类库时,你可以在配置的头文件ximcfg.h 中找到一些编译开关选项来关闭一些图像库。JPG、PNG、TIFF中的每一个库,都会向最终程序增加约100KB的内容。而CxImage类库压缩后只有约60KB。所以,你需要谨慎挑选一些你真正需要的类库。

//类库配置文件:ximacfg.h
#define CXIMAGE_SUPPORT_JPG 1
//如果要实现bmp->jpg 则必须打开
#define CXIMAGE_SUPPORT_BMP 1
#define CXIMAGE_SUPPORT_GIF 1
#define CXIMAGE_SUPPORT_JPG 1
//以上为必须打开,下面的随便............
#define CXIMAGE_SUPPORT_PNG 0//不使用它
#define CXIMAGE_SUPPORT_MNG 0
#define CXIMAGE_SUPPORT_ICO 1
#define CXIMAGE_SUPPORT_TIF 0//
#define CXIMAGE_SUPPORT_TGA 0//
#define CXIMAGE_SUPPORT_PCX 0//
#define CXIMAGE_SUPPORT_WBMP 0//
#define CXIMAGE_SUPPORT_WMF 0//
#define CXIMAGE_SUPPORT_J2K 0  // Beta, use JP2
#define CXIMAGE_SUPPORT_JBG 0
//.............

其他的可以不打开

编译该类库有好几个选择的工程,如下图所示:

各工程的作用对应如下:

  • CxImage : cximage.lib - static library
  • CxImageCrtDll : cximagecrt.dll - DLL not using mfc
  • CxImageMfcDll : cximage.dll - DLL using mfc
  • Demo : demo.exe - program linked with cximage.lib and the C libraries
  • DemoDll : demodll.exe - program linked with cximagecrt.dll
  • j2k,jasper,jbig,jpeg,png,tiff,zlib : static C libraries编译这些工程需要耗费几分钟的时间(中间文件可达60MB)。三。在程序中应用CxImage类库进行图像类型转换

    在你的VC工程中使用这个类库,要对工程进行如下设置(Project Settings):

    |- C/C++
    |   |- Code Generation
    |   |   |- Use run-time library : Multithreaded DLL (must be the same for
    |   |   |  all the linked libraries)  //应该只要是多线程DLL即可,DEBUG的也行
    |   |   |- Struct member alignment : must be the same for all the linked libraries
    |   |- Precompiled headers : not using precompiled headers
    |   |- Preprocessor
    |       |- Additional Include Directories:  ..\cximage(该处填CxImage里的.h和.cpp文件拷贝并导入工程后所在的文件夹,填写后在工程中include时编译器会查找该文件 夹,故include的文件无需路径)
    |- Link
    |- General
    |- Object/library modules:  png.lib
    jpeg.lib
    zlib.lib
    tiff.lib
    jasper.lib
    cximage.lib  (把需要的lib文件从CxImage中拷贝到工程中的lib文件所在的目录)

    并 且从CxImage中将xfile.h、ximacfg.h、ximadef.h、ximage.cpp、ximage.h、xiofile.h、 xmemfile.cpp、xmemfile.h拷贝到工程文件夹下并将CxImage.h文件加入工程中即可。也可以设置vc6的"tools"中的 "include"路径.

    下面介绍应用它进行图像类型转换的方式:

    1.从一种图像文件类型转换为另一种文件类型(convert from a format to another)

    CxImage  image;   // 定义一个CxImage对象

    // 从bmp文件转换为jpg文件(bmp -> jpg)
    image.Load("image.bmp", CXIMAGE_FORMAT_BMP);   //先装载bmp文件,需要指定文件类型
    // 判断加载的bmp文件是否存在。
    if (image.IsValid())......{
    // Returns true if the image has 256 colors  and a linear grey scale palette.
    if(!image.IsGrayScale()) image.IncreaseBpp(24);   // param nbit: 4, 8, 24
    image.SetJpegQuality(99);                // 设置图像的压缩质量参数(从0到100,数值越大,质量越高)
    image.Save("image.jpg",CXIMAGE_FORMAT_JPG);          // 把压缩后的图像以jpg文件类型保存起来。
    }

    // 从png文件转换为tif文件(png -> tif)
    image.Load("image.png", CXIMAGE_FORMAT_PNG);
    if (image.IsValid())...{
    image.Save("image.tif",CXIMAGE_FORMAT_TIF);
    }

    2。加载程序资源图像(load an image resource)

    即从程序的资源图像中构建CxImage对象,有如下几种方式:

    // Load the resource IDR_PNG1 from the PNG resource type
    CxImage* newImage = new CxImage();
    newImage->LoadResource(FindResource(NULL,MAKEINTRESOURCE(IDR_PNG1),
    "PNG"),CXIMAGE_FORMAT_PNG);

    或者

    //Load the resource IDR_JPG1 from DLL
    CxImage* newImage = new CxImage();
    HINSTANCE hdll=LoadLibrary("imagelib.dll");
    if (hdll)...{
    HRSRC hres=FindResource(hdll,MAKEINTRESOURCE(IDR_JPG1),"JPG");
    newImage->LoadResource(hres,CXIMAGE_FORMAT_JPG,hdll);
    FreeLibrary(hdll);
    }

    或者

    //Load a bitmap resource;
    HBITMAP bitmap = ::LoadBitmap(AfxGetInstanceHandle(),
    MAKEINTRESOURCE(IDB_BITMAP1)));
    CxImage *newImage = new CxImage();
    newImage->CreateFromHBITMAP(bitmap);

    3。在内存缓冲中的图像类型转换

    (1)把内存缓冲中的数据解码成一个Image对象(decode an image from memory)

    有如下几种方式:
    ------

    CxImage image((BYTE*)buffer,size,image_type);//把内存缓冲buffer中的数据构造成Image对象

    //或:

    CxMemFile memfile((BYTE*)buffer,size); // 显式使用CxMemFile对象
    CxImage image(&memfile,image_type);

    //或:

    CxMemFile memfile((BYTE*)buffer,size);
    CxImage* image = new CxImage();
    image->Decode(&memfile,type);

    ============

    (2)把Image编码存放到内存缓冲中(encode an image in memory)

    --------

    long size=0;//得到图像大小
    BYTE* buffer=0;//存储图像数据的缓冲
    image.Encode(buffer,size,image_type);//把image对象中的图像以type类型数据copy到buffer
    ...
    free(buffer);

    或:

    CxMemFile memfile;            // 显式使用CxMemFile对象
    memfile.Open();
    image.Encode(&memfile,image_type);
    BYTE* buffer = memfile.GetBuffer();
    long size = memfile.Size();
    ...
    free(buffer);

    ---------------

    4。处理系统粘贴板中的图像(copy/paste an image)

    //copy(到粘贴板)
    HANDLE hDIB = image->CopyToHandle();
    if (::OpenClipboard(AfxGetApp()->m_pMainWnd->GetSafeHwnd())) ...{
    if(::EmptyClipboard()) ...{
    if (::SetClipboardData(CF_DIB,hDIB) == NULL ) ...{
    AfxMessageBox( "Unable to set Clipboard data" );
    }    }    }
    CloseClipboard();

    //paste(从粘贴板粘贴出来)
    HANDLE hBitmap=NULL;
    CxImage *newima = new CxImage();
    if (OpenClipboard()) hBitmap=GetClipboardData(CF_DIB);
    if (hBitmap) newima->CreateFromHANDLE(hBitmap);
    CloseClipboard();

    5。在picture box中显示一个png格式的文件

  • 注意CXimage官方网站给出的方法如下,我测试了一下,没有成功(可能我用得是vc6.0,或者我哪里写错了?),我对这个例子简单的修改了一下,就可以显示图片了
  • 官网上的方法如下:
  • HBITMAP m_bitmap = NULL;
    CxImage image("myfile.png", CXIMAGE_FORMAT_PNG);
    ...
    m_bitmap = image.MakeBitmap(m_picture.GetDC()->m_hDC);
    m_picture.SetBitmap(m_bitmap);
    ...
    if (m_bitmap) DeleteObject(m_bitmap);
    我修改后的方法如下:
    picture control 控件我懒得用,用CStatic实现的

    CStatic  m_p;//该变量声明应放在文件最开头,弄成全局变量
    m_p.Create("",WS_VISIBLE|WS_BORDER|WS_CHILD,CRect(0,0,300,300),this,1234);
    CxImage image("1.bmp",CXIMAGE_FORMAT_BMP);
    HBITMAP mbitmap=image.MakeBitmap(m_p.GetDC()->m_hDC);
    CDC memdc;
    memdc.CreateDompatibleDC(m_p.GetDC());
    memdc.SelectObject(m_bitmap);
    m_p.GetDC()->BitBlt(0,0,300,300,&memdc,0,0,SRCCOPY);
    //记得释放资源

    四。其它

    一 个CxImage对象是一个扩展了的位图。作者只是在位图结构上添加了一些起存储信息作用的成员变量。一个CxImage对象(同时)也是一组层。每个层 只有在需要时才会分配相应的缓冲区。CxImage::pDib代表着背景图像,CxImage::pAlpha代表着透明层,CxImage:: pSelection代表着被选中的层,被用来创建图像处理时让用户感兴趣的区域。在这三个特殊层面的基础上,你可以增加一些额外的层,这些层可以存储在 CxImage::pLayers中。一般说来,层是一个完整的CxImage对象。因此,你可以构造很复杂的嵌套层。下面是CxImage的一些成员变 量:

    class CxImage
    ...{
    ...
    protected:
    void* pDib;            //包含文件头,调色板等等
    BITMAPINFOHEADER head; //标准的文件头(位图)
    CXIMAGEINFO info;      //扩展了的信息
    BYTE* pSelection;      //用户选中的区域
    BYTE* pAlpha;          //alpha通道
    CxImage** pLayers;     //通用层
    }
    typedef struct tagCxImageInfo ...{
    DWORD   dwEffWidth;       //DWORD 扫描线宽
    BYTE*   pImage;           //图像位数
    void*   pGhost;           //if this is a ghost, pGhost point to the body
    DWORD   dwType;           //原图像的格式
    char    szLastError[256]; //出错信息
    long    nProgress;        //监视循环的次数
    long    nEscape;          //跳出标志
    long    nBkgndIndex;      //GIF, PNG, MNG格式使用
    RGBQUAD nBkgndColor;      //RGB三原色透明度
    BYTE    nQuality;         //JPEG格式使用
    long    nFrame;           //TIF, GIF, MNG使用 :实际的帧数
    long    nNumFrames;       //TIF, GIF, MNG使用 :帧总数
    DWORD   dwFrameDelay;     //GIF, MNG使用
    long    xDPI;             //水平分辨率
    long    yDPI;             //垂直分辨率
    RECT    rSelectionBox;    //选中的矩形区
    BYTE    nAlphaMax;        //阴影的最大不透明度
    bool    bAlphaPaletteEnabled;  //如果调色板中有Alpha通道则为真
    bool    bEnabled;         //打开绘图函数
    long    xOffset;
    long    yOffset;
    DWORD   dwEncodeOption;   //一些编码选项
    RGBQUAD last_c;           //一些优化选项
    BYTE    last_c_index;
    bool    last_c_isvalid;
    long    nNumLayers;
    DWORD   dwFlags;
    } CXIMAGEINFO;

    关于CxImage类库作者: Davide Pizzolato,一位电子工程师。1984年开始编程,已不在乎使用何种编程语言来开发软件。现就职于Askoll的电子研发部。

    关于CxImage类库更多的信息,请到它的官网或作者主页去了解。

    VC下使用MatCom(类似于在VC下使用Matlab函数)

    Matcom是个小公司做的,这个公司用c把Matlab几乎所有的函数都编了一遍,不过这个公司已经不存在了,被收购了(貌似是matlab收购的)

    1.在Project\Add to project\选择\lib\v4500.lib或在工程设置中指定其路径。

    2.文件头写#include "matlib.h" 然后就可以用了
    3.       如何取出matcom矩阵的元素的值并交给C语言变量?

    例:

    double mydata[3][3];

    dMm(amatrix);

    amatrix=rand(3);

    for(int i=1;i<;=3;i++)

    for(int j=1;j<;=3;j++)

    mydata[i-1][j-1]=amatrix.r(i,j);

    //or  mydata[i-1][j-1]=amatrix.fastindex(i,j);

    也可以用memcpy() 把amatrix.pr指针的内容读出来,交给mydata,但行列需要转置。

    6.       使用了matcom4.5数学库的VC/C++Builder程序如何发布?

    在VC/CB中调用了matcom库,通过编译生成独立执行的程序,所以,不需要matlab系统,但一些必要的dll文件还是需要的,这些dll在MATCOM的安装目录下面,(在4.5版本中)大概有ago4500.dll,v4500v.dll等两个文件是必需的。

    7.       安装matcom在搜索matlab路径时说找不到matcom.m,怎么办?

    原因是matlab 6.x的Windows版本matlab.exe不在matlab\bin下,而在matlab\bin\win32,所以,在安装matcom时,把bin\win32\matlab.exe移动到bin\,即上移一层目录,安装matcom后,再移动回去就可以了。

    其实是否安装有matlab对matcom没有什么影响,你完全可以选择没有安装matlab,仍然可以编译大多数文件。需要matlab\toolbox下的文件时,用addpath()添加路径或者拷贝到当前目录下就可以了。

    VC MSComm控件 Com口编程

    瘟到死接管了所有的底层端口,要想对COM口进行操作,在VC下可以使用MSComm控件。

    MSComm控件的属性

    (必须要熟悉的几个——CommPort:设置并返回通信端口号;Setting:以字符串的形式设置并返回数据传输速率、奇偶校验、数据比特、停止比特;PortOpen:设置并返回通信端口的状态,也可以打开和关闭端口;Input:从接收缓冲区返回和删除字符;Output:向传输缓冲区定一个字符串)

    1)CommPort属性

    void SetCommPort(short nNewValue); short GetCommPort();
    这一属性设置并返回连接的串行端口号,Windows将会利用该串口和外界通信。在设计时,nNewValue可以设置成从1~16的任何数(默认值为1)。但是如果用PortOpen属性打开一个并不存在的端口时,MSComm控件会产生错误68(设备无效)。
    注意:必须在打开端口之前设置CommPort属性。

    2)Settings属性

    void Settings(LPCTSTR lpszNewValue); String GetSettings();
    该属性用于设置并返回数据传输速率、奇偶校验、数据比特、停止比特参数。当端口打开时,如果value非法,则MSComm控件产生错误380(非法属性值)。其中lpszNewValue用字符串表示,由四个设置值组成,有如下的组成格式:
    “BBBB,P,D,S”
    BBBB 为数据传输速率,P为奇偶校验,D为数据比特,S为停止比特。Value的默认值是:“9600,N,8,1”,数据传输速率合法值可以是110、 300、600、1200、2400、4800、9600、14400、19200、28800、38400、56000、57600、115200、 12800、25600。
    奇偶校验值可以是设置为下表的任一值。

    设定值描述
    E偶校验(EVEN)
    M标号校验(MARK)
    N无校验(NONE)
    O奇校验(ODD)
    S空格校验(SPACE)

    数据比特数可以是4、5、6、7、8。
    停止比特数可以是1、1.5、2。
    注意:只有当通信的双方的Settings属性值都一样时,通信连接才能生效。

    3)Handshaking属性

    这一属性用于设置或者返回硬件握手协议,也就是PC和通信设备之间为了控制流速而设定的内部协议。属性值可以设置为下表中的任一个。
    设定值值描述
    ComNone0默认值,无握手协议
    comXOnXOff 1XON/XOFF握手
    ComRTS 2RTS/CTS握手
    comRtsXOnXOff3 RTS/CTS和XON/XOFF握手皆可

    4)RThreshold属性

    void SetRThreshold(short nNewValue); short GetRThreshold();
    在MSComm控件设置CommEvent属性为comEvReceive并产生OnComm之前,RThreshold属性设置并返回的要接收的字符数。这里nNewValue是short型参数,说明在产生OnComm事件之前要接收的字符数。
    当接收字符后,若RThreshold属性设置为0(默认值)则不会产生OnComm事件。否则,该属性为一阈值,当接收缓冲区内字节个数达到或超过该值后就会产生OnComm事件,例如,设置RThreshold为1,接收缓冲区收到每一个字符都会使MSComm控件产生OnComm事件。

    5)CTSHolding属性
    void SetCTSHolding(BOOL bNewValue); BOOL GetCTSHolding();
    其属性设定值如下表所示。
    设定值功能描述
    TRUECTS线为高电平
    FALSECTS线为低电平

    该属性确定是否可通过查询CTS线的状态发送数据。CTS是调制解调器发送到相连计算机的信号,指示传输可以进行。该属性在设计时无效,在运行时为只读。

    如果CTS线为低电平(CTSHolding =
    FALSE)并且超时时,MSComm控件设置CommEvent属性为comEventCTSTO(Clear To Send
    Timeout)并产生OnComm事伯。CTS线用于RTS/CTS硬件握手。如果需要确定CTS线的状态,CTSHolding属性给出一种手工查询方法。

    6)SThreshold属性

    void SetSThreshold(short nNewValue); short GetSThreshold();
    MSComm控件设置CommEvent属性为comEvSend并产生OnComm事件之前,设置并返回传输缓冲区中允许的最小字符数。这里nNewValue是short型数据,代表在OnComm事件产生之前在传输缓冲区中的最小字符数。

    若设置Sthreshold属性为0(默认值),数据传输事件不会产生OnComm事件。若设置Sthreshold属性为0,当传输缓冲区完全空时,MSComm控件产生OnComm事件。如果在传输缓冲区的字符数小于value,CommEvent属性设置为comEvSend,并产生 OnComm事件。comEvSend事件仅当字符数与Sthreshold交叉时被激活一次。例如,如果Sthreshold等于5,仅当在输出队列中字符数从5降到4时,comEvSend才发生。如果在输出队列中从没有比Sthreshold多的字符,comEvSend事件将绝不会发生。

    7)InputMode属性
    void SetInputMode(long nNewValue); long GetInputMode();
    访属性用于设置或者返回传输数据的类型。其取值和基本含义如下表所示。

    设定值值描述
    ComInputModeText(缺省)0通过Input属性以文本方式取回数据
    ComInputModeBinary1通过Input属性以二进制方式取回数据

    8)InputLen属性

    void SetInputLen(short nNewValue); short GetInputLen ();
    该属性用于设置并返回Input属性从接收缓冲区读取的字符数。nNewValue是short型数值,说明Input属性从接收缓冲区中读取的字符数。

    InputLen属性的默认值是0。当设置InputLen为0时,使用Input将使MSComm控件读取接收缓冲区中全部的内容。若接收缓冲区中InputLen字符无效,Input属性返回一个零长度字符串(“”)。在使用Input前,用户可以选择检查InBufferCount属性来确定缓冲区中是否已有需要数目的字符。该属性在从输出格式为定长数据的机器读取数据时非常有用。

    9)InBuffersize属性

    void SetBufferSize(short nNewValue); short GetInBufferSize();
    InBuffersize属性用于设置或返回输入缓冲区的大小,默认值为1024字节。

    10)InBufferCount属性

    void SetInBufferCount(short nNewValue); short
    GetInBufferCount();
    InBufferCount属性用于返回输入缓冲区内的等待读取的字节个数,可以通过该属性值为0来清除接收缓冲区。

    11)Input属性

    VARIANT GetInput();
    Input属性表示从接收缓冲区移走一串字符,将缓冲区中收到的数据读入变量。属性值为VARIAN型变量。该属性在端口未打开时不可用,在运行时是只读的。

    注意:当InputMode属性值
    为0时(文本模式),变量中含String型数据。当InputMode属性值为1(二进制模式),变量中含Byte型数组数据。

    12)PortOpen属性

    void SetPortOpen(BOOL bNewValue); BOOL GetPortOpen();
    PortOpen属性用于打开或者关闭端口。如果bNewValue设为TRUE,可以打开端口;设为FALSE时可以关闭端口。
    一般情况下在程序开始时打开端口,在程序结束时关闭端口。当应用程序终止时,MSComm控件将自动关闭串
    口。在打开端口前,确定CommPort属性调协为正确的端口号。而且,用户的串口设备必须支持Settings属性中的设置。如果硬件设备不支持Settings属性中的一些设置,则硬件工作或许不正确。

    13)OutBuffersize属性

    void SetOutBufferSize(short nNewValue);
    OutBuffersize属性用于设置或者返回发送缓冲区的大小,值为整形表达式,表示传输缓冲区的字节数,默认值为512字节。对发送缓冲区设置得越大,应用程序可以使用的内存就越小。然而,如果发送缓冲区太小,缓冲区将会溢出,除非使用握手协议。

    14)OutBufferCounter属性

    void SetOutBufferSize(short nNewValue); short
    GetOutBufferSize();
    OutBufferCounter属性用于返回发送缓冲区内等待发送的字节数,可以通过设置该属性为0来清空发送缓冲区。

    15)Output属性
    void SetOutput(const VARIANT & newValue);
    Output属性用于向发送缓冲区写数据流。属性为VARIANT变量。该属性在端口未打开时不可用,在运行时只是写的。

    注意:Output属性可以发送文本数据或二进制数据。传输文本数据时,应该将字符型
    数据放入VARIANT变量中;传输二进制数据(即按字节传送)时,应将字节型数据放入VARIANT型变量中。如果通常给应用程序发送ANSI字符串,可以以文本方式发送。如果数据包含了内嵌控制字符、NULL字符等,必须将其作为二进制传递过去。

    16)CommEvent属性

    short GetCommEvent();
    如果在通信过程中发生错误或事件,将会引发OnComm事件并且改变其属性值。CommEvent属性值反映错误或者事件类型,通信的设计中可以根据该属性值执行同的***作。该属性在端口未打开时不可用,在运行时是只读的。通信错误及通信事件的设定值分别如下两表所示。

    通信错误设定值
    设定值值描述
    comEventBreak 1001接收到中断信号
    comEventCTSTO1002CTS超时
    comEventDSRTO1003DSR超时
    comEventFrame1004帧错误,硬件检测到一个帧出错,双方设置的格式不一致时,就会引发此错误
    comEventOverrun1006端口超速。一个字符没有在下一个字符到达之前被硬件读取,该字符丢失
    comEventCDTO 1007数据检测超时
    comEventRxOver1008接收缓冲区溢出
    comEventRxParity1009奇偶校验错误
    comEventTxFull1010传输缓冲区溢出,表明输出缓冲区已满,不能再将字符输出到缓冲
    comEventDCB1011检索端口、设备控制块(DCB)时的意外错误

    通信事件设定值
    设定值值描述
    comEvSend1发送事件。发送缓冲区的内容少于SThreshold指定的值
    comEvReceive2接收事件。接收缓冲区内字符数达到RThreshold值,该事件在缓冲区内数据被移走前将持续产生,利用此事件可编写接收数据的过程
    comEvCTS3CTS线变化
    comEvDSR4DSR线变化
    comEvCD5CD线变化
    comEvRing6振铃检测
    comEvEOF7文件结束。接收数据中出现文件结束(ASCII 码26)字符

    17)DTREnable属性

    void SetDTREnable(BOOL bNewValue); BOOL GetDTREnable();
    DTREnable属性确定在通信时是否使用DTR线有效,DTR是计算机发送到调制解调器的信号,表明计算机在等待数据传输。

    18)RTSEnable属性

    void SetRTSEnable(BOOL bNewValue); BOOL GetRTSEnable();
    RTSEnable属性确定是否使用RTS线有效。一般情况下,由计算机发送RTS信号到连接的调制解调器,请求允许发送数据。

    19)EOFEnable属性

    void SetEOFEnable(BOOL bNewValue); BOOL GetEOFEnable();
    EOFEnable属性确定在输入过程中MSComm控件是否寻找文件结尾(EOF)字符。如果找到EOF字符,将停止输入并激活OnComm事件,此时CommEvent属性设置为comEvEOF,这里bNewValue为布尔表达式,确定当找到EOF字符时,OnComm事件是否被激活。当bNewValue的设置值 TRUE时,EOF字符找到时OnComm事件被激活。否则当VALUE值设为
    FALSE(默认)时,EOF字符找到时OnComm事件不被激活。

    注意:当EOFEnable属性设置为FALSE时,OnComm控件将不在输入流中寻找EOF字符。

    20)CDHolding属性

    void SetCDHolding(BOOL bNewValue); BOOL GetCDHolding();
    通过查询CD线的状态确定当前是否有传输。CD是从调制解调器发送到相连计算机的一个信号,指示调制解调器正在联机。该属性在设计时无效,在运行时为只读。属性的设置值为:当 bNewValue为TRUE时,CD线为高电平;当bNewValue为FALSE时,CD线为低电平。注意当CD线为高电平(CDHolding= TRUE)且超时时,MSComm控件设置CommEvent属性为comEventCDTO(CD超时错误),并产生OnComm事件。

    注意:在主机应用程序中捕获一个丢失的传输是特别重要的,例如一个公告板,因为呼叫者可以随时挂起(放弃传输)。CD也被称为Receive
    Line Signal Detect(RLSD)。

    21)DSRHolding属性

    void SetDSRHolding(BOOL bNewValue); BOOL GetDSRHolding();
    确定DSR线的状态。DSR信号由调制解调器发送到相连计算机,指示作好***作准备。该属性在设计时无效,在运行时为只读。DSRHolding属性返回为TRUE时,表示DSR线高,返回FALSE时,表示DSR线低。当DSR线为高电平时(DSRHolding=TRUE)超时时,MSComm控件设置 CommEvent属性为comEventDSRTO(数据准备超时)并产生OnComm事件。当为DTE(Data
    Terminal Equipment)机器写DSR/DTR握手例程时该属性是分有用的。

    VC opengl输出文本的方法

    HDC    hdc;
    HGLRC  hglrc;

    // create a rendering context
    hglrc = wglCreateContext (hdc);

    // make it the calling thread's current rendering context
    wglMakeCurrent (hdc, hglrc);

    // now we can call OpenGL API

    // make the system font the device context's selected font
    SelectObject (hdc, GetStockObject (SYSTEM_FONT));

    // create the bitmap display lists
    // we're making images of glyphs 0 thru 255
    // the display list numbering starts at 1000, an arbitrary choice
    wglUseFontBitmaps (hdc, 0, 255, 1000);

    // display a string:
    // indicate start of glyph display lists
    glListBase (1000);
    // now draw the characters in a string
    glCallLists (24, GL_UNSIGNED_BYTE, "Hello Win32 OpenGL World");
    //可以用glRasterPos控制字符位置,但一定要在glRasterPos之前设置颜色

    VC Opengl读取.3ds文件的函数

    这个方法只支持bmp纹理理.3ds文件,值得注意的是OpenGL的坐标系和3DS Max的坐标系是不同的,3D Studio Max中的模型的Z轴是指向上的,而OpenGL中模型的Z轴是垂直屏幕指向用户的,因此需要将顶点的坐标的y和z翻转过来。

    ////////////////////////////////////////////////////////////////////////////////////////

    //头文件
    #ifndef _3DS_H
    #define _3DS_H
    #include <math.h>
    #include <vector>
    //  基本块(Primary Chunk),位于文件的开始
    #define PRIMARY       0x4D4D
    //  主块(Main Chunks)
    #define OBJECTINFO    0x3D3D                // 网格对象的版本号
    #define VERSION       0x0002                // .3ds文件的版本
    #define EDITKEYFRAME  0xB000                // 所有关键帧信息的头部
    //  对象的次级定义(包括对象的材质和对象)
    #define MATERIAL          0xAFFF                // 保存纹理信息
    #define OBJECT                  0x4000                // 保存对象的面、顶点等信息
    //  材质的次级定义
    #define MATNAME       0xA000                // 保存材质名称
    #define MATDIFFUSE    0xA020                // 对象/材质的颜色
    #define MATMAP        0xA200                // 新材质的头部
    #define MATMAPFILE    0xA300                // 保存纹理的文件名
    #define OBJ_MESH          0x4100                // 新的网格对象
    #define MAX_TEXTURES  100                        // 最大的纹理数目
    //  OBJ_MESH的次级定义
    #define OBJ_VERTICES  0x4110                // 对象顶点
    #define OBJ_FACES          0x4120                // 对象的面
    #define OBJ_MATERIAL  0x4130                // 对象的材质
    #define OBJ_UV                  0x4140                // 对象的UV纹理坐标

    using namespace std;
    class CVector3                //定义3D点的类,用于保存模型中的顶点
    {public:        float x, y, z;
    };
    class CVector2                //定义2D点类,用于保存模型的UV纹理坐标
    {public:        float x, y;
    };
    struct tFace                //面的结构定义
    {        int vertIndex[3];                        // 顶点索引
    int coordIndex[3];                        // 纹理坐标索引
    };
    struct tMatInfo//材质信息结构体
    {        char  strName[255];                        // 纹理名称
    char  strFile[255];                        // 如果存在纹理映射,则表示纹理文件名称
    BYTE  color[3];                                // 对象的RGB颜色
    int   texureId;                                // 纹理ID
    float uTile;                                // u 重复
    float vTile;                                // v 重复
    float uOffset;                            // u 纹理偏移
    float vOffset;                                // v 纹理偏移
    } ;
    struct t3DObject        //对象信息结构体
    {        int  numOfVerts;                        // 模型中顶点的数目
    int  numOfFaces;                        // 模型中面的数目
    int  numTexVertex;                        // 模型中纹理坐标的数目
    int  materialID;                        // 纹理ID
    bool bHasTexture;                        // 是否具有纹理映射
    char strName[255];                        // 对象的名称
    CVector3  *pVerts;                        // 对象的顶点
    CVector3  *pNormals;                // 对象的法向量
    CVector2  *pTexVerts;                // 纹理UV坐标
    tFace *pFaces;                                // 对象的面信息
    };
    struct t3DModel        //模型信息结构体
    {        int numOfObjects;                        // 模型中对象的数目
    int numOfMaterials;                        // 模型中材质的数目
    vector<tMatInfo>pMaterials;        // 材质链表信息
    vector<t3DObject> pObject;        // 模型中对象链表信息
    };
    struct tChunk        //保存块信息的结构
    {        unsigned short int ID;                // 块的ID
    unsigned int length;                // 块的长度
    unsigned int bytesRead;                // 需要读的块数据的字节数
    };
    //////////////////////////////////////////////////////////////////////////
    class CLoad3DS// CLoad3DS类处理所有的装入代码
    {
    public:
    CLoad3DS();                                                                // 初始化数据成员
    virtual ~CLoad3DS();
    void show3ds(int j0,float tx,float ty,float tz,float size);//显示3ds模型
    void Init(char *filename,int j);
    private:
    bool Import3DS(t3DModel *pModel, char *strFileName);// 装入3ds文件到模型结构中
    void CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID);//  从文件中创建纹理
    int  GetString(char *);                                                                // 读一个字符串
    void ReadChunk(tChunk *);                                                        // 读下一个块
    void ReadNextChunk(t3DModel *pModel, tChunk *);                // 读下一个块
    void ReadNextObjChunk(t3DModel *pModel,t3DObject *pObject,tChunk *);// 读下一个对象块
    void ReadNextMatChunk(t3DModel *pModel, tChunk *);        // 读下一个材质块
    void ReadColor(tMatInfo *pMaterial, tChunk *pChunk);// 读对象颜色的RGB值
    void ReadVertices(t3DObject *pObject, tChunk *);        // 读对象的顶点
    void ReadVertexIndices(t3DObject *pObject,tChunk *);// 读对象的面信息
    void ReadUVCoordinates(t3DObject *pObject,tChunk *);// 读对象的纹理坐标
    void ReadObjMat(t3DModel *pModel,t3DObject *pObject,tChunk *pPreChunk);// 读赋予对象的材质名称
    void ComputeNormals(t3DModel *pModel);                                // 计算对象顶点的法向量
    void CleanUp();                                                                                // 关闭文件,释放内存空间
    FILE        *m_FilePointer;                                                                // 文件指针
    tChunk        *m_CurrentChunk;
    tChunk        *m_TempChunk;
    };
    #endif
    //////////////////////////////////////////////////////////////////////////////////////////////////////

    //cpp文件
    #include "stdafx.h"
    #include "3DS.H"
    #include<gl/gl.h>
    #include<gl/glu.h>
    #include<gl/glaux.h>

    UINT g_Texture[10][MAX_TEXTURES] = {0};
    t3DModel g_3DModel[10];

    int   g_ViewMode          = GL_TRIANGLES;
    bool  g_bLighting     = true;

    CLoad3DS::CLoad3DS()//  构造函数的功能是初始化tChunk数据
    {
    m_CurrentChunk = new tChunk;        // 初始化并为当前的块分配空间
    m_TempChunk = new tChunk;                // 初始化一个临时块并分配空间
    }
    CLoad3DS::~CLoad3DS()
    {
    CleanUp();// 释放内存空间
    for(int j = 0; j <10;j++)
    for(int i = 0; i < g_3DModel[j].numOfObjects; i++)
    {
    delete [] g_3DModel[j].pObject[i].pFaces;// 删除所有的变量
    delete [] g_3DModel[j].pObject[i].pNormals;
    delete [] g_3DModel[j].pObject[i].pVerts;
    delete [] g_3DModel[j].pObject[i].pTexVerts;
    }
    }
    ////////////////////////////////////////////////////////////////////////
    void CLoad3DS::Init(char *filename,int j)//
    {
    Import3DS(&g_3DModel[j], filename);                        // 将3ds文件装入到模型结构体中
    for(int i =0; i<g_3DModel[j].numOfMaterials;i++)
    {
    if(strlen(g_3DModel[j].pMaterials[i].strFile)>0)// 判断是否是一个文件名
    CreateTexture(g_Texture[j], g_3DModel[j].pMaterials[i].strFile, i);//使用纹理文件名称来装入位图
    g_3DModel[j].pMaterials[i].texureId = i;// 设置材质的纹理ID
    }
    }
    //  从文件中创建纹理
    void CLoad3DS::CreateTexture(UINT textureArray[], LPSTR strFileName, int textureID)
    {
    AUX_RGBImageRec *pBitmap = NULL;
    if(!strFileName) return;                                                        // 如果无此文件,则直接返回
    pBitmap = auxDIBImageLoad(strFileName);                                // 装入位图,并保存数据
    if(pBitmap == NULL)                exit(0);                                        // 如果装入位图失败,则退出
    // 生成纹理
    glGenTextures(1, &textureArray[textureID]);
    // 设置像素对齐格式
    glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
    glBindTexture(GL_TEXTURE_2D, textureArray[textureID]);
    gluBuild2DMipmaps(GL_TEXTURE_2D, 3, pBitmap->sizeX, pBitmap->sizeY, GL_RGB, GL_UNSIGNED_BYTE, pBitmap->data);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR_MIPMAP_LINEAR);
    if (pBitmap)                                                                                // 释放位图占用的资源
    {        if (pBitmap->data)        free(pBitmap->data);
    free(pBitmap);
    }
    }
    void CLoad3DS::show3ds(int j0,float tx,float ty,float tz,float size) //显示3ds模型
    {
    glPushAttrib(GL_CURRENT_BIT);//保存现有颜色属实性
    glPushMatrix();
    glDisable(GL_TEXTURE_2D);
    ::glTranslatef( tx, ty, tz);
    ::glScaled(size,size,size);
    glRotatef(90, 0, 1.0f, 0);
    // 遍历模型中所有的对象
    for(int i = 0; i < g_3DModel[j0].numOfObjects; i++)
    {if(g_3DModel[j0].pObject.size() <= 0) break;// 如果对象的大小小于0,则退出
    t3DObject *pObject = &g_3DModel[j0].pObject[i];// 获得当前显示的对象
    if(pObject->bHasTexture)// 判断该对象是否有纹理映射
    {        glEnable(GL_TEXTURE_2D);// 打开纹理映射
    glBindTexture(GL_TEXTURE_2D, g_Texture[j0][pObject->materialID]);
    }
    else        glDisable(GL_TEXTURE_2D);// 关闭纹理映射
    glColor3ub(255, 255, 255);

    glBegin(g_ViewMode);//开始以g_ViewMode模式绘制
    for(int j = 0; j < pObject->numOfFaces; j++)                // 遍历所有的面
    {for(int tex = 0; tex < 3; tex++)                                        // 遍历三角形的所有点
    {int index = pObject->pFaces[j].vertIndex$$;        // 获得面对每个点的索引
    glNormal3f(pObject->pNormals[index].x,pObject->pNormals[index].y,
    pObject->pNormals[index].z);                // 给出法向量
    if(pObject->bHasTexture)                                                // 如果对象具有纹理
    {        if(pObject->pTexVerts)                                                // 确定是否有UVW纹理坐标
    glTexCoord2f(pObject->pTexVerts[index].x,pObject->pTexVerts[index].y);
    }
    else
    {        if(g_3DModel[j0].pMaterials.size() && pObject->materialID>= 0)
    {        BYTE *pColor = g_3DModel[j0].pMaterials[pObject->materialID].color;
    glColor3ub(pColor[0],pColor[1],pColor[2]);
    }
    }
    glVertex3f(pObject->pVerts[index].x,pObject->pVerts[index].y,pObject->pVerts[index].z);
    }
    }
    glEnd();// 绘制结束
    }
    glEnable(GL_TEXTURE_2D);
    glPopMatrix();
    glPopAttrib();//恢复前一属性
    }
    //////////////////////////////////////////////////////////////////
    //  打开一个3ds文件,读出其中的内容,并释放内存
    bool CLoad3DS::Import3DS(t3DModel *pModel, char *strFileName)
    {        char strMessage[255] = {0};
    // 打开一个3ds文件
    m_FilePointer = fopen(strFileName, "rb");
    // 确保所获得的文件指针合法
    if(!m_FilePointer)
    {        sprintf(strMessage, "Unable to find the file: %s!", strFileName);
    MessageBox(NULL, strMessage, "Error", MB_OK);
    return false;
    }
    // 当文件打开之后,首先应该将文件最开始的数据块读出以判断是否是一个3ds文件
    // 如果是3ds文件的话,第一个块ID应该是PRIMARY
    // 将文件的第一块读出并判断是否是3ds文件
    ReadChunk(m_CurrentChunk);
    // 确保是3ds文件
    if (m_CurrentChunk->ID != PRIMARY)
    {        sprintf(strMessage, "Unable to load PRIMARY chuck from file: %s!", strFileName);
    MessageBox(NULL, strMessage, "Error", MB_OK);
    return false;
    }
    // 现在开始读入数据,ReadNextChunk()是一个递归函数
    // 通过调用下面的递归函数,将对象读出
    ReadNextChunk(pModel, m_CurrentChunk);
    // 在读完整个3ds文件之后,计算顶点的法线
    ComputeNormals(pModel);
    // 释放内存空间
    CleanUp();
    return true;
    }
    //  下面的函数释放所有的内存空间,并关闭文件
    void CLoad3DS::CleanUp()
    {        // 遍历场景中所有的对象
    fclose(m_FilePointer);                                                // 关闭当前的文件指针
    delete m_CurrentChunk;                                                // 释放当前块
    delete m_TempChunk;                                                        // 释放临时块
    }

    //  下面的函数读出3ds文件的主要部分
    void CLoad3DS::ReadNextChunk(t3DModel *pModel, tChunk *pPreChunk)
    {
    t3DObject newObject = {0};                                        // 用来添加到对象链表
    tMatInfo newTexture = {0};                                // 用来添加到材质链表
    unsigned int version = 0;                                        // 保存文件版本
    int buffer[50000] = {0};                                        // 用来跳过不需要的数据
    m_CurrentChunk = new tChunk;                                // 为新的块分配空间
    //  下面每读一个新块,都要判断一下块的ID,如果该块是需要的读入的,则继续进行
    //  如果是不需要读入的块,则略过
    // 继续读入子块,直到达到预定的长度
    while (pPreChunk->bytesRead < pPreChunk->length)
    {        // 读入下一个块
    ReadChunk(m_CurrentChunk);
    // 判断块的ID号
    switch (m_CurrentChunk->ID)
    {
    case VERSION:                                                        // 文件版本号
    // 在该块中有一个无符号短整型数保存了文件的版本
    // 读入文件的版本号,并将字节数添加到bytesRead变量中
    m_CurrentChunk->bytesRead += fread(&version, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    // 如果文件版本号大于3,给出一个警告信息
    if (version > 0x03)
    MessageBox(NULL, "This 3DS file is over version 3 so it may load incorrectly", "Warning", MB_OK);
    break;
    case OBJECTINFO:                                                // 网格版本信息
    // 读入下一个块
    ReadChunk(m_TempChunk);
    // 获得网格的版本号
    m_TempChunk->bytesRead += fread(&version, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
    // 增加读入的字节数
    m_CurrentChunk->bytesRead += m_TempChunk->bytesRead;
    // 进入下一个块
    ReadNextChunk(pModel, m_CurrentChunk);
    break;
    case MATERIAL:                                                        // 材质信息
    // 材质的数目递增
    pModel->numOfMaterials++;
    // 在纹理链表中添加一个空白纹理结构
    pModel->pMaterials.push_back(newTexture);
    // 进入材质装入函数
    ReadNextMatChunk(pModel, m_CurrentChunk);
    break;
    case OBJECT:                                                        // 对象的名称
    // 该块是对象信息块的头部,保存了对象了名称
    // 对象数递增
    pModel->numOfObjects++;
    // 添加一个新的tObject节点到对象链表中
    pModel->pObject.push_back(newObject);
    // 初始化对象和它的所有数据成员
    memset(&(pModel->pObject[pModel->numOfObjects - 1]), 0, sizeof(t3DObject));
    // 获得并保存对象的名称,然后增加读入的字节数
    m_CurrentChunk->bytesRead += GetString(pModel->pObject[pModel->numOfObjects - 1].strName);
    // 进入其余的对象信息的读入
    ReadNextObjChunk(pModel, &(pModel->pObject[pModel->numOfObjects - 1]), m_CurrentChunk);
    break;
    case EDITKEYFRAME:
    // 跳过关键帧块的读入,增加需要读入的字节数
    m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    default:
    //  跳过所有忽略的块的内容的读入,增加需要读入的字节数
    m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    }
    // 增加从最后块读入的字节数
    pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    }
    // 释放当前块的内存空间
    delete m_CurrentChunk;
    m_CurrentChunk = pPreChunk;
    }
    //  下面的函数处理所有的文件中对象的信息
    void CLoad3DS::ReadNextObjChunk(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
    {        int buffer[50000] = {0};                                        // 用于读入不需要的数据
    // 对新的块分配存储空间
    m_CurrentChunk = new tChunk;
    // 继续读入块的内容直至本子块结束
    while (pPreChunk->bytesRead < pPreChunk->length)
    {        // 读入下一个块
    ReadChunk(m_CurrentChunk);
    // 区别读入是哪种块
    switch (m_CurrentChunk->ID)
    {
    case OBJ_MESH:                                        // 正读入的是一个新块
    // 使用递归函数调用,处理该新块
    ReadNextObjChunk(pModel, pObject, m_CurrentChunk);
    break;
    case OBJ_VERTICES:                                // 读入是对象顶点
    ReadVertices(pObject, m_CurrentChunk);
    break;
    case OBJ_FACES:                                        // 读入的是对象的面
    ReadVertexIndices(pObject, m_CurrentChunk);
    break;
    case OBJ_MATERIAL:                                // 读入的是对象的材质名称
    // 该块保存了对象材质的名称,可能是一个颜色,也可能是一个纹理映射。同时在该块中也保存了
    // 纹理对象所赋予的面
    // 下面读入对象的材质名称
    ReadObjMat(pModel, pObject, m_CurrentChunk);
    break;
    case OBJ_UV:                                                // 读入对象的UV纹理坐标
    // 读入对象的UV纹理坐标
    ReadUVCoordinates(pObject, m_CurrentChunk);
    break;
    default:
    // 略过不需要读入的块
    m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    }
    // 添加从最后块中读入的字节数到前面的读入的字节中
    pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    }
    // 释放当前块的内存空间,并把当前块设置为前面块
    delete m_CurrentChunk;
    m_CurrentChunk = pPreChunk;
    }
    //  下面的函数处理所有的材质信息
    void CLoad3DS::ReadNextMatChunk(t3DModel *pModel, tChunk *pPreChunk)
    {        int buffer[50000] = {0};                                        // 用于读入不需要的数据
    // 给当前块分配存储空间
    m_CurrentChunk = new tChunk;
    // 继续读入这些块,知道该子块结束
    while (pPreChunk->bytesRead < pPreChunk->length)
    {        // 读入下一块
    ReadChunk(m_CurrentChunk);
    // 判断读入的是什么块
    switch (m_CurrentChunk->ID)
    {
    case MATNAME:                                                        // 材质的名称
    // 读入材质的名称
    m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strName, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    case MATDIFFUSE:                                                // 对象的R G B颜色
    ReadColor(&(pModel->pMaterials[pModel->numOfMaterials - 1]), m_CurrentChunk);
    break;
    case MATMAP:                                                        // 纹理信息的头部
    // 进入下一个材质块信息
    ReadNextMatChunk(pModel, m_CurrentChunk);
    break;
    case MATMAPFILE:                                                // 材质文件的名称
    // 读入材质的文件名称
    m_CurrentChunk->bytesRead += fread(pModel->pMaterials[pModel->numOfMaterials - 1].strFile, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    default:
    // 掠过不需要读入的块
    m_CurrentChunk->bytesRead += fread(buffer, 1, m_CurrentChunk->length - m_CurrentChunk->bytesRead, m_FilePointer);
    break;
    }
    // 添加从最后块中读入的字节数
    pPreChunk->bytesRead += m_CurrentChunk->bytesRead;
    }
    // 删除当前块,并将当前块设置为前面的块
    delete m_CurrentChunk;
    m_CurrentChunk = pPreChunk;
    }
    //  下面函数读入块的ID号和它的字节长度
    void CLoad3DS::ReadChunk(tChunk *pChunk)
    {        // 读入块的ID号,占用了2个字节。块的ID号象OBJECT或MATERIAL一样,说明了在块中所包含的内容
    pChunk->bytesRead = fread(&pChunk->ID, 1, 2, m_FilePointer);
    // 然后读入块占用的长度,包含了四个字节
    pChunk->bytesRead += fread(&pChunk->length, 1, 4, m_FilePointer);
    }
    //  下面的函数读入一个字符串
    int CLoad3DS::GetString(char *pBuffer)
    {        int index = 0;
    // 读入一个字节的数据
    fread(pBuffer, 1, 1, m_FilePointer);
    // 直到结束
    while (*(pBuffer + index++) != 0) {
    // 读入一个字符直到NULL
    fread(pBuffer + index, 1, 1, m_FilePointer);
    }
    // 返回字符串的长度
    return strlen(pBuffer) + 1;
    }
    //  下面的函数读入RGB颜色
    void CLoad3DS::ReadColor(tMatInfo *pMaterial, tChunk *pChunk)
    {        // 读入颜色块信息
    ReadChunk(m_TempChunk);
    // 读入RGB颜色
    m_TempChunk->bytesRead += fread(pMaterial->color, 1, m_TempChunk->length - m_TempChunk->bytesRead, m_FilePointer);
    // 增加读入的字节数
    pChunk->bytesRead += m_TempChunk->bytesRead;
    }
    //  下面的函数读入顶点索引
    void CLoad3DS::ReadVertexIndices(t3DObject *pObject, tChunk *pPreChunk)
    {        unsigned short index = 0;                                        // 用于读入当前面的索引
    // 读入该对象中面的数目
    pPreChunk->bytesRead += fread(&pObject->numOfFaces, 1, 2, m_FilePointer);
    // 分配所有面的存储空间,并初始化结构
    pObject->pFaces = new tFace [pObject->numOfFaces];
    memset(pObject->pFaces, 0, sizeof(tFace) * pObject->numOfFaces);
    // 遍历对象中所有的面
    for(int i = 0; i < pObject->numOfFaces; i++)
    {        for(int j = 0; j < 4; j++)
    {        // 读入当前面的第一个点
    pPreChunk->bytesRead += fread(&index, 1, sizeof(index), m_FilePointer);
    if(j < 3)
    {        // 将索引保存在面的结构中
    pObject->pFaces[i].vertIndex[j] = index;
    }
    }
    }
    }
    //  下面的函数读入对象的UV坐标
    void CLoad3DS::ReadUVCoordinates(t3DObject *pObject, tChunk *pPreChunk)
    {        // 为了读入对象的UV坐标,首先需要读入UV坐标的数量,然后才读入具体的数据
    // 读入UV坐标的数量
    pPreChunk->bytesRead += fread(&pObject->numTexVertex, 1, 2, m_FilePointer);
    // 分配保存UV坐标的内存空间
    pObject->pTexVerts = new CVector2 [pObject->numTexVertex];
    // 读入纹理坐标
    pPreChunk->bytesRead += fread(pObject->pTexVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    }
    //  读入对象的顶点
    void CLoad3DS::ReadVertices(t3DObject *pObject, tChunk *pPreChunk)
    {        // 在读入实际的顶点之前,首先必须确定需要读入多少个顶点。
    // 读入顶点的数目
    pPreChunk->bytesRead += fread(&(pObject->numOfVerts), 1, 2, m_FilePointer);
    // 分配顶点的存储空间,然后初始化结构体
    pObject->pVerts = new CVector3 [pObject->numOfVerts];
    memset(pObject->pVerts, 0, sizeof(CVector3) * pObject->numOfVerts);
    // 读入顶点序列
    pPreChunk->bytesRead += fread(pObject->pVerts, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    // 现在已经读入了所有的顶点。
    // 因为3D Studio Max的模型的Z轴是指向上的,因此需要将y轴和z轴翻转过来。
    // 具体的做法是将Y轴和Z轴交换,然后将Z轴反向。
    // 遍历所有的顶点
    for(int i = 0; i < pObject->numOfVerts; i++)
    {        // 保存Y轴的值
    float fTempY = pObject->pVerts[i].y;
    // 设置Y轴的值等于Z轴的值
    pObject->pVerts[i].y = pObject->pVerts[i].z;
    // 设置Z轴的值等于-Y轴的值
    pObject->pVerts[i].z = -fTempY;
    }
    }
    //  下面的函数读入对象的材质名称
    void CLoad3DS::ReadObjMat(t3DModel *pModel, t3DObject *pObject, tChunk *pPreChunk)
    {        char strMaterial[255] = {0};                        // 用来保存对象的材质名称
    int buffer[50000] = {0};                                // 用来读入不需要的数据
    // 材质或者是颜色,或者是对象的纹理,也可能保存了象明亮度、发光度等信息。
    // 下面读入赋予当前对象的材质名称
    pPreChunk->bytesRead += GetString(strMaterial);
    // 遍历所有的纹理
    for(int i = 0; i < pModel->numOfMaterials; i++)
    {        //如果读入的纹理与当前的纹理名称匹配
    if(strcmp(strMaterial, pModel->pMaterials[i].strName) == 0)
    {        // 设置材质ID
    pObject->materialID = i;
    // 判断是否是纹理映射,如果strFile是一个长度大于1的字符串,则是纹理
    if(strlen(pModel->pMaterials[i].strFile) > 0) {
    // 设置对象的纹理映射标志
    pObject->bHasTexture = true;
    }
    break;
    }
    else
    {        // 如果该对象没有材质,则设置ID为-1
    pObject->materialID = -1;
    }
    }
    pPreChunk->bytesRead += fread(buffer, 1, pPreChunk->length - pPreChunk->bytesRead, m_FilePointer);
    }
    //  下面的这些函数主要用来计算顶点的法向量,顶点的法向量主要用来计算光照
    // 下面的宏定义计算一个矢量的长度
    #define Mag(Normal) (sqrt(Normal.x*Normal.x + Normal.y*Normal.y + Normal.z*Normal.z))
    // 下面的函数求两点决定的矢量
    CVector3 Vector(CVector3 vPoint1, CVector3 vPoint2)
    {        CVector3 vVector;
    vVector.x = vPoint1.x - vPoint2.x;
    vVector.y = vPoint1.y - vPoint2.y;
    vVector.z = vPoint1.z - vPoint2.z;
    return vVector;
    }
    // 下面的函数两个矢量相加
    CVector3 AddVector(CVector3 vVector1, CVector3 vVector2)
    {        CVector3 vResult;
    vResult.x = vVector2.x + vVector1.x;
    vResult.y = vVector2.y + vVector1.y;
    vResult.z = vVector2.z + vVector1.z;
    return vResult;
    }
    // 下面的函数处理矢量的缩放
    CVector3 DivideVectorByScaler(CVector3 vVector1, float Scaler)
    {        CVector3 vResult;
    vResult.x = vVector1.x / Scaler;
    vResult.y = vVector1.y / Scaler;
    vResult.z = vVector1.z / Scaler;
    return vResult;
    }
    // 下面的函数返回两个矢量的叉积
    CVector3 Cross(CVector3 vVector1, CVector3 vVector2)
    {        CVector3 vCross;
    vCross.x = ((vVector1.y * vVector2.z) - (vVector1.z * vVector2.y));
    vCross.y = ((vVector1.z * vVector2.x) - (vVector1.x * vVector2.z));
    vCross.z = ((vVector1.x * vVector2.y) - (vVector1.y * vVector2.x));
    return vCross;
    }
    // 下面的函数规范化矢量
    CVector3 Normalize(CVector3 vNormal)
    {        double Magnitude;
    Magnitude = Mag(vNormal);                                        // 获得矢量的长度
    vNormal.x /= (float)Magnitude;
    vNormal.y /= (float)Magnitude;
    vNormal.z /= (float)Magnitude;
    return vNormal;
    }
    //  下面的函数用于计算对象的法向量
    void CLoad3DS::ComputeNormals(t3DModel *pModel)
    {        CVector3 vVector1, vVector2, vNormal, vPoly[3];
    // 如果模型中没有对象,则返回
    if(pModel->numOfObjects <= 0)
    return;
    // 遍历模型中所有的对象
    for(int index = 0; index < pModel->numOfObjects; index++)
    {        // 获得当前的对象
    t3DObject *pObject = &(pModel->pObject[index]);
    // 分配需要的存储空间
    CVector3 *pNormals                = new CVector3 [pObject->numOfFaces];
    CVector3 *pTempNormals        = new CVector3 [pObject->numOfFaces];
    pObject->pNormals                = new CVector3 [pObject->numOfVerts];
    // 遍历对象的所有面
    for(int i=0; i < pObject->numOfFaces; i++)
    {        vPoly[0] = pObject->pVerts[pObject->pFaces[i].vertIndex[0]];
    vPoly[1] = pObject->pVerts[pObject->pFaces[i].vertIndex[1]];
    vPoly[2] = pObject->pVerts[pObject->pFaces[i].vertIndex[2]];
    // 计算面的法向量
    vVector1 = Vector(vPoly[0], vPoly[2]);                // 获得多边形的矢量
    vVector2 = Vector(vPoly[2], vPoly[1]);                // 获得多边形的第二个矢量
    vNormal  = Cross(vVector1, vVector2);                // 获得两个矢量的叉积
    pTempNormals[i] = vNormal;                                        // 保存非规范化法向量
    vNormal  = Normalize(vNormal);                                // 规范化获得的叉积
    pNormals[i] = vNormal;                                                // 将法向量添加到法向量列表中
    }
    //  下面求顶点法向量
    CVector3 vSum = {0.0, 0.0, 0.0};
    CVector3 vZero = vSum;
    int shared=0;
    // 遍历所有的顶点
    for (i = 0; i < pObject->numOfVerts; i++)
    {        for (int j = 0; j < pObject->numOfFaces; j++)        // 遍历所有的三角形面
    {                                                                                                // 判断该点是否与其它的面共享
    if (pObject->pFaces[j].vertIndex[0] == i ||
    pObject->pFaces[j].vertIndex[1] == i ||
    pObject->pFaces[j].vertIndex[2] == i)
    {        vSum = AddVector(vSum, pTempNormals[j]);
    shared++;
    }
    }
    pObject->pNormals[i] = DivideVectorByScaler(vSum, float(-shared));
    // 规范化最后的顶点法向
    pObject->pNormals[i] = Normalize(pObject->pNormals[i]);
    vSum = vZero;
    shared = 0;
    }
    // 释放存储空间,开始下一个对象
    delete [] pTempNormals;
    delete [] pNormals;
    }
    }

    VC Opengl获取显卡信息

    OpenGL显卡编程

    文档简述:
    随着显卡的飞速发展,更快的速度以及越来越多的新功能为硬件所支持,硬件的进步使得图形程序开发人员可以创造出更加绚丽的视觉效果,现在,电影级动画的实时渲染已不再是梦想。我们怎么在OpenGL中利用显卡的新特性呢?答案就是OpenGL扩展。
    注:如不作特别说明,本站文章中的显卡均指面向普通用户的非专业显卡。

    文档目录:
    OpenGL扩展
    显卡差异
    顶点/片断编程
    Cg/RenderMonkey/及其他

    文档内容:

    OpenGL扩展(OpenGL Extensions)

    OpenGL和Direct3D比较起来,最大的一个长处就是其扩展机制。硬件厂商开发出一个新功能,可以针对新功能开发OpenGL扩展,软件开发人员通过这个扩展就可以使用新的硬件功能。所以虽然显卡的发展速度比OpenGL版本更新速度快得多,但程序员仍然可以通过OpenGL使用最新的硬件功能。而Direct3D则没有扩展机制,硬件的新功能要等到微软发布新版DirectX后才可能支持。

    OpenGL扩展也不是没有缺点,正因为各个硬件厂商都可以开发自己的扩展,所以扩展的数目比较大,而且有点混乱,有些扩展实现的相同的功能,可因为是不同厂商开发的,接口却不一样,所以程序中为了实现这个功能,往往要为不同的显卡写不同的程序。这个问题在OpenGL 2.0出来后可能会得到解决,OpenGL 2.0的一个目标就是统一扩展,减少扩展数目。

    扩展名

    每个扩展都有一个扩展名,扩展名类似如下形式:

    GL_ARB_multitexture

    第一段GL,用来表示针对OpenGL哪部分开发的扩展,有以下几个值:

    *

    GL  – 针对OpenGL核心的扩展
    *

    WGL – 针对Windows平台的扩展
    *

    GLX – 针对Unix / Linux平台的扩展
    *

    GLU – 针对OpenGL Utility Library的扩展

    第二段ARB,用来表示是谁开发的这个扩展,常见以下几个值:

    *

    ARB – 经OpenGL Architecture Review Board(OpenGL管理机构)正式核准的扩展,往往由厂商开发的扩展发展而来,如果同时存在厂商开发的扩展和ARB扩展,应该优先使用ARB扩展
    *

    EXT – 被多个硬件厂商支持的扩展
    *

    NV  – nVIDIA 公司开发的扩展
    *

    ATI – ATI公司开发的扩展
    *

    ATIX– ATI公司开发的实验性扩展
    *

    SGI – Silicon Graphics(SGI)公司开发的扩展
    *

    SGIX– Silicon Graphics(SGI)公司开发的实验性扩展

    第三段multitexture就是真正的扩展名了,如multitexture就是多重纹理扩展。

    使用OpenGL扩展

    要使用一个OpenGL扩展,首先必须检查显卡是否支持这个扩展,以下代码可以获取一个显卡支持的的OpenGL扩展:
    const char *str = glGetString( GL_EXTENSIONS );
    函数返回一个字符串指针,这个字符串就是显卡所支持的所有扩展的扩展名,不同的扩展名之间用空格隔开,形如:

    "GL_ARB_imaging GL_ARB_multitexture GL_ARB_point_parameters ……"

    OpenGL扩展往往都会新增一些函数,在Windows平台上,这些函数不是通过.lib库连接到程序里的,而要在运行时动态获得函数的指针。我们以GL_ARB_point_parameters扩展为例看看怎么获得函数指针。

    首先要定义函数指针类型,

    typedef void (APIENTRY * PFNGLPOINTPARAMETERFARBPROC)(GLenum pname, GLfloat param);
    typedef void (APIENTRY * PFNGLPOINTPARAMETERFVARBPROC)(GLenum pname, const GLfloat *params);

    这个工作SGI已经为我们做好,它提供了一个头文件 glext.h ,里面有目前绝大多数扩展的常量和函数指针定义,下载下来放到编译器的include/GL文件夹下面,然后在程序里面加上:

    #include <GL/glext.h>

    就可以在程序中使用常量和函数指针类型了。

    然后要定义函数指针:

    PFNGLPOINTPARAMETERFARBPROC glPointParameterfARB;
    PFNGLPOINTPARAMETERFVARBPROC glPointParameterfvARB;

    再检查显卡是否支持GL_ARB_point_parameters扩展,其中isExtensionSupported是自定义的一个函数,就是在glGetString( GL_EXTENSIONS )返回的字符串里查找是否存在指定的扩展名:

    int hasPointParams = isExtensionSupported("GL_ARB_point_parameters");

    如果支持,就可以用wglGetProcAddress函数获取扩展函数的指针:
    if (hasPointParams)

    {
    glPointParameterfARB = (PFNGLPOINTPARAMETERFARBPROC)\ wglGetProcAddress( "glPointParameterfEXT" );
    glPointParameterfvARB = (PFNGLPOINTPARAMETERFVARBPROC) \ wglGetProcAddress( "glPointParameterfvEXT" );
    }

    最后就可以在程序里使用扩展函数:

    if (hasPointParams)

    {
    static GLfloat quadratic[3] = { 0.25, 0.0, 1/60.0 };
    glPointParameterfvARB(GL_DISTANCE_ATTENUATION_ARB, quadratic);
    glPointParameterfARB(GL_POINT_FADE_THRESHOLD_SIZE_ARB, 1.0);
    }

    WGL扩展

    glGetString( GL_EXTENSIONS )取得的扩展字符串中并不包括针对Windows平台的WGL扩展,WGL扩展串要通过WGL_ARB_extensions_string扩展来获得,以下代码演示了如何获得WGL扩展串:

    定义WGL_ARB_extensions_string扩展新增函数wglGetExtensionsStringARB的函数指针类型,同样这个工作SGI已经为我们做好,只不过不在glext.h中,而在它提供的另外一个头文件 wglext.h 中:

    typedef const char *(APIENTRY * PFNWGLGETEXTENSIONSSTRINGARBPROC)( HDC hdc);

    定义函数指针:

    PFNWGLGETEXTENSIONSSTRINGARBPROC wglGetExtensionsStringARB;

    检查是否支持WGL_ARB_extensions_string扩展,如果不支持,表示这个显卡不支持WGL扩展,如果支持,则得到wglGetExtensionsStringARB函数的指针,并调用它得到WGL扩展串:

    int hasWGLext = isExtensionSupported("WGL_ARB_extensions_string");

    if (hasWGLext)

    {

    wglGetExtensionsStringARB = (PFNWGLGETEXTENSIONSSTRINGARBPROC) \ wglGetProcAddress( "wglGetExtensionsStringARB" );

    const char *wglExt = wglGetExtensionsStringARB( hdc );

    ……

    }

    OpenGL版本

    一些常用的OpenGL扩展会在新版的OpenGL中加到OpenGL核心中去,成为OpenGL标准的一部分,可以简化程序开发,程序员使用这些功能时不必做繁琐的扩展初始化工作。比如多重纹理功能,在OpenGL1.2.1加入到OpenGL核心中,以前要使用多重纹理,要先检查是否支持 GL_ARB_multitexture扩展,然后初始化glActiveTextureARB等函数,很麻烦,而OpenGL1.2后,则可以直接使用 glActiveTexture函数。

    不过,这种简化只有Mac/Unix/Linux程序员才能享受到,在Windows平台上没有这么简单。微软为了维护Direct3D,对 OpenGL的支持很消极,其OpenGL实现仍然是1.1。由于Windows上的OpenGL程序最终都会动态链接到微软的 OpenGL32.dll,可OpenGL32.dll只支持OpenGL 1.1,使我们不能直接使用新版OpenGL,仍然要用扩展访问OpenGL1.1以来新增的功能。

    OpenGL扩展资料

    All About OpenGL Extensions :
    讨论OpenGL扩展机制,讲述了如何阅读扩展官方说明书,并举了一些扩展的例子。必读。

    OpenGL Extension Registry :
    由SGI维护,列出了目前公开的所有扩展及其官方说明书。

    OpenGL Hardware Registry :
    由Delphi3D.net维护,列出了目前几乎所有3D加速卡的OpenGL硬件信息,包括其支持的扩展。当然,这里面列的扩展不能作为程序的依据,程序中要使用某个扩展,还是要先检查显卡是否支持。因为同样的显卡,如果驱动程序不同,支持的扩展也不相同,往往新的驱动程序会加入新的扩展,丢掉一些废弃的扩展。

    OpenGL硬件加速

    在Windows平台上,OpenGL驱动可能有三种模式:纯软件、MCD和ICD:

    *

    纯软件模式:微软提供一个OpenGL的软件实现,所有渲染操作均由CPU完成,速度很慢。如果安装系统时使用Windows自带的显卡驱动程序,那么 OpenGL程序就会运行在软件模式下。而且由于微软有自己的Direct3D,所以对OpenGL的支持很消极,它的OpenGL纯软件实现只支持 OpenGL1.1,而目前OpenGL的最新版本为1.4
    *

    MCD (Mini Client Driver):MCD是早期微软在Windows NT上支持OpenGL时,为了简化驱动开发时使用的一个模型。在这个模型中,OpenGL渲染管线的变换、光照部分仍然由软件实现,而光栅化部分则由硬件厂商实现,因此只要硬件支持,MCD可以硬件加速光栅化部分。MCD虽然可以简化驱动开发,但是功能限制太大,现在市面上的3D加速卡均支持硬件变换和光照,MCD却不能利用这一特性,看上去MCD已经没有存在的价值
    *

    ICD (Installable Client Driver):ICD是一个完整的OpenGL驱动模型,比MCD复杂得多。硬件厂商要实现完整的OpenGL渲染管线,如变换、光照、光栅化等,因此只要硬件支持,ICD可以硬件加速整个OpenGL渲染管线。我们通常说的OpenGL硬件加速就是指的通过ICD模型获得的硬件加速,而现在硬件厂商提供的OpenGL驱动程序也都是依照ICD模型开发的。主要硬件厂商的ICD已经可以支持OpenGL的最新版1.4

    Windows怎么实现OpenGL硬件加速呢?OpenGL32.dll是微软的OpenGL 1.1纯软件实现,我们的程序都要动态链接到这个 dll。如果安装3D芯片厂商的驱动程序,会将一个不同名字的dll放到Windows系统目录下,比如在Windows 2000下安装 nVIDIA GeForce2 MX的驱动程序,会在系统目录下放一个nvoglnt.dll(这就是nVIDIA的OpenGL驱动),并在注册表中登记nvoglnt.dll,让Windows知道硬件加速OpenGL驱动的名字,以后运行OpenGL程序,OpenGL32.dll就会把 OpenGL调用直接转到nvoglnt.dll。

    Windows平台上,一个OpenGL程序是否使用硬件加速由三个因素决定,这三个因素缺一不可,否则程序都会运行于纯软件模式:

    *

    是否有一块3D加速卡
    *

    是否安装了显卡厂商提供的最新的驱动程序,Windows自带的显卡驱动程序并不会提供OpenGL硬件加速能力
    *

    指定的像素格式是否被显卡硬件所支持

    判断一种像素格式是否被显卡硬件所支持,可以用函数DescribePixelFormat取得该像素格式的数据,然后看结构体PIXELFORMATDESCRIPTOR中的dwFlags的值,如果

    *

    PFD_GENERIC_FORMAT被置1,并且PFD_GENERIC_ACCELERATED被置0,即
    (pfd.dwFlags & PFD_GENERIC_FORMAT) &&
    !(pfd.dwFlags & PFD_GENERIC_ACCELERATED)
    表明该像素格式不被显卡硬件支持,使用该像素格式的OpenGL程序将使用纯软件模式渲染
    *

    PFD_GENERIC_FORMAT被置1,并且PFD_GENERIC_ACCELERATED被置1,即
    (pfd.dwFlags & PFD_GENERIC_FORMAT) &&
    (pfd.dwFlags & PFD_GENERIC_ACCELERATED)
    表明该像素格式被显卡硬件支持,并且程序使用MCD模式渲染
    *

    PFD_GENERIC_FORMAT被置0,并且PFD_GENERIC_ACCELERATED被置0,
    !(pfd.dwFlags & PFD_GENERIC_FORMAT) &&
    !(pfd.dwFlags & PFD_GENERIC_ACCELERATED)
    表明该像素格式被显卡硬件支持,并且程序使用ICD模式渲染

    显卡差异

    正如前面所说,不同的显卡厂商可能会为相同的功能开发不同的OpenGL扩展,使得OpenGL扩展的编程复杂化,就算实现相同的功能也可能需要为不同的显卡开发不同的程序。好在现在显卡芯片市场只有nVIDIA和ATI两家当道,所以工作量也不会太大。而OpenGL 2.0推出后,这种情况会大为改观。

    游戏的运行环境差异很大,可能运行于只具备基本3D加速能力的老显卡上,也可能运行于最新推出的功能强大的3D加速卡上,而且显卡芯片的厂商也各不相同。对于显卡较差的机器,要保证游戏能运行,而对于较好的显卡,要充分发挥它的功能,创造绚丽的图形效果。这就是说游戏需要多条执行路径,运行时根据显卡配置选择不同的执行路径。怎么决定需要多少种执行路径呢?

    设计执行路径要考虑两个主要因素:

    *

    决定要重点支持的显卡芯片厂商

    现在3D显卡芯片市场有两个主要厂商nVIDIA和ATI,它们各自的OpenGL扩展也最多,必须为它们设计相应的执行路径。对于其他厂商,因为几乎所有厂商都会尽量支持ARB扩展,可以设计执行路径,使用各个厂商都支持的ARB扩展。

    *

    程序支持的显卡档次

    显卡芯片都会有个芯片代号,类似于软件的版本号。相同主版本号的芯片属于一个档次,支持的功能往往一样。设计图形程序时,可以根据芯片代号来决定需要设计多少种执行路径。要注意的是必须要设计一条和显卡无关的,只使用OpenGL基本功能的执行路径,使得程序能够在低端显卡上运行。

    现在可能使用的nVIDIA和ATI的显卡芯片代号和显卡型号的对应关系如下表:

    nVIDIA

    ATI
    芯片代号     显卡型号            芯片代号     显卡型号
    NV1
    NV2
    NV3
    NV4
    NV5      NV1
    Riva 128
    Riva 128ZX
    Riva TnT
    Riva TnT2            RAGE(?)     RAGE PRO
    RAGE 128
    RAGE 128 PRO
    NV10
    NV11
    NV15
    NV17
    NV18      GeForce 256
    GeForce2 MX *
    GeForce2 *
    GeForce4 MX *
    GeForce4 MX AGP8X *            R100

    RV200     RADEON 7000
    RADEON
    RADEON 7200
    RADEON 7500
    NV20

    NV25
    NV28      Geforce3 Ti200 GeForce3
    GeForce3 Ti500
    GeForce4 Ti *
    GeForce4 Ti AGP 8X *            RV250
    R200

    RV280      RADEON 9000
    RADEON 8500
    RADEON 9100
    RADEON 9200 AGP 8X
    NV34
    NV31
    NV30
    NV35      GeForce FX 5200
    GeForce FX 5600
    GeForce FX 5800
    GeForce FX 5900            RV300
    RV350
    R300
    R350      RADEON 9500
    RADEON 9600
    RADEON 9700
    RADEON 9800

    ·其中标注 * 的是产品系列,型号还会细分

    ·通常大家习惯用芯片代号的主版本号来统称一代芯片,例如用NV20统称NV20/NV25/NV28,用R200统称RV250/R200/RV280

    ·虽然ATI RADEON 7500的芯片型号是RV200,但实际属于R100档次的产品

    我们来看一个执行路径的例子,idSoftware即将推出的Doom3的执行路径。

    Doom3一共有6条执行路径:

    1.

    ARB:几乎不使用扩展,没有镜面高光效果,没有顶点编程(vertex program),保证程序能在低端显卡上运行
    2.

    NV10:支持所有功能,每帧需要渲染5个pass(five rendering passes,指一个对象多次送入渲染管道,比如第一次渲染漫射光diffuse,第二次渲染镜面高光specular ……,各次渲染结果通过glBlend混合在一起形成最终效果),没有顶点编程
    3.

    NV20:支持所有功能,2或3个pass
    4.

    NV30:支持所有功能,1个pass
    5.

    R200:支持所有功能,大部分情况下只需1个pass
    6.

    ARB2:支持所有功能,浮点片断编程(fragment program),1个pass

    nVIDIA显卡可能执行ARB / NV10 / NV20 / NV30 / ARB2 五条路径,其中NV10 / NV20 / NV30 专门针对不同档次的nVIDIA显卡开发的。

    ATI显卡可能执行 ARB / R200 / ARB2 三条路径,其中R200是专门针对ATI开发的。

    而其他显卡则根据档次高低执行ARB或ARB2路径。ARB用于低端显卡,ARB2用于高端显卡。

    顶点/片断编程

    1999年底,nVIDIA推出GeForce 256,并开始使用GPU(Graphics Processing Unit)来称呼显卡芯片(也有厂商叫VPU(Visual Processing Unit),是一回事)。GeForce 256最大卖点是硬件T&L(Transform&Lighting,变换和光照),这意味着变换和光照计算可以在GPU中进行,大大减轻了CPU的压力,显卡开始成为独立于CPU的一个处理器。

    硬件T&L后,GPU最激动人心的进步就是引入了可编程能力。我们知道,OpenGL和Direct3D都有固定的渲染管线,定义光源,送入顶点位置、法线、纹理坐标等,就可以给你渲染出一幅图像来,程序员对具体的渲染过程无法控制。而OpenGL扩展和DirectX8给渲染管线引入可编程能力,图形软件开发人员可以编写运行于显卡芯片的汇编程序来控制具体的渲染过程,这给予图形软件开发更大的灵活性,并且由于硬件的支持,获得这些灵活性并不会牺牲性能。GPU的可编程能力对实时图形渲染会产生深远的影响。

    OpenGL支持两种可编程模型:

    *

    顶点编程Vertex Program,对应于Direct3D中的Vertex Shader,提供可编程的T&L能力,代替了传统渲染流水线中的T&L,处理顶点的变换、光照计算
    *

    片断编程Fragment Program,对应于Direct3D的Pixel Shader,提供可编程的光栅化操作,代替了传统流水线中的纹理映射、逐像素颜色及雾效处理

    目前顶点编程相关的扩展有:

    *

    GL_NV_vertex_program:nVIDIA NV10档次显卡用软件模拟,NV20及以上档次显卡上硬件支持
    *

    GL_NV_vertex_program1_1:nVIDIA NV10档次显卡用软件模拟,NV20及以上档次显卡上硬件支持
    *

    GL_EXT_vertex_shader:在ATI R200及以上档次显卡上支持
    *

    GL_ARB_vertex_program:由上面三个扩展发展而来,支持上面三个扩展的显卡安装最新的驱动程序后都会支持此扩展,所以程序中不必支持上面三个扩展,只支持此扩展就可以了。此扩展不支持分支循环
    *

    GL_NV_vertex_program2:在nVIDIA NV30及以上档次显卡上支持,支持分支循环,估计会成为GL_ARB_vertex_program2扩展的原型
    *

    实际上ATI R300级别的显卡已经在顶点编程中支持分支循环(硬件支持DirectX9的vs2.0),但并没有开发扩展提供给OpenGL程序员使用,估计是在等支持分支循环的GL_ARB_vertex_program2扩展出台

    可见现在使用OpenGL的vertex program,只需支持GL_ARB_vertex_program和GL_NV_vertex_program2两个扩展。

    目前片断编程相关的扩展有:

    *

    GL_NV_register_combiners:在nVIDIA NV10及以上档次显卡上支持,处理逐像素颜色及雾效计算
    *

    GL_NV_register_combiners2:在nVIDIA NV20及以上档次显卡上支持,对GL_NV_register_combiners作了个简单的扩展,支持两个常量寄存器在每级combiner都可以有不同的值
    *

    GL_NV_texture_shader / GL_NV_texture_shader2 / GL_NV_texture_shader3:在nVIDIA NV20及以上档次显卡上支持,提供多种功能强大的纹理提取操作
    *

    GL_ATI_fragment_shader:在ATI R200及以上档次显卡上支持

    以上扩展提供的功能不是编写运行于GPU的汇编码,而是通过函数调用的方式实现,不如编写汇编码直观方便。下面几个扩展则可以通过编写汇编码来实现片断编程

    *

    GL_ATI_text_fragment_shader:实际上就是GL_ATI_fragment_shader的汇编码版本,到现在还没看到在PC上的支持信息,看来只会在苹果机上支持
    *

    GL_ARB_fragment_program:在nVIDIA NV30 / ATI R300及以上档次显卡上支持
    *

    GL_NV_fragment_program:在nVIDIA NV30及以上档次显卡上支持,比GL_ARB_fragment_program更强大

    与顶点编程相比,片断编程要复杂得多:

    *

    在NV10系列上,只能使用GL_NV_register_combiners提供的部分片断编程能力
    *

    在NV20系列上,则可以使用register combiners和texture shader实现片断编程
    *

    在NV30系列上,可以使用GL_ARB_fragment_program和GL_NV_fragment_program
    *

    在ATI R200系列上,使用GL_ATI_fragment_shader
    *

    在ATI R300系列上,使用GL_ARB_fragment_program

    看到这里我们不难理解为什么Doom3会将渲染执行路径分成ARB、NV10、NV20、NV30、R200、ARB2几条了:

    *

    ARB既没有顶点编程也没有片断编程
    *

    NV10没有用到顶点编程(虽然NV10支持顶点编程,但是是软件模拟,而显卡硬件并不支持),但用到register combiners实现凹凸贴图(Bump Mapping)
    *

    NV20使用顶点编程,并用register combiners和texture shader实现片断编程
    *

    NV30使用顶点编程,并用GL_NV_fragment_program实现片断编程
    *

    R200使用顶点编程,并用GL_ATI_fragment_shader实现片断编程
    *

    ARB2使用顶点编程,并用GL_ARB_fragment_program实现片断编程

    附表:

    nVIDIA显卡顶点、片断编程支持情况
    芯片代号     纹理单元数     Register Combiner     Texture Shader     Vertex Program     Fragment Program
    NVX     2     X     X     X     X
    NV10     2     2级     X     NVvp1.1/ARBvp1.0     RC
    NV20     4     8级     支持     NVvp1.1/ARBvp1.0     RC/TS
    NV30     4     8级     支持     NVvp2.0/ARBvp1.0     ARBfp1.0/NVfp

    ATI显卡顶点、片断编程支持情况
    芯片代号     纹理单元数     Vertex Program     Fragment Program
    RAGE     2     X     X
    R100     3     X     X
    R200     6     EXTvp/ARBvp1.0     ATIfp
    R300     8     EXTvp/ARBvp1.0     ARBfp1.0

    Cg(C for Graphics)

    直接使用扩展编写vertex program和fragment program不太方便,要么是函数调用,要么是汇编码,相当于用x86汇编编写PC程序,而现在已经有了面向vertex program和fragment program的高级语言,称为HLSL(高级着色语言,High Level Shading Language)。

    Cg是nVIDIA提出的一种高级着色语言,它的语法和C语言类似,可以大大简化vertex program和fragment program的编写。用它写的程序可以:

    *

    编译成GL_NV_vertex_program / GL_NV_vertex_program1_1 / GL_ARB_vertex_program / GL_NV_vertex_program2 的汇编码
    *

    编译成GL_ARB_fragment_program / GL_NV_fragment_program的汇编码
    *

    编译成用于nvParse的RC(Register Combiners)及TS(Texture Shader)脚本
    *

    直接在程序中调用Cg提供的API,运行Cg程序
    *

    编译成DirectX的 vertex shader / pixel shader

    我们可以看到Cg只是对nVIDIA的产品支持比较好,而其他厂商的产品只有支持 GL_ARB_vertex_program/GL_ARB_fragment_program时才能从Cg获得好处,不支持这两个ARB扩展的显卡则不能运行Cg编写的程序,大大降低了Cg的实用性。虽然Cg提供接口,使其他厂商可以对Cg进行扩展,以支持各个厂商自己的OpenGL扩展,不过Cg毕竟是一个企业的产品,别的厂商不会支持,所以如果要写通用的图形程序,Cg并不合适。

    况且OpenGL的HLSL——GLslang(OpenGL Shading Language)规范已经被ARB审核通过,估计不久就可以使用GLslang编写vertex program和fragment program,到时Cg的位置应该会相当尴尬,因为OpenGL和DirectX都已经有了自己的HLSL。不过话说回来,Cg可以同时支持OpenGL和DirectX,这也算是它的一个优势。

    RenderMonkey

    RenderMonkey并不是一种语言,而是ATI推出的一个编写调试vertex program和fragment program的集成开发环境,目前只支持DirectX的vertex shader / pixel shader / HLSL,不过ATI正在和3Dlabs合作,不日RenderMonkey也会支持OpenGL vertex program / fragment program / GLslang。另外,RenderMonkey不仅仅是为程序员设计的,美工也可以使用。

    nvParse

    nvParse是nVIDIA公司推出的一个库,可以简化RC(Register Combiners)及TS(Texture Shader)的开发。使用GL_NV_register_combiners和GL_NV_texture_shader扩展实现片断编程全是函数调用的形式,很不方便,而nVIDIA为了简化RC和TS程序开发,建立了一种脚本格式,用简单的脚本代替复杂的 GL_NV_register_combiners和GL_NV_texture_shader函数调用,nvParse则提供API用于解释执行RC和 TS脚本。

    模拟NV30

    并非所有程序员都有一块NV30显卡,但nVIDIA最新的雷管驱动程序(version 40.41及以后)支持软件模拟NV30架构,只不过很慢,但对没有NV30显卡的程序员已经是个福音了,只要我们有一块GeForce级的显卡,安装最新的雷管驱动程序,然后下载一个NVEmulate.exe (52 KB),运行它,打开NV30模拟,你的显卡就支持NV30的所有功能了,这样就算没有NV30显卡同样可以针对NV30开发程序。要注意的是,不需要NV30模拟的时候要记得关掉它,毕竟是软件模拟,速度很慢。

    后记

    写这些只是希望为后来的朋友指一个方向,写得很简略,因为我自己也在学习,hoho,更多的东西还是需要大家自己多看多写 :) 我将来也会慢慢放上一些详细的文章以及源码,希望能对大家有所帮助。关于这篇文章的问题或建议,可以写信给我,我的联系方式。

    Maple Studio

    第 1 页,共 2 页12