Rise的自留地

记录生活中的点滴,分享编程技术和游戏开发经验。

0%

下面正式开始,先讲窗口类,创建窗口,销毁窗口,窗口消息处理函数. 

 

·窗口类WNDCLASS
struct WNDCLASS {
    UINT        style;
    WNDPROC     lpfnWndProc;
    int         cbClsExtra;
    int         cbWndExtra;
    HINSTANCE   hInstance;
    HICON       hIcon;
    HCURSOR     hCursor;
    HBRUSH      hbrBackground;
    LPCSTR      lpszMenuName;
    LPCSTR      lpszClassName;
};
style用来定义窗口的行为。如果打算共同使用GDID3D的话,可以使用CS_OWNDC作为参数。
lpfnWndProc一个函数指针,指向与这个窗口类绑定在一起的处理窗口消息的函数。
cbClsExtracbWndExtra为窗口和为分配内存空间。很少使用到这两个参数,一般设为0
hInstance应用程序的实例句柄。你可以使用GetModuleHandle()来得到它,也可以从Win32程序的入口函数WinMain那里得到它。当然,你也可以把它设为NULL(不知有什么用)
hIconhCursorhbrBackground设置默认的图标、鼠标、背景颜色。不过在这里设置这些其实并不怎么重要,因为我们可以在后面定制自己的渲染方法。
lpszMenuName用来创建菜单
lpszClassName窗口类的名字。我们可以通过这个名字来创建以这个窗口类为模板的窗口。甚至可以通过这个名字来得到窗口的句柄。
设置好窗口类结构的内容后,使用RegisterClass(const WNDCLASS *lpWndClass)函数来注册它。关闭窗口后可以用UnregisterClass(LPCSTR lpClassName, HINSTANCE hInstance)来撤销注册。
·创建窗口CreateWindow
HWND CreateWindow(
   LPCTSTR lpClassName,
   LPCTSTR lpWindowName,
   DWORD dwStyle,
   int x, y,
   int nWidth, nHeight,
   HWND hWndParent,
   HMENU hMenu,
   HINSTANCE hInstance,
   LPVOID lpParam
);
lpClassName窗口类的名字。即窗口类结构体中的lpszClassName成员。
lpWindowName如果你的应用程序有标题栏,这个就是你标题栏上显示的内容。
dwStyle窗口的风格决定你的窗口是否有标题栏、最大最小化按钮、窗口边框等属性。在全屏的模式下,WS_POPUP|WS_VISIBLE是常用的设置,因为它产生一个不带任何东西的全屏窗口。在窗口的模式下,你可以设置很多窗口的风格,具体可以查看相关资料,这里不详细说明,不过WS_OVERLAPPED|WS_SYSMENU|WS_VISIBLE是一组常用的风格。
xy窗口创建的位置。(xy)表示窗口的左上角位置。
nWidthnHeight用来设置窗口的宽度和高度,以像素为单位。如果你想创建一个全屏的窗口,使用GetSystemMetrics(SM_CXSCREEN)GetSystemMetrics(SM_CYSCREEN)可以得到当前显示器屏幕的大小
hWndParent指定这个新建窗口的父窗口。在D3D应用程序中很少用,一般设为NULL
hMenu菜单句柄。
hInstance应用程序的实例句柄。你可以使用GetModuleHandle()来得到它,也可以从Win32程序的入口函数WinMain那里得到它。当然,你也可以把它设为NULL(不知有什么用)
lpParam一个很神秘的参数。除非你知道自己在做什么,否则还是把它设为NULL吧。
·销毁窗口DestroyWindow
       销毁窗口有两种方法,一种是隐式的,一种是显式的。我们都知道Windows操作系统是一个基于消息驱动的系统。流动于系统中的消息使我们的窗口跑起来。在很多软件开发特别是商业软件的开发过程中,窗口的产生和销毁都是交由系统去做的,因为这些不是这类开发的关注所在。但是游戏开发不一样,尽管你也可以只向系统发送一条WM_DESTROY消息来销毁窗口,我们还是希望窗口是销毁的明明白白的。由于窗口的注册、产生和使用都是由我们亲手来做的,那么当然窗口的销毁也得由我们亲自来做。不过还是得说明一点,使用WM_DESTROY消息和DestroyWindow函数来销毁窗口在本质上并无太大差别,使用哪种方法可以说是根据个人的爱好吧。
       销毁窗口后是不是就完事了呢?不,还没有,因为应用程序的消息队列里可能还有没处理完的消息,为了彻底的安全,我们还得把那些消息都处理完。所以结束应用程序的时候,可以使用以下方法:
       MSG msg;
       DestroyWindow(h_wnd);
       while(PeekMessage(&msg , NULL , 0 , 0 , PM_REMOVE))
       {
              TranslateMessage(&msg);
              DispatchMessage(&msg);
       }
·窗口消息处理过程
       窗口消息的处理函数是一个回调函数,什么是回调函数?就是由操作系统负责调用的函数。CALLBACK这个宏其实就是__stdcall,这是一种函数调用的方式,在这里不多说这些了,有兴趣的可以参考一些Windows编程的书籍,里面会有很详尽的说明。
       Windows里面有很多消息,这些消息都跑去哪里了呢?其实它们都在自己的消息队列里等候。消息是怎么从队列里出去的呢?就是通过GetMessagePeekMessage这两个函数。那么消息从队列里出去后又到哪里了呢?嗯,这时候消息就正式进入了我们的窗口消息处理过程,也即是窗口类中lpfnWndProc所指定的函数。一个消息处理函数有四个参数,下面分别说说:
       参数1HWND p_hWnd
       消息不都是传到以窗口类为模板产生的窗口吗?为什么还要使用窗口句柄来指明窗口呢?别忘了一个窗口类是可以产生多个窗口的呀,如果一个应用程序里面有多个窗口,并且它们之中的一些窗口是共用一个窗口类的,那么就得用一个窗口句柄来指明究竟这个消息是哪个窗口发过来的。
       参数2UINT p_msg
       这是一个消息类型,就是WM_KEYDOWN , WM_CLOSE , WM_TIMER这些东东。
       参数3WPARAM p_wparam
       这个参数内容就是消息的主要内容。如果是WM_KEYDOWN消息,那么p_wparam就是用来告诉你究竟是哪个键被按下。
       参数4LPARAM p_lparam
       这个参数的内容一般是消息的一些附加内容。
       最后说明一下DefWindowProc的作用。有时候我们把一个消息传到窗口消息处理函数里面,但是里面没有处理这个消息的内容。怎么办?很容易,交给DefWindowProc处理就对了。
 ·创建IDirect3D接口
DirectX是一组COM组件,COM是一种二进制标准,每一个COM里面提供了至少一个接口,而接口就是一组相关的函数,我们使用DirectX,其实就是使用那些函数。COMC++中的类有点像,只不过COM使用自己的方法来创建实例。创建COM实例的一般方法是使用coCreateInstance函数。有关coCreateInstance的使用方法,可以参考有关COM方面的资料,这里暂时不详细说明了,因为DirectX提供了更简洁的方法来创建DirectX组件的实例。这一章我要讲的就是Direct3D组件的使用方法。
       为了使用D3D中的函数,我们得先定义一个指向IDirect3D9这个接口的指针,顺便说明一下,其实接口也是一个指针,所以我们定义的就是一个指向指针的指针,也即二重指针,为什么要使用二重指针呢,我暂时还不是很懂,所以先留着这个疑问吧^_^。定义完这个接口指针后,例如IDirect3D9 *g_pD3D;现在我们使用Direct3DCreate9这个函数来创建一个D3D接口:
       g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );
       Direct3DCreate9这个函数只有一个参数,它表明要创建接口的版本。如果你想创建一个老的接口版本当然也可以,不过没有人会那样做吧。
       创建接口后就可以创建D3D设备了,什么是D3D设备?你可以想象为你机上的那块显卡!什么?你有几块显卡!!没关系,那就创建多几个D3D设备接口吧。创建D3D设备需要的参数很多,如果把那些参数都挤在一个函数里面,那就太长了,所以就把一些参数放进结构体里面,只要先设定好这些结构体,再把这些结构体当作参数传给创建D3D设备的函数,那就清晰多了。首先要讲的就是D3DPRESENT_PARAMETERS这个结构。下面是它的定义:
struct D3DPRESENT_PARAMETERS{
   UINT                BackBufferWidth;
   UINT                BackBufferHeight;
   D3DFORMAT           BackBufferFormat;
   UINT                BackBufferCount;
   D3DMULTISAMPLE_TYPE MultiSampleType;
   DWORD               MultiSampleQuality;
   D3DSWAPEFFECT       SwapEffect;
   HWND                hDeviceWindow;
   BOOL                Windowed;
   BOOL                EnableAutoDepthStencil;
   D3DFORMAT           AutoDepthStencilFormat;
   DWORD               Flags;
   UINT                FullScreen_RefreshRateInHz;
   UINT                PresentationInterval;
};
        BackBufferWidthBackBufferHeight后备缓冲的宽度和高度。在全屏模式下,这两者的值必需符合显卡所支持的分辨率。例如(800600),(640480)。
       BackBufferFormat后备缓冲的格式。这个参数是一个D3DFORMAT枚举类型,它的值有很多种,例如D3DFMT_R5G6B5,这说明后备缓冲的格式是每个像素16位,其实红色(R)占5位,绿色(G)占6位,蓝色(B)占5位,为什么绿色会多一位呢?据说是因为人的眼睛对绿色比较敏感。DX9只支持16位和32位的后备缓冲格式,24位并不支持。如果对这D3DFORMAT不熟悉的话,可以把它设为D3DFMT_UNKNOWN,这时候它将使用桌面的格式。
       BackBufferCount后备缓冲的数目,范围是从03,如果为0,那就当成1来处理。大多数情况我们只使用一个后备缓冲。使用多个后备缓冲可以使画面很流畅,但是却会造成输入设备响应过慢,还会消耗很多内存。
       MultiSampleTypeMultiSampleQuality这两个参数可以使你的渲染场景变得更好看,但是却消耗你很多内存资源,而且,并不是所有的显卡都支持这两者的所设定的功能的。在这里我们分别把它们设为D3DMULTISAMPLE_NONE0
       SwapEffect交换缓冲支持的效果类型。它是D3DSWAPEFFECT枚举类型,可以设定为以下三者之一:D3DSWAPEFFECT_DISCARDD3DSWAPEFFECT_FLIPD3DSWAPEFFECT_COPY。如果设定为D3DSWAPEFFECT_DISCARD,则后备缓冲区的东西被复制到屏幕上后,后备缓冲区的东西就没有什么用了,可以丢弃(discard)了。如果设定为D3DSWAPEFFECT_FLIP,则表示在显示和后备缓冲之间进行周期循环。设定D3DSWAPEFFECT_COPY的话,我也不太清楚有什么作用*^_^*。一般我们是把这个参数设为D3DSWAPEFFECT_DISCARD
       hDeviceWindow显示设备输出窗口的句柄
       Windowed如果为FALSE,表示要渲染全屏。如果为TRUE,表示要渲染窗口。渲染全屏的时候,BackBufferWidthBackBufferHeight的值就得符合显示模式中所设定的值。
       EnableAutoDepthStencil如果要使用Z缓冲,则把它设为TRUE
       AutoDepthStencilFormat如果不使用深度缓冲,那么这个参数将没有用。如果启动了深度缓冲,那么这个参数将为深度缓冲设定缓冲格式(和设定后备缓冲的格式差不多)
       Flags可以设置为0D3DPRESENTFLAG_LOCKABLE_BACKBUFFER。不太清楚是用来做什么的,看字面好像是一个能否锁定后备缓冲区的标记。
       FullScreen_RefreshRateInHz显示器的刷新率,单位是HZ,如果设定了一个显示器不支持的刷新率,将会不能创建设备或发出警告信息。为了方便,一般设为D3DPRESENT_RATE_DEFAULT就行了。
       PresentationInterval如果设置为D3DPRENSENT_INTERVAL_DEFAULT,则说明在显示一个渲染画面的时候必要等候显示器刷新完一次屏幕。例如你的显示器刷新率设为80HZ的话,则一秒内你最多可以显示80个渲染画面。另外你也可以设置在显示器刷新一次屏幕的时间内显示1到4个画面。如果设置为D3DPRENSENT_INTERVAL_IMMEDIATE,则表示可以以即时的方式来显示渲染画面,虽然这样可以提高帧速(FPS),但是却会产生图像撕裂的情况。
·创建IDirect3DDevice接口
       当你把D3DPRESENT_PARAMETERS的参数都设置好后,就可以创建一个D3D设备了,和创建D3D接口一样,先定义一个接口指针IDirect3DDevice9 * g_pD3DDevice;然后使用D3D接口里面的CreateDevice函数来创建设备。CreateDevice的声明为:
       HRESULT CreatDevice(
              UINT Adapter,
              D3DDEVTYPE DeviceType,
              HWND hFocusWindow,
              DWORD BehaviorFlags,
              D3DPRESENT_PARAMETERS *pPresentationParameters,
              IDirect3DDevice9** ppReturnedDeviceInterface
       };
       第一个参数说明要为哪个设备创建设备指针,我之前说过一台机可以有好几个显卡,这个参数就是要指明为哪块显卡创建可以代表它的设备指针。但是我怎么知道显卡的编号呢?可以使用D3D接口里面的函数来获得,例如GetAdapterCounter可以知道系统有几块显卡;GetAdapterIdentifier可以知道显卡的具体属性。一般我们设这个参数为D3DADAPTER_DEFAULT
       第二个参数指明正在使用设备类型。一般设为D3DEVTYPE_HAL
       第三个参数指明要渲染的窗口。如果为全屏模式,则一定要设为主窗口。
       第四个参数是一些标记,可以指定用什么方式来处理顶点。
       第五个参数就要用到上面所讲的D3DPRESENT_PARAMETERS
       第六个参数是返回的接口指针。
·开始渲染
       有了设备接口指针,就可以开始渲染画面了。渲染是一个连续不断的过程,所以必定要在一个循环中完成,没错,就是第一章讲的那个消息循环。在渲染开始之前我们要用IDirect3DDevice9::Clear函数来清除后备缓冲区。
HRESULT Clear(
   DWORD Count,
   const D3DRECT *pRects,
   DWORD Flags,
   D3DCOLOR Color,
   float Z,
   DWORD Stencil
);
        Count说明你要清空的矩形数目。如果要清空的是整个客户区窗口,则设为0
       pRects这是一个D3DRECT结构体的一个数组,如果count中设为5,则这个数组中就得有5个元素。
       Flags一些标记组合。只有三种标记:D3DCLEAR_STENCIL , D3DCLEAR_TARGET , D3DCLEAR_ZBUFFER
       Color清除目标区域所使用的颜色。
       float设置Z缓冲的Z初始值。小于或等于这个Z初始值的Z值才会被改写,但它的值只能取01之间。如果还不清楚什么是Z缓冲的话,可以自己找相关资料看一下,这里不介绍了,呵呵。
       Stencil设置模板缓冲的初始值。它的取值范围是02n次方减1。其中n是模板缓冲的深度。
       清除后备缓冲区后,就可以对它进行渲染了。渲染完毕,使用Present函数来把后备缓冲区的内容显示到屏幕上。
HRESULT Present(
   const RECT *pSourceRect,
   const RECT *pDestRect,
   HWND hDestWindowOverride,
   const RGNDATA *pDirtyRegion
);
        pSourceRect你想要显示的后备缓冲区的一个矩形区域。设为NULL则表示要把整个后备缓冲区的内容都显示。
       pDestRect表示一个显示区域。设为NULL表示整个客户显示区。
       hDestWindowOverride你可以通过它来把显示的内容显示到不同的窗口去。设为NULL则表示显示到主窗口。
       pDirtyRegion高级使用。一般设为NULL
  ·顶点属性与顶点格式
顶点可谓是3D世界中的基本元素。在计算机所能描绘的3D世界中,任何物体都是由多边形构成的,可以是三边形,也可以是四边形等。由于三边形,即三角形所具有的特殊性质决定其在3D世界中得到广泛的使用。构成三角形需要三个点,这些点的性质就是这章所要讲的内容。
       也许你已经知道顶点的结构定义,你可能会奇怪为什么D3D会知道我们“随便”定义的那些结构呢?其实那些顶点的定义可不是那么随便的哦。下面列举在Direct3D中,顶点所具有的所有属性。
       1)位置:顶点的位置,可以分别指定x,y,x三个值,也可以使用D3DXVECTOR3结构来定义。
       2RHW:齐次坐标W的倒数。如果顶点为变换顶点的话,就要有这个值。设置这个值意味着你所定义的顶点将不需要Direct3D的辅助(不能作变换、旋转、放大缩小、光照等),要求你自己对顶点数据进行处理。至于W是什么,WXYZ一样,只是一个四元组的一部分。RHW的英文是Reciprocal of the Homogenous W,即1/W,它是为了处理矩阵的工作变得容易一些(呼,线性代数的东东快都忘了,要恶补一下才行)。一般设RHW的值为1.0
       3)混合加权:用于矩阵混合。高级应用,这里不讲了(其实我不会,^_^
       4)顶点法线:学过高等数学就应该知道法线是什么吧?在这里是指经过顶点且和由顶点引出的边相垂直的线,即和三角形那个面垂直。用三个分量来描述它的方向,这个属性用于光照计算。
       5)顶点大小:设定顶点的大小,这样顶点就可以不用只占一个像素了。
       6)漫反射色:即光线照射到物体上产生反射的着色。理解这个比较麻烦,因为3D光照和真实光照没什么关系,不能像理解真实光照那样去理解3D光照。
       7)镜面反射色:它可以让一个3D物体的表面看起来很光滑。
       8)纹理坐标:如果想要在那些用多边形组成的物体上面贴上纹理,就要使用纹理坐标。由于纹理都是二维的,所以用两个值就可以表示纹理上面某一点的位置。在纹理坐标中,只能在0.01.0之间取值。例如(0.0 , 0.0)表示纹理的左上角,(1.0 , 1.0)表示纹理的右下角。
       好了,请记住上面属性的顺序。我们定义一个顶点结构的时候,不一定要包括全部的属性,但是一定要按照上面的顺序来定义。例如:
       struct MYVERTEX
{
              D3DXVECTOR3 position;
              float rhw;
              D3DCOLOR color;
       }
       上面定义了一个有漫反射色的变换顶点。
       定义完了顶点的结构后,我们就要告诉D3D我们定义的是什么格式。为了方便,我们通常会用#define来定义一个叫做描述“灵活顶点格式”(FVFFlexible Vertex Format)的宏。例如:#define MYFVF D3DFVF_XYZ | D3DFVF_NORMAL。根据之前定义的顶点属性结构体,我们要定义相对应的宏。假如顶点结构中有位置属性,那么就要使用D3DFVF_XYZ;如果是变换顶点的话,就要使用D3DFVF_XYZRHW;如果使用了漫反射色属性的话,就要使用D3DFVF_DIFFUSE。这些值是可以组合使用的,像上面那样用“|”符号作为连结符。定义完灵活顶点格式后,使用IDirect3DDevice9::SetVertexShader函数来告诉D3D我们所定义的顶点格式,例如:g_pD3DDevice->SetVertexShader( MYFVF );
·顶点缓冲
       处理顶点信息的地方有两个,一个是在数组里,另一个是在D3D所定义的顶点缓冲里。换个说法的话就是一个在我们所能直接操作的内存里,另一个在D3D管理的内存里。对于我们这些对操作系统底层了解不多的菜鸟来说,直接操作内存实在是太恐怖了,所以还是交给D3D帮我们处理吧,虽然不知道背后有些什么操作。要想把顶点信息交给D3D处理,我们就要先创建一个顶点缓冲区,可以使用IDirect3DDevice9->CreateVertexBuffer,它的原型是:
HRESULT CreateVertexBuffer(
   UINT Length,
   DWORD Usage,
   DWORD FVF,
   D3DPOOL Pool,
   IDirect3DVertexBuffer9** ppVertexBuffer,
   HANDLE* pSharedHandle
);
       Length缓冲区的长度。通常是顶点数目乘以顶点大小,使用Sizeof( MYVERTEX )就可以知道顶点的大小了。
       Usage高级应用。设为0就可以了。
       FVF就是我们之前定义的灵活顶点格式。
       Pool告诉D3D将顶点缓冲存储在内存中的哪个位置。高级应用,通常可取的三个值是:D3DPOOL_DEFAULTD3DPOOL_MANAGEDD3DPOOL_SYSTEMMEM。多数情况下使用D3DPOOL_DEFAULT就可以了。
       ppVertexBuffer返回来的指向IDirect3DVertexBuffer9的指针。之后对顶点缓冲进行的操作就是通过这个指针啦。到这里还要再提醒一下,对于这些接口指针,在使用完毕后,一定要使用Release来释放它。
       pSharedHandle设为NULL就行了。
       得到一个指向IDirect3DVertexBuffer9的指针后,顶点缓冲也就创建完毕了。现在要做的就是把之前保存在数组中的顶点信息放在顶点缓冲区里面。首先,使用IDirect3DVertexBuffer9::Lock来锁定顶点缓冲区:
HRESULT Lock(
   UINT OffsetToLock,
   UINT SizeToLock,
   void **ppbData,
   DWORD Flags
);
       OffsetToLock指定要开始锁定的缓冲区的位置。通常在起始位置0开始锁定。
       SizeToLock指定在锁定的缓冲区的大小。设为0的话就是表示要锁定整个缓冲区。
       ppbData用来保存返回的指向顶点缓冲区的指针。通过这个指针来向顶点缓冲区填充数据。
       Flags高级应用。通常设为0
       填充为顶点缓冲区后,使用IDirect3DDevice9::Unlock来解锁。
       最后在渲染的时候使用IDirect3DDevice9::SetStreamSource来告诉D3D要渲染哪个顶点缓冲区里面的顶点。
HRESULT SetStreamSource(
   UINT StreamNumber,
   IDirect3DVertexBuffer9 *pStreamData,
   UINT OffsetInBytes,
   UINT Stride
);
     StreamNumber设置数据流的数量。顶点缓冲最多可以使用16个数据流。确定所支持的数据流的数量,可以检查D3DCAPS中的MaxStreams成员的值。通常设为0,表示使用单数据流。
pStreamData要与数据流绑定的数据。在这里我们要把顶点缓冲区与数据流绑定。
OffsetInBytes设置从哪个位置开始读数据。设为0表示从头读起。
Stride数据流里面数据单元的大小。在这里是每个顶点的大小。
·索引缓冲
       很多时候,相邻的三角形会共用一些顶点,例如组成四方形的两个三角形就共用了一条边,即共用了两个顶点信息。如果不使用索引,我们需要六个顶点的信息来绘制这个四方形,但实际上绘制一个四方形只要四个顶点信息就足够了。如果使用了索引就不一样了,在顶点缓冲区里我们可以只保存四个顶点的信息,然后通过索引来读取顶点信息。要使用索引得先创建一个索引缓冲。也许读到这里你会有个疑问,创建一个索引缓冲不就更浪费内存空间了吗?其实不然,索引缓冲区的元素保存的是数字,一个数字所占用的内存肯定要比一个顶点所占用的小得多啦。当你节省了几千个顶点,你就会发现浪费那么一点点索引缓冲区是很值得的。
       创建索引缓冲的函数是:IDirect3DDevice9::CreateIndexBuffer
HRESULT CreateIndexBuffer(
   UINT Length,
   DWORD Usage,
   D3DFORMAT Format,
   D3DPOOL Pool,
   IDirect3DIndexBuffer9** ppIndexBuffer
);
     Length索引缓冲区的长度。通常使用索引数目乘以sizeofWORD)或sizeof(DWORD)来设置,因为索引号的数据类型是字节(WORD)或双字节(DWORD),嗯,一个WORD只有两个字节,DWORD也就只有四个字节,比顶点的大小小多了吧。
     UsageCreateVertexBuffer中的Usage设置一样。一般设为0
Format设置索引格式。不是D3DFMT_INDEX16就是D3DFMT_INDEX32的啦。
Pool又是和CreateVertexBuffer中的一样。一般设为D3DPOOL_DEFAULT
ppIndexBuffer指向IDirect3DIndexBuffer9的指针。操作索引缓冲区就靠它的啦。记得使用完后要Release啊。
和填充顶点缓冲区一样,要填充索引缓冲区,要先使用IDirect3DIndexBuffer9::Lock来锁定缓冲区。
HRESULT Lock(
   UINT OffsetToLock,
   UINT SizeToLock,
   void **ppbData,
   DWORD Flags
);
     是不是和IDirect3DVertexBuffer9::Lock一样呢?具体说明也可以参照上面的内容。填充完之后使用IDirect3DIndexBuffer9::UnLock来解锁。
最后使用IDirect3DDevice9::SetIndices来告诉设备要使用哪个索引。
HRESULT Setindices(
   IDirect3DindexBuffer9* pIndexData,
   UINT BaseVertexIndex
);
     pIndexData设置使用哪个索引缓冲。
BaseVertexIndex设置以顶点缓冲区中的哪个顶点为索引0
     有关顶点的知识就说到这了。一下章说说点、线、三角形这种D3D所支持的图元(drawing primitives)。
·D3D中的图元简介
       D3D中,一共有三种基本图元,分别是点、线和三角形。点是最简单的图元,由它可以构成一种叫点列(point list)的图元类型。线是由两个不重合的点构成的,一些不相连的线组成的集合就叫线列(line list),而一些首尾相连但不形成环路的线的集合就叫线带(line strips)。同理,单独的三角形集合就叫三角形列(triangle list),类似于线带的三角形集合就叫三角形带(triangle strips),另外,如果多个三角形共用一个顶点作为它们的一个顶点的话,那么这个集合就叫三角形扇(triangle fans)。还是画图比较容易理解吧:
 
 
       这些图元有什么用呢?基本上我们可以使用这些图元来画我们想要的任何物体。例如画一个四方形可以使用三角形带来画,画一个圆则使用三角形扇。
       现在介绍一种不需要顶点缓冲来渲染的方法,就是使用IDirect3DDevice9::DrawPrimitiveUP函数。UP就是User Pointer的意思,也即是说要使用用户定义的内存空间。
HRESULT DrawPrimitiveUP(
    D3DPRIMITIVETYPE PrimitiveType,
    unsigned int PrimitiveCount,
    const void *pVertexStreamZeroData,
    unsigned int VertexStreamZeroStride
);
     PrimitiveType要绘画的图元的种类。就是上面介绍的那六种类型。
PrimitiveCount要绘画的图元的数量。假设有n个顶点信息,绘画的图元类型是点列的话,那么图元的数量就是n;如果绘画的图元类型是线列的话,那么图元的数量就是n/2;如果是线带的话就是n-1;三角形列就是n/3;三角形带就是n-2;三角形扇出是n-2
pVertexStreamZeroData存储顶点信息的数组指针
VertexStreamZeroStride顶点的大小
·使用顶点缓冲来绘画图元
很多时候我们使用顶点来定义图形之后,就把这些顶点信息放进顶点缓冲里面,然后再进行渲染。使用点顶缓冲的好处以及如何创建顶点缓冲我已经在上一章已讲过了,现在讲讲怎么把顶点缓冲里面的图元给画出来。其实也很简单,和上面的IDirect3DDevice9::DrawPrimitiveUP函数差不多,我们使用IDirect3DDevice9::DrawPrimitive函数。不过在使用这个函数之前,我们得告诉设备我们使用哪个数据源,使用IDirect3DDevice9::SetStreamSource函数可以设定数据源。
HRESULT SetStreamSource(
   UINT StreamNumber,
   IDirect3DVertexBuffer9 *pStreamData,
   UINT OffsetInBytes,
   UINT Stride
);
StreamNumber设置和哪个数据流梆定。如果使用单数据流的话,这里设为0。最多支持16个数据流。
pStreamData要绑定的数据。也就是我们创建的顶点缓冲区里面的数据。
OffsetInBytes设置从哪个字节开始读起。如果要读整个缓冲区里面的数据的话,这里设为0
Stride单个数据元素的大小。如果数据源是顶点缓冲的话,那么这里就是每个顶点信息的大小(Sizeof(vertex))。
设置好数据源后,就可以使用IDirect3DDevice9::DrawPrimitive来绘画了。
HRESULT DrawPrimitive(
   D3DPRIMITIVETYPE PrimitiveType,
   unsigned int StartVertex,
   unsigned int PrimitiveCount
);
     PrimitiveType要绘画的图元的种类。
StarVertex设置从顶点缓冲区中的第几个顶点画起。没有特殊情况当然是想把全部的顶点画出来啦,所以一般这里设置从0开始。
PrimitiveCount要绘画的图元的数量。
     好了,这章比较简单。写到这章的时候我才发现这不是入门手册,有一些重要但是我觉得没必要讲的东西我都没有讲明。如果是新手看我写的这些东西,搞不好还会被我迷惑了,呵呵。所以还是建议大家看DXSDK里面的说明文档,虽然是英文的,但是很详细,我现在都还没有看完呢。
       嗯,前面四章把最基本的东西讲完了,使用前面的知识我们可以画一些简单的静止图形。下一章就开始讲矩阵了,它可以使我们的图形动起来。
·向量(也叫矢量,英文叫vector)
    向量就是包含大小(长度)和方向的一个量。向量有2维的,也有3维甚至4维的。在DX的所有结构体中,有一个结构体是用来表示3维向量的,它就是D3DVECTOR,这个结构体很简单,只有三个成员:x、y、z。一般来说,如果不涉及到向量运算的话,用这个结构体来定义一个向量就可以了。我们可以它来表示方向以及顶点在3D世界中的位置等。如果你要对那些向量进行一些运算的话,使用D3DVECTOR就很不方便了,因为在D3DVECTOR这个结构体中没有重载任何的运算符,如果想要做一个加法运算,就得分别对结构体中的每一个成员进行运算了。嘿嘿,不用怕,在DX里面有个叫D3DX的东东(包含d3dx.h头文件),它里面定义了很多方便我们进行数学计算的函数和结构。其中就有D3DXVECTOR2,D3DXVECTOR3,D3DXVECTOR4这三个结构体。看它们的名字就应该知道它们的作用了吧。对于2维和4维的结构体这里就不讲了,其实它们也很简单,和D3DXVECTOR3差不多。不过要说明一点的是D3DXVECTOR3是从D3DVECTOR派生过来的,说明它和D3DVECTOR一样,有x、y、z这三个成员,除此之外,D3DXVECTOR3还重载了小部分算术运算符,这样我们就可以像对待整型那样对D3DXVECTOR3的对象进行加减乘除以及判断是否相等的运算了。同时,由于D3DXVECTOR3是从D3DVECTOR派生过来的,所以两者的对象可以互相赋值,在这两种类型中随便转换。
    还是简单说一下向量的数学运算吧。矢量的加减法很简单,就是分别把两个向量的各个分量作加减运算。向量的乘除法也很简单,它只能对一个数值进行乘除法,运算的结果就是向量中的各个分量分别对那个数值进行乘除法后得出的结果。向量的模就是向量的长度,就是各个分量的平方的和的开方。向量的标准化就是使得向量的模为1,这对在3D世界中实现光照是很有用的。对于向量的运算,还有两个“乘法”,那就是点乘和叉乘了。点乘的结果就是两个向量的模相乘,然后再与这两个向量的夹角的余弦值相乘。或者说是两个向量的各个分量分别相乘的结果的和。很明显,点乘的结果就是一个数,这个数对我们分析这两个向量的特点很有帮助。如果点乘的结果为0,那么这两个向量互相垂直;如果结果大于0,那么这两个向量的夹角小于90度;如果结果小于0,那么这两个向量的夹角大于90度。对于叉乘,它的运算公式令人头晕,我就不说了,大家看下面的公式自己领悟吧……
        //v3 = v1 X v2
        v3.x = v1.y*v2.z – v1.z*v2.y
        v3.y = v1.z*v2.x – v1.x*v2.z
        v3.z = v1.x*v2.y – v1.y*v2.x
是不是很难记啊,如果暂时记不了就算了。其实我们主要还是要知道叉乘的意义。和点乘的结果不一样,叉乘的结果是一个新的向量,这个新的向量与原来两个向量都垂直,至于它的方向嘛,不知大家是否还记得左手定则。来,伸出你的左手,按照第一个向量(v1)指向第二个向量(v2)弯曲你的手掌,这时你的拇指所指向的方向就是新向量(v3)的方向了。通过叉乘,我们很容易就得到某个平面(由两个向量决定的)的法线了。
终于写完了上面的文字,描述数学问题可真是费劲,自己又不愿意画图,辛苦大家了。如果你觉得上面的文字很枯燥,那也没关系。因为上面的不是重点,下面介绍的函数才是希望大家要记住的。
D3DX中有很多很有用的函数,它们可以帮助我们实现上面所讲的所有运算。不过下面我只说和D3DXVECTOR3有关的函数:
计算点乘:FLOAT D3DXVec3Dot
CONST D3DXVECTOR3* pV1,
CONST D3DXVECTOR3* pV2
计算叉乘:D3DXVECTOR3* D3DXVec3Cross
            D3DXVECTOR3* pOut,
CONST D3DXVECTOR3* pV1,
CONST D3DXVECTOR3* pV2
    计算模:FLOAT D3DXVec3Length(
                CONST D3DXVECTOR3* pV)
    标准化向量:D3DXVECTOR3* D3DXVec3Normalize(
                    D3DXVECTOR3* pOut,
                    CONST D3DXVECTOR3 pV)
    对于D3DXVECTOR3的加减乘除运算,上面已经讲了,用+ - * / 就行了。
·矩阵与矩阵运算
    什么是矩阵?这个概念还真不好解释,不过学过线性代数的人肯定都知道矩阵长什么样,那我在这里就不解释了。在D3D中,定义矩阵的结构体是D3DMATRIX:
typedef struct _D3DMATRIX {
    union {
        struct {
            float        _11, _12, _13, _14;
            float        _21, _22, _23, _24;
            float        _31, _32, _33, _34;
            float        _41, _42, _43, _44;
        };
        float m[4][4];
    };
} D3DMATRIX;
    看这个结构的样子,你就应该很清楚怎么使用它来定义一个矩阵了吧。在这里我顺便说一下C++中union的特性吧。像上面定义的结构体所示,在union里面有两个部分,一个是结构体,另一个是二维数组,它有16个元素。在union中,所有的成员都是共用一个内存块的,这是什么意思呢?继续看上面的代码,结构体中的成员_11和成员m数组的第一个元素是共用一个内存空间,即它们的值是一样的,你对_11赋值的同时也对m[0][0]进行了赋值,_11和m[0][0]的值是一样的。这样有什么好处呢?比如你定义了一个矩阵变量D3DMATRIX mat;你想访问矩阵中第三行第四列的元素,可以这样做:mat._34;另外也可以这样:mat.m[2][3](数组是从位置0开始储存的哦)。看起来使用后者比较麻烦,不过当你把中括号里面的数换成i和j,使用mat.m[i][j]来访问矩阵中的元素,你就应该知道它的好处了吧。
    实际上直接使用D3DMATRIX的情况不多,因为在D3DX中有个更好的结构体,那就是D3DXMATRIX。和D3DXVECTOR3相似,D3DXMATRIX是从D3DMATRIX继承过来的,它重载了很多运算符,使得矩阵的运算很简单。矩阵的运算方法我不打算多说了,下面只介绍和矩阵性质有关的三个函数。
    产生一个单位矩阵:D3DXMATRIX *D3DXMatrixIdentity(
                            D3DXMATRIX *pout);//返回结果
    求转置矩阵:D3DXMATRIX *D3DXMatrixTranspose(
    D3DXMATRIX *pOut,//返回的结果
    CONST D3DXMATRIX *pM );//目标矩阵
    求逆矩阵:D3DXMATRIX *D3DXMatrixInverse(
    D3DXMATRIX *pOut,//返回的结果
    FLOAT *pDeterminant,//设为0
    CONST D3DXMATRIX *pM );//目标矩阵
    至于什么是单位矩阵,什么是转置矩阵,什么是逆矩阵我就不说了,可以看一下线性代数的书,一看就明白了。简单的加减乘除法可以使用D3DXMATRIX结构体里面重载的运算符。两个矩阵相乘也可以用函数来实现,这将在接下来的矩阵变换中讲到。
·矩阵变换
矩阵的基本变换有三种:平移,旋转和缩放。
    平移:
D3DXMATRIX *D3DXMatrixTranslation(
    D3DXMATRIX* pOut,//返回的结果
    FLOAT x, //X轴上的平移量
    FLOAT y, //Y轴上的平移量
    FLOAT z) //Z轴上的平移量
;
    绕X轴旋转:
D3DXMATRIX *D3DXMatrixRotationX(
    D3DXMATRIX* pOut, //返回的结果
    FLOAT Angle //旋转的弧度
);
绕Y轴旋转:
D3DXMATRIX *D3DXMatrixRotationY(
    D3DXMATRIX* pOut, //返回的结果
    FLOAT Angle //旋转的弧度
);
绕Z轴旋转:
D3DXMATRIX *D3DXMatrixRotationZ(
    D3DXMATRIX* pOut, //返回的结果
    FLOAT Angle //旋转的弧度
);
    绕指定轴旋转:
    D3DXMATRIX *D3DXMatrixRotationAxis(  
            D3DXMATRIX *pOut,//返回的结果
        CONST D3DXVECTOR3 *pV,//指定轴的向量
            FLOAT Angle//旋转的弧度
);
    缩放:
D3DXMATRIX *D3DXMatrixScaling(
    D3DXMATRIX* pOut, //返回的结果
    FLOAT sx, //X轴上缩放的量
    FLOAT sy, //Y轴上缩放的量
    FLOAT sz  //Z轴上缩放的量
);
好了,这章就写这么一些东西。如果你觉得好像没学到什么的话,可能是因为不知道上面的知识有什么用吧。下一章我将介绍世界空间、视图空间(也叫摄像机空间)以及投影,这三者对应的是世界矩阵、视图矩阵和投影矩阵。搞清楚这三个空间的作用后,我们就可以利用这章的知识使我们的3D世界动起来了。
 
无论计算机图形技术如何发展,只要它以二维的屏幕作为显示介质,那么它显示的图像即使多么的有立体感,也还是二维的。有时我会想,有没有以某个空间作为显示介质的的可能呢,不过即使有,也只能是显示某个范围内的图像,不可能有无限大的空间作为显示介质,如果有,那就是现实世界了。
    既然显示器的屏幕是二维的,那么我们就要对图像作些处理,让它可以欺骗我们的眼睛,产生一种立体的真实感。在D3D中,这种处理就是一系列的空间变换,从模型空间变到世界空间,再变到视图空间,最后投影到我们的显示器屏幕上。
·世界空间与世界矩阵
    什么是模型空间呢?每个模型(3D物体)都有它自己的空间,空间的中心(原点)就是模型的中心。在模型空间里,只有模型上的不同点有位置的相对关系。那什么是世界空间呢?世界就是物体(模型)所存在的地方。当我们把一个模型放进世界里面去,那么它就有了一个世界坐标,这个世界坐标是用来标记世界中不同的模型所处的位置的。在世界空间里,世界的中心就是原点(0, 0, 0),也就是你显示器屏幕中间的那一点。我们可以在世界空间里摆放很多个模型,并且设置它们在世界空间中的坐标,这样模型与模型之间就有了相对的位置。
    世界矩阵有什么用呢?我们可以利用它来改变世界空间的坐标。这样,在世界空间里面的模型就可以移动、旋转和缩放了。
    我们可以使用上一章末尾所讲的那几个函数来产生世界矩阵。例如产生一个绕X轴旋转的转阵:D3DXMatrixRotationX(&matrix,1)。利用matrix这个矩阵,就可以使世界空间中的物体绕X轴转动1弧度。
    可以结合后面的例子来理解世界矩阵。
·视图空间与视图矩阵
    世界空间建立起来后,我们不一定能看到模型,因为我们还没有“眼睛”啊。在视图空间里,我们可以建立我们在三维空间中的眼睛:摄像机。我们就是通过这个虚拟的摄像机来观察世界空间中的模型的。所以视图空间也叫摄像机空间。
要建立起这个虚拟的摄像机,我们需要一个视图矩阵,产生视图矩阵的一个函数是:
D3DXMATRIX *D3DXMatrixLookAtLH(
    D3DXMATRIX* pOut,
    CONST D3DXVECTOR3* pEye,
    CONST D3DXVECTOR3* pAt,
    CONST D3DXVECTOR3* pUp
);
pOut返回的视图矩阵指针
pEye设置摄像机的位置
pAt设置摄像机的观察点
pUp设置方向“上”
这个函数的后缀LH是表示左手系的意思,聪明的你一定能够猜出肯定有个叫D3DXMatrixLookAtRH的函数。至于左手系和右手系的区别,这里就不多说了,记住左手系中的Z正方向是指向显示器里面的就行了。只能弄懂了视图矩阵的含义,建立视图矩阵完成可以不依赖函数,自己手动完成。视图矩阵其实就是定义了摄像机在世界空间中的位置、观察点、方向“上”这些信息。
    可以结合后面的例子来理解视图矩阵。
·投影与投影矩阵
    定义投影矩阵很像是定义摄像机的镜头,下面看它的函数声明:
D3DXMATRIX *D3DXMatrixPerspectiveFovLH(
    D3DXMATRIX* pOut,
    FLOAT fovY,
    FLOAT Aspect,
    FLOAT zn,
    FLOAT zf
);
pOut返回的投影矩阵指针
fovY定义镜头垂直观察范围,以弧度为单位。对于这个参数,下面是我的理解:如果定义为D3DX_PI/4(90度角),那么就是表示以摄像机的观察方向为平分线,上方45度角和下方45度角就是摄像机所能看到的垂直范围了。嗯,可以想象一下自己的眼睛,如果可以把自己眼睛的fovY值设为D3DX_PI/2(180度角),那么我们就可以不用抬头就看得见头顶的东西了。如果设为D3DX_PI的话。。。我先编译一下试试(building…)。哈哈,结果啥也看不见。很难想象如果自己能同时看到所有方向的物体,那么将是一个怎样的画面啊。
Aspect设置纵横比。如果定义为1,那么所看到的物体大小不变。如果定义为其它值,你所看到的物体就会变形。不过一般情况下这个值设为显示器屏幕的长宽比。(终于明白为什么有些人会说电视上的自己看起来会比较胖了……)
zn设置摄像机所能观察到的最远距离
zf设置摄像机所能观察到的最近距离
·一小段代码
    请看以下代码片段:
D3DXMATRIXA16 matWorld;
    D3DXMatrixIdentity( &matWorld );
    D3DXMatrixRotationX( &matWorld, timeGetTime()/1000.0f );
    g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
 
    D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
    D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
    D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
    D3DXMATRIXA16 matView;
    D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
    g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
 
    D3DXMATRIXA16 matProj;
    D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/2, 1.0f, 1.0f, 500.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
 
通过上面三个转换,就建立了一个我们可以通过显示器屏幕来观察的3D世界。上面三个转换分别是:
从模型空间到世界空间的世界转换:SetTransform( D3DTS_WORLD, &matWorld )。
从世界空间到视图空间的视图转换:SetTransform( D3DTS_VIEW, &matView )。
从视图空间到到屏幕的投影转换:SetTransform( D3DTS_PROJECTION, &matProj )。
现在来观察matWorld,matView,matProj这三个矩阵的特点。我们使用D3DXMatrixRotationX函数来产生了一个绕X轴旋转的转换矩阵,通过设置世界转换,在世界空间里面的物体将绕X轴作旋转。然后我们定义了三个三维的向量,用来设置摄像机的位置,观察方向和定义方向“上”。使用D3DXMatrixLookAtLH函数来把这三个向量放进视图矩阵里面去。然后通过设置视图转换,我们就建立了一个虚拟的摄像机。最后通过D3DXMatrixPerspectiveFovLH函数,我们得到一个投影矩阵,用来设置虚拟摄像机的镜头。
我还是解释一下上面说的那个方向“上”是什么东西吧。这个“上”其实指的就是摄像机在刚建立的时候是如何摆放的,是向左边侧着摆,还是向右边侧着摆,还是倒过来摆,都是通过这个方向“上”来指定的。按照正常的理解,摄像机的“上”方向就是Y轴的正方向,但是我们可以指定方向“上”为Y轴的负方向,这样世界建立起来后就是颠倒的了。不过颠倒与否,也是相对来说的了,试问在没有引力的世界中,谁能说出哪是上哪是下呢?是不是看得一头雾水啊?只要自己亲手改变一下这些参数,就可以体会到了。
设置上面三个转换的先后顺序并不一定得按照世界到视图到投影这个顺序,不过习惯上按照这种顺序来写,感觉会好一点。
·使用矩阵相乘来创建世界矩阵
    在世界空间中的物体运动往往是很复杂的,比如物体自身旋转的同时,还绕世界的原点旋转。怎么实现这种运动呢?通过矩阵相乘来把两个矩阵“混”在一起。现在我们假设某一物体建立在世界的原点上,看以下代码:
    //定义三个矩阵
    D3DXMATRIX matWorld, matWorldY,matMoveLeft;
    //一个矩阵把物体移到(30,0,0)处,一个矩阵使物体绕原点(0,0,0)旋转
    D3DXMatrixTranslation(&matMoveRight,30,0,0);
    D3DXMatrixRotationY(&matWorldY, radian/1000.0f);
//第一次矩阵相乘。先旋转,再平移
    D3DXMatrixMultiply(&matWorld, &matWorldY, &matMoveRight);
    //第二次矩阵相乘。在第一次矩阵相乘的结果上,再以Y轴旋转
    D3DXMatrixMultiply(&matWorld, &matWorld, &matWorldY);
    //设置世界矩阵
    m_pD3DDevice->SetTransform( D3DTS_WORLD, &matWorld );
    矩阵相乘的时候,矩阵的先后顺序很重要,如果顺序弄错了,物体就不会按我们预料的那样运动。从最后一次矩阵相乘看起,最后相乘的两个矩阵是matWorld和matWorldY,其中matWorld又是由matWorldY和matMoveRight相乘得来的,那么这三个矩阵相乘的顺序就是(matWorldY,matMoveRight,matWorldY)。这个顺序意味着什么呢?第一个matWorldY使物体绕Y轴旋转,这时候的物体还处于原点,所以它绕Y轴旋转也就是绕自身的旋转。它转呀转呀,这时候matMoveRight来了,它把物体从(0,0,0)移到了(30,0,0),这时候物体就不再是绕Y轴旋转了,它是在(30,0,0)这个位置继续绕自身旋转。然后matWorldY又来了,它使物体再次以Y轴旋转,不过此时物体不在原点了,所以物体就以原点为中心作画圆的运动(它自身的旋转仍在继续),这个圆的半径是30。如果换一个顺序,把matMoveRight放在第一的话,那么就是先移动再旋转再旋转(第二次旋转没用),这时候物体就只是画圆运动而已,它自身没有旋转。如果把matMoveRight放在最后,那么就是先旋转再旋转(第二次旋转没用)再移动,这时候物体就没有作画圆运动了,它只是在(30,0,0)这个位置上作自身旋转。好了,理解这个需要一点点想象力。你可以先写好几个矩阵相乘的顺序,自己想象一下相乘的结果会使物体作什么运动,然后再编译执行程序,看看物体的运动是不是和自己想像中的一样,这样可以锻炼自己的空间思维能力。
    好了,又写完一章了。下一章可能要过一些日子才能写。因为自己还没找到工作,国庆过后就得出发去找工了,接下来的日子要作一些找工前的准备,所以就没什么时间继续写了。至于什么时候写第七篇,呵呵,应该不用很久,找到工作后立刻回来这里报道~~大家祝我好运吧^_^

    其实DirectX9.0里有非常详细的教程和参考,大多数人只需要看看这些帮助就可以自己学习D3D了,我的这篇文章适合那些很懒但想快速入门、不懂英文或编程知识很欠缺的人看。装好DirectX9.0后,打开VC.net,新建一个Win32工程,在StdAfx.h里添加下面的语句:

FVF(Flexible Vertex Format) 是Direct3d中的可变顶点格式,通过它可以定义三角形的顶点格式,然后通过创建顶点缓冲区并设置渲染源来显示基本的图形。

D3DFVF_XYZ和D3DFVF_XYZRHW的区别是:

1.D3DXYZ默认的坐标系统用户区中心是 (0,0) 而rhw的左上角是 (0,0)
2.D3DXYZ默认的非光照的,而RHW默认就是高洛夫的光照模式。

在 RHW下需要设置
#define FVF_XYZ (D3DFVF_XYZ | D3DFVF_DIFFUSE)
g_pd3dDevice->SetRenderState(D3DRS_LIGHTING,FALSE)

而在 rhw下就不需要这样设置了。

先是一则消息:

2006年我国国内生产总值现价总量为159878亿元,比年快报核算数增多2.3万亿元,增加了10.7%,比目标8.0%高出2.0%。

一个很简单的主题,你的一个仇人爱上了你的女友,现在想要你退出,你是一个正常的人,你爱自己的女友。那个男人愿意出一点钱来补偿你。你多少钱可以把她卖掉?

有一家银行每天早上都在你的帐户里存入86,400
象有一家银行每天早上都在你的帐户里存入86,400, 
可是每天的帐户余额都不能结转到明天, 
一到结算时间, 
银行就会把你当日未用尽的款项全数删除。 
这种情况下你会怎幺做? 
当然, 
每天不留分文地全数提领是最佳选择。 
你可能不晓得, 
其实我们每个人都有这样的一个银行, 
她的名字是「时间(TIME)」。 

每天早上「时间银行」总会为你在帐户里自动存入86,400秒; 
一到晚上, 
她也会自动地把你当日虚掷掉的光阴全数注销, 
没有分秒可以结转到明天, 
你也不能提前预支片刻。 
如果你没能适当使用这些时间存款, 
损失掉的只有你自己会承担。 
没有回头重来,也不能预提明天, 
你必须根据你所拥有的这些时间存款而活在现在。 
你应该善加投资运用, 
以换取最大的健康、快乐与成功。 

时间总是不停地在运转,努力让每个今天都有最佳收获。 
想要体会「一年」有多少价值,你可以去问一个失败重修的学生。 
想要体会「一月」有多少价值,你可以去问一个不幸早产的母亲。 
想要体会「一周」有多少价值,你可以去问一个定期周刊的编辑。 
想要体会「一小时」有多少价值,你可以去问一对等待相聚的恋人。 
想要体会「一分钟」有多少价值,你可以去问一个错过火车的旅人。 
想要体会「一秒钟」有多少价值,你可以去问一个死里逃生的幸运儿。 
想要体会「一毫秒」有多少价值,你可以去问一个错失金牌的运动员。 
请珍视你所拥有的美好时光, 
特别是你可以和一些值得付出的人来分享这些时光。 
别忘了时间不等人。 

昨天以成为
历史, 
明天则遥不可知, 
而今天是一个礼物, 
所以英文把「现在」称为Present, 
请珍惜这份礼物 

<tbody>

    <tr>

        <td style="FONT-SIZE: 10pt" width="18%" height=42>

        <div style="FONT-SIZE: 10pt" align=left>    <strong>诸葛亮《前出师表》</strong>   <br></div>

        </td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left height=223>

        <div style="FONT-SIZE: 10pt" align=left>    臣亮言:先帝创业未半,而中道崩殂;今天下三分,益州疲敝,此诚危急存亡之秋也。然侍卫之臣,不懈于内;忠志之士,忘身于外者:盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体;陟罚臧否,不宜异同:若有作奸犯科,及为忠善者,宜付有司,论其刑赏,以昭陛下平明之治;不宜偏私,使内外异法也。侍中、侍郎郭攸之、费依、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下:愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰"能",是以众议举宠为督:愚以为营中之事,事无大小,悉以咨之,必能使行阵和穆,优劣得所也。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也!侍中、尚书、长史、参军,此悉贞亮死节之臣也,愿陛下亲之、信之,则汉室之隆,可计日而待也。<br><br>    臣本布衣,躬耕南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,谘臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间:尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧虑,恐付托不效,以伤先帝之明;故五月渡泸,深入不毛。今南方已定,甲兵已足,当奖帅三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都:此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、依、允等之任也。愿陛下托臣以讨贼兴复之效,不效则治臣之罪,以告先帝之灵;若无兴复之言,则责攸之、依、允等之咎,以彰其慢。陛下亦宜自谋,以谘诹善道,察纳雅言,深追先帝遗诏。臣不胜受恩感激!今当远离,临表涕泣,不知所云。 <br></div>

        </td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" width="18%" height=34>

        <div style="FONT-SIZE: 10pt" align=left>    <strong>诸葛亮《后出师表》</strong></div>

        </td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left height=79>

        <div style="FONT-SIZE: 10pt" align=left>    先帝虑汉、贼不两立,王业不偏安,故托臣以讨贼也。以先帝之明,量臣之才,故知臣伐贼,才弱敌强也。然不伐贼,王业亦亡。惟坐而待亡,孰与伐之?是故托臣而弗疑也。臣受命之日,寝不安席,食不甘味;思惟北征,宜先入南:故五月渡泸,深入不毛,并日而食。--臣非不自惜也:顾王业不可偏安于蜀都,故冒危难以奉先帝之遗意。而议者谓为非计。今贼适疲于西,又务于东,兵法"乘劳":此进趋之时也。谨陈其事如左:<br><br>    高帝明并日月,谋臣渊深,然涉险被创,危然后安;今陛下未及高帝,谋臣不如良、平,而欲以长策取胜,坐定天下:此臣之未解一也。刘繇、王朗,各据州郡,论安言计,动引圣人,群疑满腹,众难塞胸;今岁不战,明年不征,使孙策坐大,遂并江东:此臣之未解二也。曹操智计,殊绝于人,其用兵也,仿怫孙、吴,然困于南阳,险于乌巢,危于祁连,逼于黎阳,几败北山,殆死潼关,然后伪定一时耳;况臣才弱,而欲以不危而定之:此臣之未解三也。曹操五攻昌霸不下,四越巢湖不成,任用李服而李服图之,委任夏侯而夏侯败亡,先帝每称操为能,犹有此失;况臣弩下,何能必胜:此臣之未解四也。自臣到汉中,中间期年耳,然丧赵云、阳群、马玉、阎芝、丁立、白寿、刘合、邓铜等,及驱长屯将七十余人,突将无前,丛叟、青羌,散骑武骑一千余人,此皆数十年之内,所纠合四方之精锐,非一州之所有;若复数年,则损三分之二也。--当何以图敌:此臣之未解五也。今民穷兵疲,而事不可息;事不可息,则住与行,劳费正等;而不及今图之,欲以一州之地,与贼持久:此臣之未解六也。<br><br>    夫难平者,事也。昔先帝败军于楚,当此时,曹操拊手,谓天下已定。--然后先帝东连吴、越,西取巴、蜀,举兵北征,夏侯授首:此操之失计,而汉事将成也。--然后吴更违盟,关羽毁败,秭归蹉跌,曹丕称帝:凡事如是,难可逆见。臣鞠躬尽瘁,死而后已;至于成败利钝,非臣之明所能逆睹也。<br>   </div>

        </td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left height=163>    <strong>悼周瑜</strong><br>    呜呼公瑾,不幸夭亡!修短故天,人岂不伤?我心实痛,酹酒一觞;君其有灵,享我蒸尝!吊君幼学,以交伯符;仗义疏财,让舍以民。吊君弱冠,万里鹏抟;定建霸业,割据江南。吊君壮力,远镇巴丘;景升怀虑,讨逆无忧。吊君丰度,佳配小乔;汉臣之婿,不愧当朝,吊君气概,谏阻纳质;始不垂翅,终能奋翼。吊君鄱阳,蒋干来说;挥洒自如,雅量高志。吊君弘才,文武筹略;火攻破敌,挽强为弱。想君当年,雄姿英发;哭君早逝,俯地流血。忠义之心,英灵之气;命终三纪,名垂百世,哀君情切,愁肠千结;惟我肝胆,悲无断绝。昊天昏暗,三军怆然;主为哀泣;友为泪涟。亮也不才,丐计求谋;助吴拒曹,辅汉安刘;掎角之援,首尾相俦,若存若亡,何虑何忧?呜呼公瑾!生死永别!朴守其贞,冥冥灭灭,魂如有灵,以鉴我心:从此天下,更无知音!呜呼痛哉!伏惟尚飨。<br><br></td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left>    <strong>诫子书</strong><br>    夫君子之行,静以修身,俭以养德。非淡泊无以明志,非宁静无以致远。夫学须静也,才须学也,非学无以广才,非志无以成学。淫慢则不能励精,险躁则不能治性。年与时驰,意与日去,遂成枯落,多不接世,悲守穷庐,将复何及!<br>    译文:有道德修养的人,是这样进行修养锻炼的,他们以静思反省来使自己尽善尽美,以俭朴节约财物来培养自己高尚的品德。不清心寡欲就不能使自己的志向明确坚定,不安定清静就不能实现远大理想而长期刻苦学习。要学得真知必须使身心在宁静中研究探讨,人们的才能是从不断的学习中积累起来的;如果不下苦工学习就 不能增长与发扬自己的才干;如果没有坚定不移的意志就不能使学业成功。纵欲放荡、消极怠慢就不能勉励心志使精神振作;冒险草率、急燥不安就不能陶治性情使节操高尚。如果年华与岁月虚度,志愿时日消磨,最终就会像枯枝落叶般一天天衰老下去。这样的人不会为社会所用而有益于社会,只有悲伤地困守在自己的穷家破舍里,到那时再悔也来不及了。(兰溪诸葛村)[注:应网友 武春森要求] <br><br></td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left>    <strong>隆中对</strong><br><br><!--

            亮躬耕陇亩,好为《梁父吟》。身高八尺,每自比于管仲、乐毅,时人莫之许也。惟博陵崔州平、颍川徐庶元直与亮友善,谓为信然。<br>

            时先主屯新野。徐庶见先主,先主器之,谓先主曰:&#8220;诸葛孔明者,卧龙也,将军岂愿见之乎?&#8221;先主曰:&#8220;君与俱来。&#8221;庶曰:&#8220;此人可就见,不可屈致也。将军宜枉驾顾之。&#8221; <br>

            由是先主遂诣亮,凡三往,乃见。因屏人曰:&#8220;汉室倾颓,奸臣窃命,主上蒙尘。孤不度德量力,欲信大义于天大,而智太短浅,遂用猖獗,至于今日。然志犹未已,君谓计将安出?&#8221;<br>

        -->    亮答曰:&#8220;自董卓已来,豪杰并起,跨州连郡者不可胜数。曹操比于袁绍,则名微而众寡,然操遂能克绍,以弱为强者,非惟天时,抑亦人谋也。今操已拥百万之众,挟天子而令诸侯,此诚不可与争锋。孙权据有江东,已历三世,国险而民附,贤能为之用,此可以为援而不可图也。荆州北据汉、沔,利尽南海,东连吴会,西通巴、蜀,此用武之国,而其主不能守,此殆天所以资将军,将军岂有意乎?益州险塞,沃野千里,天府之土,高祖因之以成帝业。刘璋暗弱,张鲁在北,民殷国富而不知存恤,智能之士思得明君。将军既帝室之胄,信义著于四海,总揽英雄,思贤如渴,若跨有荆、益,保其岩阻,西和诸戎,南抚夷越,外结好孙权,内修政理;天下有变,则命一上将将荆州之军以向宛、洛,将军身率益州之众出于秦川,百姓孰敢不箪食壶浆以迎将军者乎?诚如是,则霸业可成,汉室可兴矣。&#8221;<br><br><!--

            先主曰:&#8220;善!&#8221;于是与亮情好日密。<br>

            关羽、张飞等不悦,先主解之曰:&#8220;孤之有孔明,犹鱼之有水也。愿诸君勿复言。&#8221;羽、飞乃止。<br><br>

            译文:诸葛亮亲自耕种田地,喜爱吟唱《梁父吟》。他身高八尺,常常把自己与管仲、乐毅相比,当时的人没有谁承认这一点。只有博陵崔州平,颖川的徐庶徐元直跟他交情很好,说是确实这样。 <br><br>

            当时刘备驻军在新野。徐庶拜见刘备,刘备很器重他,徐庶对刘备说:&#8220;诸葛孔明,是卧龙啊,将军可愿意见他吗?&#8221;刘备说:&#8220;您和他一起来吧。&#8221;徐庶说:&#8220;这个人只能到他那里去拜访,不能委屈他,召他上门来,您应当屈身去拜访他。&#8221; <br>

            于是刘备就去拜访诸葛亮,共去了三次,才见到。刘备于是叫旁边的人避开,说:&#8220;汉朝的天下崩溃,奸臣窃取了政权,皇上逃难出奔。我没有估量自己的德行,衡量自己的力量,想要在天下伸张大义,但是自己的智谋浅短、办法很少,终于因此失败,造成今天这个局面。但是我的志向还没有罢休,您说该采取怎样的计策呢?&#8221;<br>

        -->    译文:诸葛亮回答道:&#8220;自董卓篡权以来,各地豪杰纷纷起兵,占据几个州郡的数不胜数。曹操与袁绍相比,名声小,兵力少,但是曹操能够战胜袁绍,从弱小变为强大,不仅是时机好,而且也是人的谋划得当。现在曹操已拥有百万大军,挟制皇帝来号令诸侯,这的确不能与他较量。孙权占据江东,已经历了三代,地势险要,民众归附,有才能的人被他重用,孙权这方面可以以他为外援,而不可谋取他。荆州的北面控制汉、沔二水,一直到南海的物资都能得到,东面连接吴郡和会稽郡,西边连通巴、蜀二郡,这是兵家必争的地方,但是他的主人刘表不能守住,这地方大概是老天用来资助将军的,将军难道没有占领的意思吗?益州有险要的关塞,有广阔肥沃的土地,是自然条件优越,物产丰饶,形势险固的地方,汉高祖凭着这个地方而成就帝王业绩的。益州牧刘玲昏庸懦弱,张鲁在北面占据汉中,人民兴旺富裕、国家强盛,但他不知道爱惜人民。有智谋才能的人都想得到贤明的君主。将军您既然是汉朝皇帝的后代,威信和义气闻名于天下,广泛地罗致英雄,想得到贤能的人如同口渴一般,如果占据了荆州、益州,凭借两州险要的地势,西面和各族和好,南面安抚各族,对外跟孙权结成联盟,对内改善国家政治;天下形势如果发生了变化,就派一名上等的将军率领荆州的军队向南阳、洛阳进军,将军您亲自率领益州的军队出击秦川,老百姓谁敢不用竹篮盛着饭食,用壶装着酒来欢迎您呢?如果真的做到这样,那么汉朝的政权就可以复兴了。&#8221;<br><!--

            刘备说:&#8220;好!&#8221;从此同诸葛亮的情谊一天天地深厚了。<br>

            关羽、张飞等人不高兴了,刘备劝解他们说:&#8220;我有了孔明,就像鱼得到水一样。希望你们不要再说什么了。&#8221;关羽、张飞才平静下来。<br><br>

        --></td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left>    <strong>诫外甥书</strong><br><br>    夫志当存高远,慕先贤,绝情欲,弃疑滞。使庶几之志揭然有所存,恻然有所感。忍屈伸,去细碎,广咨问,除嫌吝,虽有淹留,何损于美趣,何患于不济。若志不强毅,意气不慷慨,徒碌碌滞于俗,默默束于情,永窜伏不庸,不免于下流。<br></td>

    </tr>

    <tr>

        <td style="FONT-SIZE: 10pt" align=left>    <strong>诸葛亮预言《马前课》</strong><br><br>    相传为诸葛亮于军中闲暇时写了一个《马前课》,是预测天下大事的书。从字面上讲,就是在出兵之前,在马前面占卜一课,即起卦的意思。诸葛亮的《马前课》非常好破译,每个朝代就一卦,这样往下排就是了。&nbsp;而中国历史上出现的其他很多预言书比较难破译,因为它讲历史大事,有的时候一个朝代可能有很多大事,有的朝代大事要少一些,不规律。&nbsp; <br><br>《马前课》&nbsp; <br>第一课 ○●●●●○ 中下&nbsp; <br>无力回天 鞠躬尽瘁&nbsp; <br>阴居阳拂 八千女鬼&nbsp; <br>证曰:阳阴阴阴阴阳在卦为颐&nbsp; <br>解曰:诸葛鞠躬尽瘁而死,后蜀汉后主降于魏&nbsp;&nbsp; <br>第二课 ○●○○●○ 中下&nbsp; <br>火上有火 光烛中土&nbsp; <br>称名不正 江东有虎&nbsp; <br>证曰:阳阴阳阳阴阳在卦为离&nbsp; <br>解曰:司马炎篡魏元帝都建康属江东&nbsp; <br> &nbsp; <br>第三课 ○●●●●● 下下&nbsp; <br>扰扰中原 山河无主&nbsp; <br>二三其位 手终马始&nbsp; <br>证曰:阳阴阴阴阴阴在卦为剥&nbsp; <br>解曰:五代始于司马终于杨氏&nbsp;&nbsp; <br><br>第四课 ●●○●○● 中上&nbsp; <br>十八男儿 起于太原&nbsp; <br>动则得解 日月丽天&nbsp; <br>证曰:阴阴阳阴阳阴在卦为解&nbsp; <br>解曰:李唐起于太原武□称周&nbsp; <br><br>第五课 ○○○●●● 下中&nbsp; <br>五十年中 其数有八&nbsp; <br>小人道长 生灵荼毒&nbsp; <br>证曰:阳阳阳阴阴阴在卦为否&nbsp; <br>解曰:五代八姓共五十三年&nbsp; <br><br>第六课 ●○○●○○ 上中&nbsp; <br>惟天生水 顺天应人&nbsp; <br>刚中柔外 土乃生金&nbsp; <br>证曰:阴阳阳阴阳阳在卦为兑&nbsp; <br>解曰:赵宋黄袍加身而立敌为金&nbsp; <br><br>第七课 ●○●○○● 中中&nbsp; <br>一元复始 以刚处中&nbsp; <br>五五相传 尔西我东&nbsp; <br>证曰:阴阳阴阳阳阴在卦为井&nbsp; <br>解曰:元代共十主后各汗国分裂&nbsp;&nbsp; <br><br>第八课 ○○●●●○ 上上&nbsp; <br>日月丽天 其色若赤&nbsp; <br>绵绵延延 凡十六叶&nbsp; <br>证曰:阳阳阴阴阴阳在卦为益&nbsp; <br>解曰:朱即赤日月是明共十六主&nbsp; <br><br>第九课 ○●○●●● 中上&nbsp; <br>水月有主 古月为君&nbsp; <br>十传绝统 相敬若宾&nbsp; <br>证曰:阳阴阳阴阴阴在卦为晋&nbsp; <br>解曰:水月有主是清也,古月是胡也,满清十皇朝最后亡于宣统&nbsp;&nbsp; <br><br>第十课 ●○●○●● 中下&nbsp; <br>豕后牛前 千人一口&nbsp; <br>五二倒置 朋来无咎&nbsp; <br>证曰:阴阳阴阳阴阴在卦为蹇&nbsp; <br>解曰:豕后牛前辛亥也千人一口为和,五二倒置是民也朋者外邦也&nbsp; <br><br>第十一课 ○●○○●○ 中下&nbsp; <br>四门乍辟 突如其来&nbsp; <br>晨鸡一声 其道大衰&nbsp; <br>证曰:阳阴阳阳阴阳在卦为离&nbsp; <br>解曰:当朝之象也四门乍辟谓为门户开放,酉年当期时无人再相信其道理故&nbsp;&nbsp; <br><br>第十二课 ●○○○○● 上中&nbsp; <br>拯患救难 是唯圣人&nbsp; <br>阳复而治 晦极生明&nbsp; <br>证曰:阴阳阳阳阳阴在卦为大过&nbsp; <br>解曰:当来之象也灾难当头之极,其时圣人出现救苦救难故 &nbsp; <br><br>第十三课 ○●●○○○ 上中&nbsp; <br>贤不遗野 天下一家&nbsp; <br>无名无德 光耀中华&nbsp; <br>证曰:阳阴阴阳阳阳在卦为大畜&nbsp; <br>解曰:世界大同之象&nbsp;&nbsp; <br><br>第十四课 ○●○●○● 中下&nbsp; <br>占得此课 易数乃终&nbsp; <br>前古后今 其道无穷&nbsp; <br>证曰:阳阴阳阴阳阴在卦为未济 <br></td>

    </tr>

</tbody>

初步涉及3D,被高手指出对D3D的硬件流水线不熟悉.马上找资料,并做总结.我可不能落后哎.

   先发张流水线的图:
  
 
一.system memory(系统内存)
  3D数据被CPU创建后,在进入流水线之前,会储存在系统内存中.之后,这些待决数据将通过数据总线传入显卡的AGP存储器或显存中.
二.1.vertex data(顶点数据)
   点是最基本的几何图元,一个三角形由三个顶点组成,一个矩形有四个。
   D3D中定义的顶点不只包含位置信息,还可以加入好多其他的要素.举个例子
   typedef struct vertex
 {
   FLOAT x,y,z;
   FLOAT u,v;
   DWORD color;
 }COSTUMVERTEX;
 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE | D3DFVF_TEXT1)
   这段代码就表示所定义的顶点包含位置,漫反射,纹理坐标的信息.这种形式称为FVF(Flexible Vertex Function)可变顶点格式.这些顶点数据会继续送入流水线下一级进行平移,旋转,光照,着色处理.
   2.Surface Data(页面数据)
  这是个啥东西以前还真没搞明白过,一直理解为2D中的surface,在D3D9SDK中,ddraw被去掉了,据说是运用3D流水线进行2D编程将比传统的2D流水线更高效><,今天算是碰到它了,借此机会一蹴而就!
   Andre的书上说,这种surface被称为high-order-surface,不是在CPU里创建的,使用的也不是自己定义的那个顶点格式,而是由一些数学函数直接在显卡里创建的,作用能够使网格模型更加平滑,还提到两个技术术语,一个N-Patches(貌似以前在哪见过...)和
TRUFORM meshes on ATI hardware(从没听说过,从字面上理解应该是ATI显卡特有的技术).完了,貌似有点深奥,时间紧迫,还是先放这儿了.....
三.1.Transform and Lighting(移动和光照)
   这部分作用就是让顶点组成的物体能够在虚拟世界中产生位移产生旋转以及进行光照计算,不然,整个世界不都黑忽忽的,不生动啊,不真实啊,现在人不都讲究个真实和诚信嘛!
   移动是用变换矩阵实现的,让组成物体的每个顶点乘上那个变换矩阵就OKAY了。光照稍微复杂一点,按光源分类有点光源(point light),平行光(directional),探照灯(spot light);按照性质有自发光(emisive),环境光(ambient),镜面反射(specular),漫反射(diffuse).在虚拟的环境中,这些光考虑的越周全,表现出的效果也就越真实,但渲染速度也会变慢的。
 2.Vertex Shader(顶点着色)
   分为flat着色和Gouraud着色,各有利弊吧.说的细点,flat着色是根据组成物体每个面的法线进行着色,做出的效果不平滑,如果对一个球体运用flat的话,效果会是disco舞厅天花板上的那个球装灯的样子.Gouraud着色根据各顶点的法线来计算插植进行着色,得到的效果要平滑的多。
                       
                              flat 
                  
                       Gouraud+specular
    还可以不用微软的API,自己用HLSL做算法,那个就太难了....(HIGH LEVEL SHADING LANGUAGE)   
四.clipping(裁减)
   这部很关键哦,能够有效减少GPU的运算量.在虚拟世界中,我们得到的是角都是有限的,就像在现实世界,人不可能看到身后的东西,除非后面长眼了,还有,太远的物体看不到,比如,美国人看不到本拉登在做什么,所以他们急啊,他们想找到那老头子....,太近了更看不到了,看到了可麻烦了,如果每个人都能看到细菌,那还吃饭吗?所以,在光栅化之前,要把理论上看不到的东西裁减掉,别让GPU运算,那没用啊。
五.1.MutiTexture(多重纹理)
    人靠衣服妆吧,总不能光着身子上大街,那也不好看啊。在虚拟世界里也一样,单单一个网格模型只不过是个骨架子,看上面那两个球就知道,很生硬吧.如果能加上纹理就会有生动的效果了哦。现在的显卡都能支持好多重纹理了,具体多少我也不清楚,十几层吧,你可以做个人物模型,先贴上皮肤,再贴上内衣内裤,再贴上秋衣秋库,再贴上毛衣毛库.....
   2.Pixel Shader(象素着色)
    就是产生象素级别的颜色控制,做的好的话能够使模型展现一流的效果,更加平滑,更加真实.比如柔顺的绣发,光滑富有质感的皮肤.....,API里目前我还不知道有啥,HLSL肯定能做.
 
六 FOG(雾化效果)
   想要朦胧感?雾化一下吧,参考PS上的寂静岭1......那个终年被浓雾笼罩的小镇.
 
七.stencil/depth/alpha test(色深,深度,啊而法测试)
   在将"渲"好的世界投影在屏幕之前还是做下物体遮挡测试,现实中肯定不会有人拥有透视眼吧,能看到墙后面的物体,那可绝了!那穿衣服也没啥用了....
   stencil和depth我还没分太清楚,好象都是进行物体的象素对比.depth可以用z-buffer或w-buffer运算,stencil还不清楚,先放这儿了.alpha是标识物体透明度的,比如,水总是透明的吧,丝绸也有透明的吧.玻璃总透明吧.....
八.frame buffer
   一切的一切都准备好了,送入帧缓冲,准备把美丽的3D世界投影到屏幕上吧^^

其实,微软在Vista激活上的态度十分宽松,如果不是此前某些组织做得过火——擅自架设Vista激活服务器,微软甚至愿意“悄悄”提供给用户免激活长期使用Vista的方式。

事实上,只需将Vista注册表中一个键值从0改为1,就可以无限次延长Vista激活最后期限——微软甚至在自己的Technet网站上提供了相关说明文档。

不过,某些 不良PC销售商有可能利用这一“漏洞”欺骗消费者,声称提供正版Vista激活——而用户至少要到几个月甚至一年后才会发现。

当然,相对目前网络上流行的Vista破解方式,这种“合法”途径需要进入注册表修改,而且有可能需要多次进行激活,并算不上方便,不适合普通用户使用,对注册表较为了解的用户倒是可以尝试一下。

The following describes the Registry key that’s involved.

Step 1. While running a copy of Windows Vista that hasn’t yet been activated, click the Start button, type regedit into the Search box, then press Enter to launch the Registry Editor.

Step 2. Explore down to the following Registry key:

HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Windows NT \ CurrentVersion \ SL

Step 3. Right-click the Registry key named SkipRearm and click Edit. The default is a Dword (a double word or 4 bytes) with a hex value of 00000000. Change this value to any positive integer, such as 00000001, save the change, and close the Registry Editor.

Step 4. Start a command prompt with administrative rights. The fastest way to do this is to click the Start button, enter cmd in the Search box, then press Ctrl+Shift+Enter. If you’re asked for a network username and password, provide the ones that log you into your domain. You may be asked to approve a User Account Control prompt and to provide an administrator password.

Step 5. Type one of the following two commands and press Enter:

slmgr -rearm
or
rundll32 slc.dll,SLReArmWindows

Either command uses Vista’s built-in Software Licensing Manager (SLMGR) to push the activation deadline out to 30 days after the command is run. Changing SkipRearm from 0 to 1 allows SLMGR to do this an indefinite number of times. Running either command initializes the value of SkipRearm back to 0.

Step 6. Reboot the PC to make the postponement take effect. (After you log in, if you like, you can open a command prompt and run the command slmgr -xpr to see Vista’s new expiration date and time.

Step 7. To extend the activation deadline of Vista indefinitely, repeat steps 1 through 6 as necessary

VS1.1.7内部测试版:

修改了vs1.1.6的一些已知的bug,对于dota积分规则的改进现在经过测试发现仍有问题,但是vs1.1.7能够更好的支持vista

VS1.1.6内部测试版(测试群共享目录也可下载):

1 For Vista !
  全面支持vista的vs客户端,欢迎各位vista爱好者下载测试,特别的由于考虑兼容vista,这个版本做了不小的改动,因此请广大热心玩家都多多下载,在xp以及其他windows操作系统上面做测试,以避免新开发带来的新的bug
  
2 For Dota  !
  为了Dota用户特别定制了RPG新的计分规则,规则允许对抗性RPG游戏中,对手一方真人控制的玩家全部退出(包括掉线)以后,本方立即获胜(无需再继续拆基地),计算得分的公式保持不变。确认对方玩家全部退出后,本方即可随之退出比赛,系统不会再记录这个行为为负。请参与测试的dota爱好者把这个客户端分发给你的好友,争取保证测试时本方所有队员都使用这个测试版本,否则可能会由于客户端不一致影响测试效果。
  
3 我的收藏房间
  右键点击房间,菜单里面可以选择添加为自己的收藏房间,收藏房间会被列到你客户端显示的第一个。这个功能主要是给网吧网管用的,呵呵,大家要是金额的好自己也可以用用吧
  
这个版本还修正了以往出现的一些bug,特别是CS1.6积分的某些bug,欢迎VS所有爱好者下载测试,支持VS,支持电子竞技!

加群的各位请注意,群目前人比较多,群主要的目的是内部测试,加入群的人应该具有热情和责任感,应该有责任反馈测试情况,包括测试结果正常或者不正常的情况
如果仅仅只是等着要用最新功能,不愿意履行测试义务的用户可以等待内部测试阶段结束,版本正式开放,你也一样可以得到最新的功能


http://www.cnblogs.com/Files/flying_bat/VS1.1.7.zip