Rise的自留地

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

0%

源码及素材下载

大爆炸,烟雾痕迹甚至魔术飞弹尾部发出的微小火花,都是粒子(particle)所制造出来的特殊效果。在适当的时机,启用alpha混合并绘制粒子,这样粒子就能朝向观察点(使用公告板),得到的结果就是混合对象的抽象拼贴,他们可以用于创建一些奇妙的效果。

粒子奇妙的地方就在于粒子的大小实际上是任意的,原因在于可以创建一个缩放矩阵,使其同粒子多边形的世界变换矩阵结合起来。也就是说,除非粒子纹理不同,否则只需要使用一个多边形来绘制所有的粒子,无论如何,多边形的数目都必须同纹理的数目保持一致。

还需要创建粒子图像,图像中心为一个实心(不透明)圆形,向图像的边缘延伸,图像逐渐变透明,如下图所示:



接着,需要设置4个顶点,这4个顶点使用了2个多边形(可以使用三角形带进行优化)。顶点的坐标表示粒子的缺省大小,稍后需要将粒子进行缩放,以适合这个大小。每个粒子都可以拥有独特的属性,包括粒子颜色(通过使用材质来实现)。

接下来,将这个结构体同一个含有两个多边形(创建一个正方形)的顶点缓冲结合起来,以便将多边形渲染到3D设备上。在被绘制出来之前,每个粒子都需要通过它自己的世界矩阵进行定向(当然使用公告板)。然后将世界变换矩阵同每个粒子的缩放变换矩阵组合起来,再设置一个材质(使用 IDirect3DDevice::SetMaterial函数),用来改变粒子的颜色。最后,绘制粒子。

完整源码如下所示:

点击下载源码和素材

公告板(billboard)是一种允许在2D对象出现在3D中的很酷的技术,公告板的原理就是通过使用世界矩阵,根据观察点来排列多边形,因为观察的角度已知(或能够获得一个观察变换矩阵),就只需要使用相反的观察角来构造矩阵。创建公告板世界矩阵的方法是从Direct3D获取当前的观察矩阵并将此矩阵转置。这个转置矩阵会将所有的东西进行恰当的定位,以朝向观察点。接着就只需应用网格的平移矩阵,在世界中正确地确定网格的位置。

源码中的Setup_Mesh函数用来创建顶点缓冲和从文件取得纹理数据,其中用到了D3DXCreateTextureFromFileEx函数,来看看它的使用信息:

Creates a texture from a file. This is a more advanced function than D3DXCreateTextureFromFile.

Syntax

HRESULT WINAPI D3DXCreateTextureFromFileEx(LPDIRECT3DDEVICE9 pDevice,
    LPCTSTR pSrcFile,
    UINT Width,
    UINT Height,
    UINT MipLevels,
    DWORD Usage,
    D3DFORMAT Format,
    D3DPOOL Pool,
    DWORD Filter,
    DWORD MipFilter,
    D3DCOLOR ColorKey,
    D3DXIMAGE_INFO *pSrcInfo,
    PALETTEENTRY *pPalette,
    LPDIRECT3DTEXTURE9 *ppTexture
);

Parameters

pDevice
[in] Pointer to an IDirect3DDevice9 interface, representing the device to be associated with the texture.

pSrcFile
[in] Pointer to a string that specifies the filename. If the compiler settings require Unicode, the data type LPCTSTR resolves to LPCWSTR. Otherwise, the string data type resolves to LPCSTR. See Remarks.

Width
[in] Width in pixels. If this value is zero or D3DX_DEFAULT, the dimensions are taken from the file and rounded up to a power of two. If the device supports non-power of 2 textures and D3DX_DEFAULT_NONPOW2 is specified, the size will not be rounded.

Height
[in] Height, in pixels. If this value is zero or D3DX_DEFAULT, the dimensions are taken from the file and rounded up to a power of two. If the device supports non-power of 2 textures and D3DX_DEFAULT_NONPOW2 is sepcified, the size will not be rounded.

MipLevels
[in] Number of mip levels requested. If this value is zero or D3DX_DEFAULT, a complete mipmap chain is created. If D3DX_FROM_FILE, the size will be taken exactly as it is in the file, and the call will fail if this violates device capabilities.

Usage
[in] 0, D3DUSAGE_RENDERTARGET, or D3DUSAGE_DYNAMIC. Setting this flag to D3DUSAGE_RENDERTARGET indicates that the surface is to be used as a render target. The resource can then be passed to the pNewRenderTarget parameter of the IDirect3DDevice9::SetRenderTarget method. If either D3DUSAGE_RENDERTARGET or D3DUSAGE_DYNAMIC is specified, Pool must be set to D3DPOOL_DEFAULT, and the application should check that the device supports this operation by calling IDirect3D9::CheckDeviceFormat. D3DUSAGE_DYNAMIC indicates that the surface should be handled dynamically. See Using Dynamic Textures.

Format
[in] Member of the D3DFORMAT enumerated type, describing the requested pixel format for the texture. The returned texture might have a different format from that specified by Format. Applications should check the format of the returned texture. If D3DFMT_UNKNOWN, the format is taken from the file. If D3DFMT_FROM_FILE, the format is taken exactly as it is in the file, and the call will fail if this violates device capabilities.

Pool
[in] Member of the D3DPOOL enumerated type, describing the memory class into which the texture should be placed.

Filter
[in] A combination of one or more D3DX_FILTER controlling how the image is filtered. Specifying D3DX_DEFAULT for this parameter is the equivalent of specifying D3DX_FILTER_TRIANGLE | D3DX_FILTER_DITHER.

MipFilter
[in] A combination of one or more D3DX_FILTER controlling how the image is filtered. Specifying D3DX_DEFAULT for this parameter is the equivalent of specifying D3DX_FILTER_BOX.

ColorKey
[in] D3DCOLOR value to replace with transparent black, or 0 to disable the color key. This is always a 32-bit ARGB color, independent of the source image format. Alpha is significant and should usually be set to FF for opaque color keys. Thus, for opaque black, the value would be equal to 0xFF000000.

pSrcInfo
[in, out] Pointer to a D3DXIMAGE_INFO structure to be filled in with a description of the data in the source image file, or NULL.

pPalette
[out] Pointer to a PALETTEENTRY structure, representing a 256-color palette to fill in, or NULL.

ppTexture
[out] Address of a pointer to an IDirect3DTexture9 interface, representing the created texture object.

Return Value

If the function succeeds, the return value is D3D_OK.

If the function fails, the return value can be one of the following:

D3DERR_INVALIDCALL The method call is invalid. For example, a method’s parameter may have an invalid value.
D3DERR_NOTAVAILABLE This device does not support the queried technique.
D3DERR_OUTOFVIDEOMEMORY Direct3D does not have enough display memory to perform the operation.
D3DXERR_INVALIDDATA The data is invalid.
E_OUTOFMEMORY Direct3D could not allocate sufficient memory to complete the call.

Remarks

The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateTextureFromFileExW. Otherwise, the function call resolves to D3DXCreateTextureFromFileExA because ANSI strings are being used.

Use D3DXCheckTextureRequirements to determine if your device can support the texture given the current state.

This function supports the following file formats: .bmp, .dds, .dib, .hdr, .jpg, .pfm, .png, .ppm, and .tga. See D3DXIMAGE_FILEFORMAT.

Mipmapped textures automatically have each level filled with the loaded texture. When loading images into mipmapped textures, some devices are unable to go to a 1x1 image and this function will fail. If this happens, then the images need to be loaded manually.

For the best performance when using D3DXCreateTextureFromFileEx:

Doing image scaling and format conversion at load time can be slow. Store images in the format and resolution they will be used. If the target hardware requires power of 2 dimensions, then create and store images using power of 2 dimensions.
For mipmap image creation at load time, filter using D3DX_FILTER_BOX. A box filter is much faster than other filter types such as D3DX_FILTER_TRIANGLE.
Consider using DDS files. Since DDS files can be used to represent any Microsoft DirectX 9.0 texture format, they are very easy for D3DX to read. Also, they can store mipmaps, so any mipmap-generation algorithms can be used to author the images.

我们来看看Do_Frame是如何进行公告板的绘制的,以下是关键代码:

技巧如下:

字体的绘制需要使用ID3DXFont对象和 D3DXCreateFontIndirect函数。

DirectX SDK文档对ID3DXFont做了简要的说明:

The ID3DXFont interface encapsulates the textures and resources needed to render a specific font on a specific device.

The ID3DXFont interface is obtained by calling D3DXCreateFont or D3DXCreateFontIndirect.

我们来看看SDK文档提供的关于 D3DXCreateFontIndirect的使用说明:

Creates a font object indirectly for both a device and a font.

Syntax

HRESULT WINAPI D3DXCreateFontIndirect(LPDIRECT3DDEVICE9 pDevice,
    CONST D3DXFONT_DESC *pDesc,
    LPD3DXFONT *ppFont
);

Parameters

pDevice
[in] Pointer to an IDirect3DDevice9 interface, the device to be associated with the font object.

pDesc
[in] Pointer to a D3DXFONT_DESC structure, describing the attributes of the font object to create. If the compiler settings require Unicode, the data type D3DXFONT_DESC resolves to D3DXFONT_DESCW; otherwise, the data type resolves to D3DXFONT_DESCA. See Remarks.

ppFont
[out] Returns a pointer to an ID3DXFont interface, representing the created font object.

Return Value

If the function succeeds, the return value is D3D_OK.

If the function fails, the return value can be one of the following:

D3DERR_INVALIDCALL The method call is invalid. For example, a method’s parameter may have an invalid value.
E_OUTOFMEMORY Microsoft Direct3D could not allocate sufficient memory to complete the call.

Remarks

The compiler setting also determines the function version. If Unicode is defined, the function call resolves to D3DXCreateFontIndirectW. Otherwise, the function call resolves to D3DXCreateFontIndirectA because ANSI strings are being used.

该函数的第二个参数使用了一个结构体D3DXFONT_DESC,来看看它的具体定义:

Defines the attributes of a font.

Syntax

typedef struct D3DXFONT_DESC {
    INT Height;
    UINT Width;
    UINT Weight;
    UINT MipLevels;
    BOOL Italic;
    BYTE CharSet;
    BYTE OutputPrecision;
    BYTE Quality;
    BYTE PitchAndFamily;
    TCHAR FaceName[LF_FACESIZE];
} D3DXFONT_DESC;

Members

Height
Height, in logical units, of the font’s character cell or character.

Width
Width, in logical units, of characters in the font.

Weight
Weight of the font in the range from 0 through 1000.

MipLevels
Number of mip levels requested. If this value is zero or D3DX_DEFAULT, a complete mipmap chain is created. If the value is 1, the texture space is mapped identically to the screen space.

Italic
Set to TRUE for an Italic font.

CharSet
Character set.

OutputPrecision
Output precision. The output precision defines how closely the output must match the requested font height, width, character orientation, escapement, pitch, and font type.

Quality
Output quality.

PitchAndFamily
Pitch and family of the font.

FaceName
A null-terminated string or characters that specifies the typeface name of the font. The length of the string must not exceed 32 characters, including the terminating null character. If FaceName is an empty string, the first font that matches the other specified attributes will be used.

If the compiler settings require Unicode, the data type TCHAR resolves to WCHAR; otherwise, the data type resolves to CHAR. See Remarks.

Remarks

The compiler setting also determines the structure type. If Unicode is defined, the D3DXFONT_DESC structure type resolves to a D3DXFONT_DESCW; otherwise the structure type resolves to a D3DXFONT_DESCA.

Possible values of the above members are given in the Microsoft Windows Graphics Device Interface (GDI) LOGFONT  structure.

当然,在设置的时候我们不需要设置所有的属性,只要设置其中的几个主要属性就可以了。

我们可以这样设置字体的属性并创建字体:

技巧如下:

在设置可变顶点格式时加入法线和漫反色,如下所示:

// The 3D vertex format and descriptor
typedef struct
{
    
float x, y, z;      // 3D coordinates    
    float nx, ny, nz;   // normals
    D3DCOLOR diffuse;   // color
} VERTEX;

#define VERTEX_FVF   (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE)

开启光照和Z缓存:

    // enable d3d lighting
    g_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
    
// enable z-buffer
    g_d3d_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

设置光源属性并打开光源:

    D3DLIGHT9 light;

    // set light data, color, position, range.
    ZeroMemory(&light, sizeof(light));

    light.Type      
= D3DLIGHT_POINT;
    light.Diffuse.r 
= light.Ambient.r = 0.5;
    light.Diffuse.g 
= light.Ambient.g = 0.5;
    light.Diffuse.b 
= light.Ambient.b = 0.0;
    light.Diffuse.a 
= light.Ambient.a = 1.0;
    light.Range         
= 1000.0;
    light.Attenuation0  
= 0.5;
    light.Position.x    
= 300.0;
    light.Position.y    
= 0.0;
    light.Position.z    
= -600.0;

    
// set and enable the light
    g_d3d_device->SetLight(0&light);
    g_d3d_device
->LightEnable(0, TRUE);

完整源码如下:

/***************************************************************************************
PURPOSE:
    Light Demo

Required libraries:
  WINMM.LIB, D3D9.LIB, D3DX9.LIB.
 **************************************************************************************
*/

#include 
<windows.h>
#include 
<stdio.h>
#include 
"d3d9.h"
#include 
"d3dx9.h"

#pragma comment(lib, 
"winmm.lib")
#pragma comment(lib, 
"d3d9.lib")
#pragma comment(lib, 
"d3dx9.lib")

#pragma warning(disable : 
4305)

#define WINDOW_WIDTH    400
#define WINDOW_HEIGHT   400

#define Safe_Release(p) if((p)) (p)->Release();

// window handles, class and caption text.
HWND g_hwnd;
HINSTANCE g_inst;
static char g_class_name[] = "LightClass";
static char g_caption[]    = "Light Demo";

// the Direct3D and device object
IDirect3D9* g_d3d = NULL;
IDirect3DDevice9
* g_d3d_device = NULL;

// The 3D vertex format and descriptor
typedef struct
{
    
float x, y, z;      // 3D coordinates    
    float nx, ny, nz;   // normals
    D3DCOLOR diffuse;   // color
} VERTEX;

#define VERTEX_FVF   (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE)

IDirect3DVertexBuffer9
* g_vertex_buffer = NULL;

//--------------------------------------------------------------------------------
// Window procedure.
//--------------------------------------------------------------------------------
long WINAPI Window_Proc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    
switch(msg)
    {
    
case WM_DESTROY:
        PostQuitMessage(
0);
        
return 0;
    }

    
return (long) DefWindowProc(hwnd, msg, wParam, lParam);
}

//--------------------------------------------------------------------------------
// Initialize d3d, d3d device, vertex buffer, texutre; set render state for d3d;
// set perspective matrix and view matrix.
//--------------------------------------------------------------------------------
BOOL Do_Init()
{
    D3DPRESENT_PARAMETERS present_param;
    D3DDISPLAYMODE  display_mode;
    D3DXMATRIX mat_proj, mat_view;
    D3DLIGHT9 light;
    BYTE
* vertex_ptr;

    
// initialize vertex data
    VERTEX verts[] = {
        { 
-100.0f,  100.0f-100.0f0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f,  100.0f-100.0f0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f-100.0f-100.0f0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f-100.0f-100.0f0.0f,0.0f,-1.0f, D3DCOLOR_RGBA(255,255,255,255) },

        {  
100.0f,  100.0f-100.0f1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f,  100.0f,  100.0f1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f-100.0f-100.0f1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f-100.0f,  100.0f1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },

        {  
100.0f,  100.0f,  100.0f0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f,  100.0f,  100.0f0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        {  
100.0f-100.0f,  100.0f0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f-100.0f,  100.0f0.0f,0.0f,1.0f, D3DCOLOR_RGBA(255,255,255,255) },

        { 
-100.0f,  100.0f,  100.0f-1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f,  100.0f-100.0f-1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f-100.0f,  100.0f-1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) },
        { 
-100.0f-100.0f-100.0f-1.0f,0.0f,0.0f, D3DCOLOR_RGBA(255,255,255,255) }
    }; 

    
// do a windowed mode initialization of Direct3D
    if((g_d3d = Direct3DCreate9(D3D_SDK_VERSION)) == NULL)
        
return FALSE;

    
// retrieves the current display mode of the adapter
    if(FAILED(g_d3d->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &display_mode)))
        
return FALSE;

    ZeroMemory(
&present_param, sizeof(present_param));

    
// initialize d3d presentation parameter
    present_param.Windowed               = TRUE;
    present_param.SwapEffect             
= D3DSWAPEFFECT_DISCARD;
    present_param.BackBufferFormat       
= display_mode.Format;
    present_param.EnableAutoDepthStencil 
= TRUE;
    present_param.AutoDepthStencilFormat 
= D3DFMT_D16;

    
// creates a device to represent the display adapter
    if(FAILED(g_d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,
                                  D3DCREATE_SOFTWARE_VERTEXPROCESSING, 
&present_param, &g_d3d_device)))
        
return FALSE;     

    
// set render state

    
// enable d3d lighting
    g_d3d_device->SetRenderState(D3DRS_LIGHTING, TRUE);
    
// enable z-buffer
    g_d3d_device->SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);

    
// create and set the projection matrix

    
// builds a left-handed perspective projection matrix based on a field of view
    D3DXMatrixPerspectiveFovLH(&mat_proj, D3DX_PI/4.01.333331.01000.0);

    
// sets a single device transformation-related state
    g_d3d_device->SetTransform(D3DTS_PROJECTION, &mat_proj);

    
// create and set the view matrix
    D3DXMatrixLookAtLH(&mat_view, 
                       
&D3DXVECTOR3(0.00.0-500.0),
                       
&D3DXVECTOR3(0.0f0.0f0.0f), 
                       
&D3DXVECTOR3(0.0f1.0f0.0f));

    g_d3d_device
->SetTransform(D3DTS_VIEW, &mat_view);

    
// create the vertex buffer and set data
    g_d3d_device->CreateVertexBuffer(sizeof(VERTEX) * 160, VERTEX_FVF, D3DPOOL_DEFAULT, &g_vertex_buffer, NULL);

    
// locks a range of vertex data and obtains a pointer to the vertex buffer memory
    g_vertex_buffer->Lock(00, (void**)&vertex_ptr, 0);

    memcpy(vertex_ptr, verts, 
sizeof(verts));

    
// unlocks vertex data
    g_vertex_buffer->Unlock();

    
// set light data, color, position, range.
    ZeroMemory(&light, sizeof(light));

    light.Type      
= D3DLIGHT_POINT;
    light.Diffuse.r 
= light.Ambient.r = 0.5;
    light.Diffuse.g 
= light.Ambient.g = 0.5;
    light.Diffuse.b 
= light.Ambient.b = 0.0;
    light.Diffuse.a 
= light.Ambient.a = 1.0;
    light.Range         
= 1000.0;
    light.Attenuation0  
= 0.5;
    light.Position.x    
= 300.0;
    light.Position.y    
= 0.0;
    light.Position.z    
= -600.0;

    
// set and enable the light
    g_d3d_device->SetLight(0&light);
    g_d3d_device
->LightEnable(0, TRUE);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Release all d3d resource.
//--------------------------------------------------------------------------------
BOOL Do_Shutdown()
{
    Safe_Release(g_vertex_buffer);
    Safe_Release(g_d3d_device);
    Safe_Release(g_d3d);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Render a frame.
//--------------------------------------------------------------------------------
BOOL Do_Frame()
{
    D3DXMATRIX mat_world;

    
// clear device back buffer
    g_d3d_device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_RGBA(000255), 1.0f0);

    
// Begin scene
    if(SUCCEEDED(g_d3d_device->BeginScene()))
    {
        
// create and set the world transformation matrix
        
// rotate object along y-axis
        D3DXMatrixRotationY(&mat_world, (float) (timeGetTime() / 1000.0));
        
        g_d3d_device
->SetTransform(D3DTS_WORLD, &mat_world);

        
// set the vertex stream, shader.

        
// binds a vertex buffer to a device data stream
        g_d3d_device->SetStreamSource(0, g_vertex_buffer, 0sizeof(VERTEX));

        
// set the current vertex stream declation
        g_d3d_device->SetFVF(VERTEX_FVF);

        
// draw the vertex buffer
        for(short i = 0; i < 4; i++)
            g_d3d_device
->DrawPrimitive(D3DPT_TRIANGLESTRIP, i * 42);

        
// end the scene
        g_d3d_device->EndScene();
    }

    
// present the contents of the next buffer in the sequence of back buffers owned by the device
    g_d3d_device->Present(NULL, NULL, NULL, NULL);

    
return TRUE;
}

//--------------------------------------------------------------------------------
// Main function, routine entry.
//--------------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE inst, HINSTANCE, LPSTR cmd_line, int cmd_show)
{
    WNDCLASSEX  win_class;
    MSG         msg;

    g_inst 
= inst;

    
// create window class and register it
    win_class.cbSize        = sizeof(win_class);
    win_class.style         
= CS_CLASSDC;
    win_class.lpfnWndProc   
= Window_Proc;
    win_class.cbClsExtra    
= 0;
    win_class.cbWndExtra    
= 0;
    win_class.hInstance     
= inst;
    win_class.hIcon         
= LoadIcon(NULL, IDI_APPLICATION);
    win_class.hCursor       
= LoadCursor(NULL, IDC_ARROW);
    win_class.hbrBackground 
= NULL;
    win_class.lpszMenuName  
= NULL;
    win_class.lpszClassName 
= g_class_name;
    win_class.hIconSm       
= LoadIcon(NULL, IDI_APPLICATION);

    
if(! RegisterClassEx(&win_class))
        
return FALSE;

    
// create the main window
    g_hwnd = CreateWindow(g_class_name, g_caption, WS_CAPTION | WS_SYSMENU, 00,
                          WINDOW_WIDTH, WINDOW_HEIGHT, NULL, NULL, inst, NULL);

    
if(g_hwnd == NULL)
        
return FALSE;

    ShowWindow(g_hwnd, SW_NORMAL);
    UpdateWindow(g_hwnd);

    
// initialize game
    if(Do_Init() == FALSE)
        
return FALSE;

    
// start message pump, waiting for signal to quit.
    ZeroMemory(&msg, sizeof(MSG));

    
while(msg.message != WM_QUIT)
    {
        
if(PeekMessage(&msg, NULL, 00, PM_REMOVE))
        {
            TranslateMessage(
&msg);
            DispatchMessage(
&msg);
        }
        
        
// draw a frame
        if(Do_Frame() == FALSE)
            
break;
    }

    
// run shutdown function
    Do_Shutdown();

    UnregisterClass(g_class_name, inst);
    
    
return (int) msg.wParam;
}

效果图:


       文/本报记者 邓艳玲
  摄影/本报记者 吕家佐
  2007年06月28日青年周末

  “抓住陈晓旭一说,大家都登了,反而我的观点就出来了,说些不痛不痒的
话,谁理你!”尽管上遭卫生部副部长、国家中医药管理局局长王国强怒斥“这
是借人们喜爱的影视演员攻击中医药的言论,很不严肃、很不道德、很不科
学!”,下遇普通网民拍砖“信口雌黄,无聊至极”,何祚庥反而认为自己借陈
晓旭一事来说中医,是“非常讲策略的”,也达到了让人们注意中医存在严重问
题的目的。

  5月底,当多数国人还在为“林妹妹”陈晓旭的红颜薄命扼腕叹息时,一个
非常“不和谐”的声音传出:是中医害死了陈晓旭。发出这个声音的正是中国科
学院院士、著名反伪斗士何祚庥。他声称自己就是根据报纸上对陈晓旭去世的报
道,得出这个结论的。

  “他这不是瞎胡闹吗?”在网上,他被强烈地质疑和谩骂着。就在他几乎被
唾沫淹没之时,6月14日,卫生部副部长王国强在接受采访时,对他进行了严苛
的评价。第二日,几乎所有媒体都报道和转载了这一说法,何祚庥也似乎陷入了
官方和民间的双重谴责永无翻身之日。但第二日,他就在网上写了一篇措辞毫不
客气的文章对王国强加以反驳。

  一直以来,何祚庥就以反对伪科学和学术腐败为公众熟知,而他也因为敢于
直言,在多个领域直言为引来无数争议,此次他借陈晓旭得乳腺癌致死来抨击中
医甚至引来了官方人士的骂声,这个顽固的80岁老人难道真的觉得自己一点错都
没有?

  中医就应该为陈晓旭得死负责
  报纸上的新闻报道足以让我下结论

  青年周末(以下简称为“青周”):您是在什么情形下说“陈晓旭是中医害
死的”?

  何祚庥:我参加全国科技活动周,在南宁给学生们做关于反对伪科学和学术
腐败的讲座,有学生问到有关中医的问题,我就说了。

  青周:是您刻意提到了陈晓旭之死吗?

  何祚庥:学生主动问起,我在回答中也就不回避。当时,这是个非常热的事
情,我在去南宁的飞机上看的几乎每份报纸上都有对她去世情况、她治病的情况
的详细报道。

  青周:您只是通过看报纸也没经过严密的调查研究,就下这么一个肯定的判
断,合适吗?

  何祚庥:我和陈晓旭不熟,也只在电视上看过她演的角色,我能去做什么调
查研究?也没这个必要,根据各大报纸几乎相同的报道,提供的细节,足以让我
下一个基本判断。

  青周:还有不少人看完报道也认为,是陈本人因为讳疾忌医而不愿意看病导
致的结果呢!怎么就单单指中医害死她呢?

  何祚庥:事实上她是看的中医,吃的中药。如果她是不愿意就医,既不愿意
就中医,也不愿意就西医,因为自己的信仰而死,我没话可说。但事实上她是选
择了看中医,吃中药。报道中并没有说她看的中医提醒她去做详细准确的诊断,
如果中医也说过这样的话,那就是陈晓旭自己负责任了。

  像季羡林遇到的好中医太少

  青周:那这也说明,这只是陈看的那个中医有问题,怎么能因为一个人的问
题进而指责整个行业呢?

  何祚庥:中医就没有癌症的概念!更谈不上有能力去治疗癌症。但相当的中
医大夫都宣称自己能治癌,还有众多治癌的特殊办法,也就是“偏方”。

  中医都是这样。季羡林在《病榻杂记》说起自己治病的遭遇,他遇到的那个
中医邹铭西算是一个好医生,他不能确诊,就让季羡林另请高明,但这只是极少
数。季得的也只是天疱疮,还不是什么癌症之类大病。很多中医是敢宣称,专治
西医都看不了的疑难杂症的。

  青周:凭什么西医都治不了的疑难杂症,中医也一定就治不了呢?

  何祚庥:那你中医先把西医能够治愈的大病先治好再说。我们来数一下,历
史上的对人类影响的大病是中医治愈或者说可以治愈的?肺结核、伤寒、疟疾、
鼠疫、霍乱、天花、盲肠炎(中医又称为绞肠痧)……我不是专业医生,我数不
过来,你可以到医院去问,到病人中去问嘛。

  你还可以去翻翻巴金的《家》、《春》、《秋》,林语堂的《京华烟云》,
去看看鲁迅的书,太多的文学作品中都有中医怎么耽误病人的。从五四以来,太
多的先进知识分子都是反对中医的:梁启超、鲁迅、傅斯年、郭沫若……

  我自己就出生在上海一个封建大家庭,家里看病一定是找上海当时著名的老
中医夏应堂。他在我们那个大家庭也是治死了不少人的。我父亲就是其中一个。
我父亲26岁去世,得了伤寒。(接着马上起身拿来一本书《走出寄啸山庄》),
这本书是我一个堂兄所作,讲的就是我们这个大家庭的事情,在这本书里面,他
就好几处提到了夏应堂,夏应堂把他一个姐姐,得天花就没有看出来,几乎治得
送命,把一个姑姑治成了傻子。

  一般的伤风、感冒、泻肚子、拉稀中医倒是可以治,但这些小病,很多时候
不治也是可以痊愈的。在旧社会,如果大病中医治不了,就有这样一种说法,可
以治病不能治命,得了大病命中就该死的。

  青周:西医也照样成批成批的治死人,您为什么只攻击中医,而不去说说西
医的弊端呢?这能说明中医就比西医差吗?

  何祚庥:那不一样。西医也会死人,但西医能够告诉你,为什么治不好。中
医不是,它治不好了,就告诉你命中该死。盲肠炎,中医叫绞肠痧,不知道怎么
治,很多时候只能等死;瘟病,就是我们现在说的霍乱,也只能等死。

  夏应堂的儿子叫夏理彬,也是个名中医。夏应堂去世后,就是夏理彬给我们
家人看病,就用上温度计了,他也认为温度计比用手摸要准确多了。还有一代人
文大师陈寅恪,他家可是中医世家,他都说中医是要不得的。

  中医学泰斗反对中医现代化
  农民也都认疗效显著的西医

  青周:中医在我们人民心目中地位还是很高的,这种高地位恐怕也是它长期
以来的疗效形成的口碑吧?

  何祚庥:那你就错了,我首先问你,有了大病的时候,你是先看西医还是中
医?现在就是在农村,观念也扭转过来了。(何祚庥的爱人庆承瑞插话:我们
(上个世纪)50-60年代下乡的时候,农民朋友最认的就是青霉素针,如果发烧
得厉害了,他们最希望的就是医生给打一针青霉素。)1965年的时候,我在农村
搞四清,有一位社员,得了胃溃疡,快要穿孔了,当时我立即做决定把这位社员
紧急送到医院,做手术,治好了。社员们说,要靠中医,这位社员就完了。

  现在中医院都是靠西医在维持

  青周:说到医院,现在中医院这么多,不照样在行使治病救人的职责,如果
都不能治病救人,也不可能生存下去吧?

  何祚庥:(庆承瑞:最近三个学生铊中毒,第一确诊的就是在中医院。这难
道是中医能诊断出来的?分明是西医吧!现在的中医院,你去看看,那里的设备
和普通的医院完全一样。)现在你去中医院的诊疗,来了以后先做的检查,那一
套完全是西医的套路啊。开的中药里面掺了西药。现在很多所谓的中药里面真正
起疗效的是西药成分,只不过是打着个中药的旗号,卖的却是西药。但是这些都
不让说。

  完全标准的传统中医院活不下去

  青周:即便是现在中医不能治大病,不代表她不会继续发展,今后不能治大
病啊,况且大病不也都是由小病发展来的么?为什么不用发展的眼光来看中医?

  何祚庥:(庆承瑞插话:中医已经存在两千年了,要发展,也早就应该发展
成熟了;但问题是,现在的中医泰斗们却在那里说,中医不能变,变了就不是中
医了。)他们认为中医的现代化是个错误的口号,认为中医现代化就等于消灭中
医,这种认识在中医界是占主导地位的。(庆承瑞:最近在广州开了一个“挺中
医”的大型研讨会,所谓“挺中医”认为现在中医院的做法是完全错误的,现在
用西医的办法来改造中医也绝对是错误的。)

  对现在中医院的格局他们是坚决反对的,他们认为就应该只使用中医的望闻
问切,他们倒是主张要办完全标准的传统的中医院。 这个我赞成!你办吧!所
有的现代化装备都不要,连温度计也不要,你就要靠手摸!你就和普通的医院比
一下,看到底谁最终能活下去!

  我只否定中医的90%
  讲10%精华还是给中医面子

  青周:说来说去,您其实就是要借陈晓旭的死来全盘否定中医罗?

  何祚庥:我没有全盘否认,我只否认90%,我说中医90%是糟粕,10%是精华。
是他们说我全盘否认,是他们放大了。我没说要取消中医,但我说要反对中医的
90%。现在有人硬说我何祚庥全盘否定中医,是故意歪曲我的理念。他们说不出
来反对我的理由,就扣个帽子。

  青周:中医90%是糟粕,10%是精华,您怎么得出这两个数据?

  何祚庥:历史上的大病,对人类有影响的,天花、伤寒、疟疾、鼠疫、肺结
核等等,都是西医治愈的,不但治愈了,而且把病原病理都弄得很清楚了。我数
出的大病,你能治一个就是10%。还有好多人说我给出这个比例还是给中医面子
呢。

  青周:您作为一个搞自然科学的人,最讲究精确的数据了,这样笼统地说
90%和10%好像并不符合科学家的一贯做法?

  何祚庥:我讲过中医的阴阳五行理论是“伪科学”,阴阳五行理论只是中医
的一部分,但我很遗憾,因为这是中医的指导思想,这种错误的指导思想影响的
行动比较严重,所以我说它90%是糟粕。

  中西医没法结合

  青周:那10%的精华又指的是什么呢?

  何祚庥:(庆承瑞:无非就是指几千年的中国民间的医学积累了一些有用的
经验,也许这可以算作精华,你要问我,精华具体表现在什么地方,我就说有一
味中药叫鸦胆子可以来治孩子手上的瘊子)还有就是我小时候曾经手脱臼,是中
医的手法弄好了。后来,我把这件事在网上说了,又有好多人告诉我,这在西医
是入门常识,是学生在学校里就要学的初级知识,这个我就不知道,到底是西医
是学了中医的,还是两家本来就有的。

  青周:您看,现在是人为地把中医和西医分成两个水火不容的体系,但事实
上,中医有些东西和西医道理是相通的,那为什么要把它们弄得那么泾渭分明,
水火不容呢?中西结合一起发展不好吗?

  何祚庥:发展一个医学体系,最重要的是科学的理论基础作为指引,中医的
理念是不准对人体解剖,西医的解剖,要看神经血管,这两者怎么融合?这可不
是我的看法。医学界很多人都这样看,举个例子,钟南山院士就说了,中西医的
理论没法结合。

  中西医已经结合50年了,结合得怎么样?(50年不是太短了吗?西医好几百
年,中西几千年呢?)你以为就我们关心吗?那些搞医学的人比我们关心多了,
不是时间长短的问题,问中医大夫,也问中央卫生部,而是根本没法结合的问题。
理论体系无法结合。

  抗击非典,都说中西医结合做出来的成就,我就问一个问题,我们因为非典
牺牲了200多位医生和护士,在这群人中,有多少是中医?我2003年就在网上提
了这个问题,但没人敢回答我。如果一个也不牺牲,就算重大贡献,我不相信!
现在都说功劳是中西医结合,我就问中医大夫牺牲了多少位?

  治病不能靠文化

  青周:您把有用的东西就认为是西医的,中医即使有用的也是些没来由的雕
虫小技,这公平吗?

  何祚庥:公不公平,推不推崇哪个,要看事实,不是人为拔高,把不好的东
西非说成是好的。中医一些有用的东西是可以归到现代医学里去,大量的还是归
不进去的,而且还不知道怎么回事。90%糟粕,10%精华,我已经做了定量的分析
了,应该说我已经说得够到位了。

  青周:即使如您所说,您怎么就判断这10%就不是中医的主流呢?而且,那
90%也许是以我们目前心智尚不能开掘出来的博大精深的内容?您的划分仅仅依
靠目前历史上的大病诊疗记录就行了么?

  何祚庥:靠这个难道还不够吗?如果作为医学,绝大多数大病都不能治,那
我还能说它是个先进的东西吗?它倒是宣称它可以治愈大病,但它从来不去治现
在西医可以治愈的病证,都是我们可以判断的大病之外的,现在谁也不知道怎么
回事的,疑难杂症,它就说它可以治了!

  中医自己说,“医者,意也”,意念的意;有时候又说,“医者,艺也”,
艺术的艺;有时候又说,“医者,易也”,易经的易;就是不说,医者,科也。
现在他们又说中医是文化,我倒是同意他们这种说法,但治病不能靠文化治吧。

  正式署名骂我的还就是王国强一人
  中医在癌症的诊断上就该全部否定

  青周:您怎么看待您说出对中医的看法就遭来骂声?

  何祚庥:那有什么关系,我讲的是事实,别人谩骂,事实还就是事实。包括
卫生部副部长,他官大,说我“极不严肃,极不道德,极不科学”,我就真的这
样了么?他说“世界上每天都有很多人去世,难道都是中医害死的?更何况,癌
症的治疗是所有疾病中死亡率最高的。”我可没那样说,我说的是陈晓旭啊,说
的是乳腺癌。

  多数癌症死亡率是高,但乳腺癌不是,如果早发现,80%是能够治愈的。他
作为一个卫生部的副部长,就应该告诉人们,应该普及这样的常识;而且同时作
为中医药管理局的局长,他应该知道中医药的短处,应该告诉人们,中医是不能
治乳腺癌的,应该看西医。中医在癌症的诊断上面就该全部否定!

  他现在是袒护啊。而且他还说何祚庥说了这种情况之后,就是不道德。这作
为一个部长说这样的话,太差劲了吧。你是对人民负责呢?还是对你手下的中医
负责?

  讲出让人不高兴得事实被说不道德

  青周:您自己并不是医生,对中医的了解会多过一个管理中医药的官员?如
此下结论的确不足以让人信服,感觉太武断?

  何祚庥:我讲的也不是什么高深的东西,常识而已。我的确不懂医学,有人
比我更懂。(他起身又拿来一本杂志《抗癌之窗》最新一期2007年6月号,他非
常认真地把封二上面的主管主办单位:中华人民共和国卫生部,中国医学科学院,
顾问委员会主席吴阶平等人的名字读出,以示这份杂志的专业性和权威性。翻到
《陈晓旭不该红颜薄命》这一篇。)写这个文章的人自己在文中称自己是乳腺癌
专科医生。他在文章中所讲的,和我所讲的几乎是一个意思。这可是他卫生部主
管的一份权威杂志啊,人家也是这样讲的,我把乳腺癌可以防治,早发现可以用
西医方法治愈的事实讲出来了,难道不道德了吗?

  事实上是,卫生部门长期在做一些乌七八糟的事情。(翻倒杂志的另一页,
《抗癌仙姑“还阳草”骗局始末》读起有关已被他划上线的句子,先是法院1991
年法院对这一诈骗当事人判了6年的有期徒刑。),你看,1982年年底,经推荐,
又获当时卫生部主要领导同意,王淑华的“还阳草”居然进入中国中医研究院所
属的广安门医院,开始了代号为831的临床验证,最后因为无效而告终。这就可
以看见这就是他们的传统,中医领导部门会支持这些没有科学根据的偏方,甚至
是诈骗,宣称能够治癌。是我武断,还是我尖锐,还是我说的事实让人不高兴了
呢?我说话不算尖锐。鲁迅,比我尖锐多了。他甚至说,中医是有意无意的骗子!
当然鲁迅比我更有资格说这样的话,鲁迅学过现代医学。

  我坚持的是我弄懂的科学常识

  青周:为什么那么多人要站出来独独骂你呢?

  何祚庥:网上骂我的人都是匿名的,说明那些人找不到可以驳倒我的理由,
只好躲在阴暗的角落里骂骂我。倒是正式署名的就是我们的王国强副部长。

  那些主张一分为二来看中医的人,也承认我讲出了一些事实的真相,中医会
害人,是事实,没法回避的事实,但这个事实让他们感到不舒畅。

  青周:但也有不少网友也在说中医治好他们病的例子,为什么他们的说法您
不采信呢?

  何祚庥:证明一个全称肯定的说法,孤证不立,要打倒一个全称肯定的命题,
一个个案就行了。其实你真要知道情况,你应该去问问医院的大夫,治癌症的大
夫,你就可以知道事实到底是什么。现在西医虽然他们在实践中发挥重要作用,
但他们不敢对中医说什么,他们也没地方去说。连我这样一个中央卫生部管不着
的圈外人士说了一句话,就遭到副部长的迎头痛击,在他们管辖下的西医大夫,
还敢说什么批评性意见?!

  现在不是提倡创新吗?创新难道不需要怀疑精神吗?中医是不准怀疑的。谁说
中医坏话,就是反对传统文化,全盘打倒传统文化,这没道理啊。

  青周:就是您说,您为什么不用一种能够让普通老百姓接受的方式方法把您
认为正确的东西传达出来,而不让人反感呢?

  何祚庥:我觉得我现在的方法很好,抓住陈晓旭一说,大家都登了,反而我
的观点就出来了,说些不痛不痒的话,谁理你?有人说我炒作,我炒作什么?再
炒作我也不会成为著名大夫,也不会有人找我治病!牵涉到社会公众健康,我认
为我讲的这种观点需要向社会公众传播。何祚庥从来没说,我的意见都科学,我
也没说我什么都懂;我很多不懂,但我现在坚持的是我弄懂了的,而且是普通科
学常识。

译者申明:

这些指南是我在阅读 DirectX9.0 SDK 中逐步翻译出来的。对于初次接触 DirectX Graphics 的编程者而言,这应该是很好的上手资料。其实,本人就是从这些指南开始深入 Direct3D9.0 的;由于这是本人第一次翻译英文材料,言语不通,词不达意之处一定很多,一些术语也译得很勉强,请见谅。

此外,需要转载此文者,请保留以下部分:

———————————————————————–

DirectX图形接口指南 译者:Rise 电子邮箱: Rise.Worlds@gmail.com

———————————————————————–

DirectX 图形接口指南:(应用于 DirectX 9.0 版 C/C++ 编程) 
本区域的指南将说明如何在 C/C++ 程序中使用 Microsoft Direct3D 和 Direct3DX 完成一些普通的工作。这些工作总是被分解成若干个必要的步骤。在某些情况下,为了使表达更清楚,一些步骤还被细分成几个子步骤。 

本区域提供的指南有:


· 指南一:创建设备 

· 指南二:演示顶点 

· 指南三:使用矩阵 

· 指南四:创建和使用光源 

· 指南五:使用纹理映射 

· 指南六:使用Mesh模型 


提示:指南中出现的示例代码来自于每个指南具体提供的路径里的源文件。 


这些指南中的源代码是用 C++ 写成的。如果使用C编译器,你必须适当的改变这些文件使它们能够编译通过。最少的,你需要加入 vtable 然后用它引用接口函数。 

包含在示例代码中的一些注解可能与来自 Microsoft Platform Software Development Kit (SDK) 中的源代码不同。这些改变仅仅为了简化表述并且只限于注解中,这样能够防止示例程序的行为被改变。


指南一:创建设备 

为了使用 Microsoft Direct3D,你首先需要创建一个应用程序窗口,并紧接着创建和初始化 Direct3D 对象。你应该使用这些对象提供的 COM 接口来操纵它们,以及创建描绘一个场景所必需的其它对象。本指南包含的 CreateDevice 示例将例示并说明以下几个工作:创建 Direct3D 设备并且绘制一个简单的蓝色屏幕。 

这个指南使用以下步骤:初始化 Direct3D,绘制场景,以及最后清理与关闭。 


·步骤一:创建一个窗口 

·步骤二:初始化 Direct3D 

·步骤三:处理系统消息 

·步骤四:绘制与显示场景 

·步骤五:关闭与清除 


注意:CreateDevice 示例程序的路径在: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut01_CreateDevice. 


步骤一:创建一个窗口

任何 Microsoft Windows 程序执行中必须要作的第一件事就是创建一个应用程序窗口并将其显示给用户。为做到这点,CreateDevice 例程将首先实现它的 WinMain 函数。以下示例代码完成了窗口的初始化。 

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT ) 



// Register the window class. 

WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, 

GetModuleHandle(NULL), NULL, NULL, NULL, NULL, 

“D3D Tutorial”, NULL }; 

RegisterClassEx( &wc ); 


// Create the application’s window. 

HWND hWnd = CreateWindow( “D3D Tutorial”, “D3D Tutorial 01: CreateDevice”, 

WS_OVERLAPPEDWINDOW, 100, 100, 300, 300, 

GetDesktopWindow(), NULL, wc.hInstance, NULL ); 


前述示例代码是标准的 Windows 编程。例子开始时定义和注册了一个窗口类名为 “D3D Tutorial”。类注册以后,示例代码使用已注册的类创建了一个基本的顶层(top-level)窗口,客户区域为 300 像素宽,300 像数高。这个窗口没有菜单或子窗口。示例使用了 WS_OVERLAPPEDWINDOW 属性创建一个包括最大化,最小化,以及关闭按钮的普通窗口。(如果该例程将运行在全屏模式下,首选的窗口属性应该是WS_EX_TOPMOST,它指定创建的窗口置于并且保持在所有非最高(non-topmost)窗口之前,甚至在窗口失活的情况下。)一旦窗口创建完成,例代码调用标准的 Microsoft Win32 函数显示和更新窗口。 


在应用程序窗口准备好以后,你就能开始设置具体的 Microsoft Direct3D 对象了, 

请见:步骤二:初始化 Direct3D 


步骤二:初始化 Direct3D 

CreateDevice 示例在 WinMain 中创建窗口之后,调用该程序定义的函数 InitD3D 完成 Microsoft Direct3D 初始化过程。在创建窗口之后,程序已经准备好初始化你将用来绘制场景的 Direct3D 对象了。这个过程包括创建一个 Direct3D 对象,设置Present Parameters,以及最后创建 Direct3D 设备。 

创建完 Direct3D 对象之后,你可以立即使用 IDirect3D8::CreateDevice 方法创建 Direct3D 设备。你也能够使用 Direct3D 对象枚举设备,类型,模式以及其他东西。这些工作的代码段应位于使用 Direct3DCreate8 函数创建 Direct3D 对象之后。 

if( NULL == ( g_pD3D = Direct3DCreate8( D3D_SDK_VERSION ) ) ) 

return E_FAIL; 


传递给 Direct3DCreate8 的唯一参数应该始终是 D3D_SDK_VERSION,它告诉 Direct3D 当前使用的头文件信息。无论如何,头文件或者其他的变化将导致这个值增加并强制使用该值的应用程序重新编译。如果此版本不匹配,调用 Direct3DCreate8 将失败。 


下一个步骤是使用 IDirect3D8::GetAdapterDisplayMode 接口找到当前的显示模式,代码如下: 


D3DDISPLAYMODE d3ddm; 

if( FAILED( g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) ) 

return E_FAIL; 


D3DDISPLAYMODE 结构中的 Format 变量将被用于创建 Direct3D 设备。如果是运行于窗口模式下的话,Format 参数通常用来创建一个与适配器当前模式相匹配的后背缓冲 (Back buffer)。 


在给 D3DPRESENT_PARAMETERS 各参数赋值时,你必须指定你的应用程序在3D下工作的方式。本 CreateDevice 例程设置D3DPRESENT_PARAMETERS结构中 Windowed 为 TRUE,SwapEffect 为 D3DSWAPEFFECT_DISCARD,BackBufferFormat 为 d3ddm.Format。 


D3DPRESENT_PARAMETERS d3dpp; 

ZeroMemory( &d3dpp, sizeof(d3dpp) ); 

d3dpp.Windowed = TRUE; 

d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; 

d3dpp.BackBufferFormat = d3ddm.Format; 


最后一步,是利用 IDirect3D8::CreateDevice 函数创建 Direct3D 设备,代码如下: 

if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, 

D3DCREATE_SOFTWARE_VERTEXPROCESSING, 

&d3dpp, &g_pd3dDevice ) ) ) 


前述代码使用 D3DADAPTER_DEFAULT 标志创建了一个使用省缺适配器的设备。在非常多数的情况下,系统只有一个适配器,除非它安装了多个图形加速卡。通过把 DeviceType 参数设成 D3DDEVTYPE_HAL,表示你希望获得一个实际硬件设备 (hardware device) 而不是软件虚拟设备 (software device)。示例代码还使用 D3DCREATE_SOFTWARE_VERTEXPROCESSING 标志通知系统使用软件顶点处理 (software vertex processing)。注意,如果你指定 D3DCREATE_HARDWARE_VERTEXPROCESSING 标志通知系统使用硬件顶点处理 (hardware vertex processing),你可以在支持硬件顶点处理的图形加速卡上得到大幅度的性能提升。 


现在 Direct3D 已经初始化完毕,下一步是确保你的程序具有一个机制用来来处理系统消息, 

见下文:步骤三:处理系统消息 


步骤三:处理系统消息 

完成创建程序窗口以及初始化 Direct3D 以后,你已经准备好绘制场景 (Render scene)。大多数情况下,Microsoft Windows 程序在它们的消息循环里监视系统消息,并且在队列里没有消息时绘制画面帧。然而,CreateDevice 例程仅仅在等到一个WM_PAINT出现在队列里时,才通知应用程序重绘窗口的所有部分。 

// The message loop. 

MSG msg; 

while( GetMessage( &msg, NULL, 0, 0 ) ) 



TranslateMessage( &msg ); 

DispatchMessage( &msg ); 



当每循环一次,DispatchMessage 调用 MsgProc,后者负责处理队列里的消息,当 WM_PAINT 消息进队时,调用该程序自身定义的函数 Render(),它将负责重绘窗口。然后 Microsoft Win32 函数 ValidateRect 执行并将整个客户区域设为有效。 

消息处理函数的例代码如下: 

LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam ) 



switch( msg ) 



case WM_DESTROY: 

PostQuitMessage( 0 ); 

return 0; 

case WM_PAINT: 

Render(); 

ValidateRect( hWnd, NULL ); 

return 0; 



return DefWindowProc( hWnd, msg, wParam, lParam ); 




现在,应用程序处理了系统消息,接着的一步是绘制显示,见:步骤四:绘制与显示场景 

步骤四:绘制与显示场景 

为了描绘和显示需要的场景,本例程在这一步把后背缓冲 (back buffer) 填充为蓝色,然后将此后背缓冲的内容传给前景缓冲 (front buffer), 并且将前景缓冲提交至屏幕。 

清除表面,应调用 IDirect3DDevice8::Clear 函数: 

// Clear the back buffer to a blue color 

g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0 ); 

Clear() 接受的前两个参数通知 Microsoft Direct3D 被清除的矩形区域数组的基址和大小,该矩形区域数组描述了绘制目标表面 (render target surface) 里需要清除的区域。 

在大多数情况下,只使用单个矩形覆盖整个绘制目标表面。这样你只需设置第一个参数为 0 及第二个参数为 NULL。第三个参数将决定方法的行为,你可以通过设置特定的标志用来清除绘制目标表面 (render target surface),关联的Z缓冲 (associated depth buffer),模版缓冲 (stencil buffer),以及任意这三者的混合。本指南不使用Z缓冲,所以仅仅使用了 D3DCLEAR_TARGET 标志。最后三个参数分别用于设置对应绘制目标表面、Z缓冲和模版缓冲的清除填充值 (reflect clearing values)。该 CreateDevice 例程将绘制目的表面的清除填充色设置为蓝色 (D3DCOLOR_XRGB(0,0,255)。由于相应的标志没有设置,最后两个参数被 Clear() 忽略。 

在清除了视口 (viewport) 之后,CreateDevice 例程告知 Direct3D 绘图将要开始,然后立即通知这次绘制完成,见以下代码段: 

// Begin the scene. 

g_pd3dDevice->BeginScene(); 

// Rendering of scene objects happens here. 

// End the scene. 

g_pd3dDevice->EndScene(); 

当绘制开始或完成时,IDirect3DDevice8::BeginScene 和 IDirect3DDevice8::EndScene 函数将用信号通知系统。你只能在这两函数之间调用其它的绘图函数。即使调用绘图函数失败,你也应该在重新调用 BeginScene 之前调用 EndScene。 

绘制完之后,调用 IDirect3DDevice8::Present显示该场景: 

g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); 

Present() 接受的前两个参数是原始矩形和目标矩形。在这一步,例程设置这两个参数为 NULL 并把整个后备缓冲提交到前景缓冲。第三个参数用于设置该次提交的目标窗口。因为这个参数被设为 NULL,实际使用的窗口是 D3DPRESENT_PARAMETERS 的 hWndDeviceWindow 成员。第四个是 DirtyRegion 参数,在绝大多数情况下应该设为 NULL。 

本指南的最终步骤是关闭应用程序,见:步骤五:关闭与清除 

步骤五:关闭与清除 

在执行的若干时刻,你的应用程序必须立即关闭。关闭一个 Direct3D 应用程序中不只是意味着你必须销毁程序窗口,并且你还要释放程序中使用过的的任何 Direct3D 对象并且无效化它们的指针。当收到一个 WM_DESTROY 消息时,CreateDevice 例程通过调用一个本地定义的函数 Cleanup() 来处理这些工作。 

VOID Cleanup() 



if( g_pd3dDevice != NULL) 

g_pd3dDevice->Release(); 

if( g_pD3D != NULL) 

g_pD3D->Release(); 



上述函数对每个对象调用 IUnknown::Release 方法来释放它们自身。由于DirectX遵循 COM 规则,大多数对象当其引用计数降为0时,DirectX会自动的从内存中释放这个对象。 

对于其他关闭程序情况,可能发生在程序的平常执行中——比如用户改变了桌面的参数或色深——此时你可能需要撤销和重建使用中的 Microsoft Direct3D 对象。因此一个好的主意就是将你的释放代码放到一起,以便能在需要时随时调用它。 

本指南已经说明了如何创建一个设备,指南二:演示顶点(Render Vertex) ,将告诉你如何用顶点(Vertex)创建几何形体。 

指南二:演示顶点(Render Vertex) 

Microsoft Direct3D 写的应用程序使用顶点(Vertex)构造几何物体。每一个三维空间 (3D) 场景包括一个或几个这样的几何物体。Vertices 例程构造简单的物体,一个三角形,并且将它绘制到显示屏上。 

本指南说明如何采用以下步骤从顶点构造一个三角形: 


·第一步:定义一个自定义顶点类型 

·第二步:设置顶点缓冲 

·第三步:绘制至显示屏 

注意:Vertices 示例程序的路径为: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut02_Vertices. 

Vertices 程序的示例代码与 CreateDevice 的代码大部分相同。本“演示顶点(Render Vertex)”指南仅仅关注于那些独特的,关于顶点的代码而不包括初始化 Direct3D,处理 Microsoft Windows 消息,绘图,与清理等工作。如要得到有关这些任务的信息,请参考 指南一:创建设备。 

第一步:定义一个自定义顶点类型 

Vertices 例程使用三个顶点构造一个 2D 的三角形。这里提及了顶点缓冲的概念,这是用于保存和演示大量顶点的 Microsoft Direct3D 对象。通过指定一个自定义的顶点结构和相应的可变向量格式 (FVF),顶点能够采用很多方法定义。本 Vertices 例程使用的顶点格式定义于以下代码片断中。 

struct CUSTOMVERTEX 



FLOAT x, y, z, rhw; // The transformed position for the vertex. 

DWORD color; // The vertex color. 

}; 

上面的结构体说明了自定义顶点类型的格式。下一步是定义 FVF 以描述顶点缓冲区中的顶点内容。以下代码片段定义了一个 FVF 并符合此上建立的自定义顶点类型。 

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZRHW|D3DFVF_DIFFUSE) 

可变顶点格式标记描述了使用中的自定义顶点类型。前述示例代码使用了 D3DFVF_XYZRHW 和 D3DFVF_DIFFUSE 标志,这将告诉顶点缓冲,自定义顶点类型包含一组转换过的点坐标并紧跟着一个颜色参数。 

现在自定义向量格式和 FVF 已经被指定好了,下一步将使用顶点填充顶点缓冲区,请参看:第二步:设置顶点缓冲 。 

注意:Vertices 例程中的顶点是转换过的。用另一句话说,它们已经在 2D 窗口坐标系下。这意味着座标点 (0,0) 位于左上角,且正的 x 半轴向右,正的 y 半轴向下。这些顶点同样也是光照过的,这说明它们的着色不通过 Direct3D 照明而由它们自己的颜色代替。 

第二步:设置顶点缓冲 

现在自定义顶点格式已经完成,初始化顶点的时候到了。 Vertices 例程创建了必需的 Microsoft Direct3D 对象之后调用本程序内部定义的函数 InitVB() 进行这个工作。以下代码段将初始化三个自定义顶点的值。 

CUSTOMVERTEX g_Vertices[] = 



{ 150.0f, 50.0f, 0.5f, 1.0f, 0xffff0000, }, // x, y, z, rhw, color 

{ 250.0f, 250.0f, 0.5f, 1.0f, 0xff00ff00, }, 

{ 50.0f, 250.0f, 0.5f, 1.0f, 0xff00ffff, }, 

}; 

前述代码片段采用三角形的三个顶点填充三个Vertex并指定了每个顶点的散射光的颜色。第一个顶点位于 (150,50) ,散射红色 (0xffff0000)。第二个顶点位于 (250,250) ,为绿色 (0xff00ff00)。第三点位于 (50,250) 并散射蓝绿色 (0xff00ffff)。每一点都具有相同的 0.5 Z值及 1.0 的 RHW 参数。关于这些矢量格式的其它信息见 SDK: Transformed and Lit Vertices。 

下一步将调用 IDirect3DDevice8::CreateVertexBuffer 创建顶点缓冲区,如以下代码段所示: 

if( FAILED( g_pd3dDevice->CreateVertexBuffer( 3sizeof(CUSTOMVERTEX), 

0 /
Usage /, D3DFVF_CUSTOMVERTEX, 

D3DPOOL_DEFAULT, &g_pVB ) ) ) 

return E_FAIL; 

CreateVertexBuffer 的头两个参数告诉 Direct3D 新顶点缓冲区预计的大小和用法。紧跟的两个参数指定新缓冲区的矢量格式及存储位置。这里的向量格式是 D3DFVF_CUSTOMVERTEX,就是例程先前定义的 FVF 值。D3DPOOL_DEFAULT 标记告诉 Direct3D 在最合适的位置创建此顶点缓冲区。最后一个参数返回创建完成的顶点缓冲区对象地址。 

创建了顶点缓冲区之后,如以下代码段所示,开始采用自定义格式的顶点填充缓冲区中的数据。 

VOID
pVertices; 

if( FAILED( g_pVB->Lock( 0, sizeof(g_Vertices), (BYTE**)&pVertices, 0 ) ) ) 

return E_FAIL; 

memcpy( pVertices, g_Vertices, sizeof(g_Vertices) ); 

g_pVB->Unlock(); 


首先调用 IDirect3DVertexBuffer8::Lock 锁定顶点缓冲区。函数第一个参数是锁定顶点数据的偏移量,按字节计算。第二个参数是需锁定的顶点数据长度,同样按字节计算。第三个参数是一个 BYTE 类型指针的地址,用于返回指向顶点数据的地址。第四个参数告知顶点缓冲区如何锁定数据。 

通过使用 memcpy,顶点被复制到顶点缓冲区里。将顶点放入缓冲区之后,调用一次 IDirect3DVertexBuffer8::Unlock 以解锁顶点缓冲区。这个锁定——解锁机制是必需的,因为正在使用的顶点缓冲区可能位于设备内存中。 

现在顶点缓冲区已经填入顶点,绘制到显示的时候到了,见描述:第三步:绘制至显示屏 。 

第三步:绘制至显示屏 

现在缓冲区已经填入顶点,现在需要把它绘制到显示屏上。在绘制到屏幕之前,先将背景清除为蓝色并调用 BeginScene。 

g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,255), 1.0f, 0L ); 

g_pd3dDevice->BeginScene(); 

从顶点缓冲区绘制顶点数据需要一些步骤。首先,你需要设置流数据源;在当前情况下,使用第 0 个流 。流的数据源是通过调用 IDirect3DDevice8::SetStreamSource 设置的。 

g_pd3dDevice->SetStreamSource( 0, g_pVB, sizeof(CUSTOMVERTEX) ); 

SetStreamSource 的第一个参数告诉 Microsoft Direct3D 设备设置数据流的索引。第二个参数是绑定在该数据流上的顶点缓冲区。第三个参数是数据单元的大小,用字节数表示。在上面的示例代码中,将使用CUSTOMVERTEX 的大小作为数据单元的大小。 

下一步通过调用 IDirect3DDevice8::SetVertexShader 使 Direct3D 了解使用中的顶点处理器(Vertex Shader)。就整体而言,自定义顶点处理器是一种高级的话题,但是在绝大多数情况下顶点处理器仅仅等于 FVF 代码。这能够让 Direct3D 知道处理中的顶点类型。以下代码片段将FVF设置为当前顶点处理器: 

g_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX ); 

SetVertexShader() 唯一的参数是当前设置的顶点处理器的句柄。这个参数的值可以是从IDirect3DDevice8::CreateVertexShader 返回的句柄,或者是 FVF 代码。在这儿,使用的参数是定义为 D3DFVF_CUSTOMVERTEX 的 FVF 代码。 

关于顶点处理器的更多信息,请见 SDK: Vertex Shader 一章。 

下一步使用 IDirect3DDevice8::DrawPrimitive 绘制顶点缓冲区中的顶点,见以下代码片段: 

g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 ); 


DrawPrimitive 接受的第一个参数是一个标记,它通知 Direct3D 绘制哪种类型的物件(Primitive)。本例程使用 D3DPT_TRIANGLELIST 标记指定为三角形序列。第二个参数是第一个顶点的索引。第三个参数通知绘制的物件的数目。本例子只画一个三角形,这个值为 1。 

关于不同种类物件的更多信息,可见 SDK: 3-D Primitive 

最后的一步是结束场景并立即将后背缓冲提交为前景缓冲。这些写在以下代码片段中: 

g_pd3dDevice->EndScene(); 

g_pd3dDevice->Present( NULL, NULL, NULL, NULL ); 

当后背缓冲被提交为前景缓冲后,客户窗口将显示出一个三个点颜色各异的三角形。 

本指南已经指导你如何使用顶点构造几何外形了。指南三:使用矩阵 将介绍矩阵的概念以及如何使用它们。 


指南三:使用矩阵 

本指南介绍矩阵的概念及演示如何使用它们。Vertices 例程通过呈递2D的顶点画出了一个三角形。然而,在这个指南中,你将通过顶点变换在 3-D 环境下工作。矩阵和变换也同样用于设置摄影头与视口(Viewport)。 

在 Matrices 例程呈递几何物体之前,它调用程序自定义函数 SetupMatrices 创建并设置用于演示 3-D 三角形的矩阵变换。作为代表,三种类型的变换同时被设置到一个 3-D 场景。创建这些典型变换的步骤如下表: 

·第一步:定义世界变换矩阵 

·第二步:定义观察变换矩阵 

·第三步:定义映射变换矩阵 

注意:Matrices 示例程序的路径为: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut03_Matrices. 


创建这三种变换的顺序并不影响场景元素的输出。无论如何,Direct3D 都使用以下顺序依次将矩阵作用于场景:(1) 世界,(2) 观察,(3) 映射。 

Matrices 工程的示例代码几乎与 Vertices 工程的代码相同。该“使用矩阵”指南仅仅关注那些有关矩阵的独特代码,而不重复初始化 Direct3D,处理 Microsoft Windows 消息,演示,以及清除。关于这些工作的信息,请见 指南一:创建设备 。 

本指南使用自定义顶点格式和单个顶点缓冲区呈递几何模型,关于更多的有关选择自定义顶点类型以及执行顶点缓冲区的信息,见 指南二:演示顶点 。 

第一步:定义世界变换矩阵(World Transformation Matrix) 

世界变换矩阵定义了怎样转换、缩放、以及旋转 3-D 模拟空间中的几何物体。 

以下代码片段为 Microsoft Direct3D 设备设置当前的世界变换并且使三角形绕 y-轴 旋转。 

D3DXMATRIX matWorld; 

D3DXMatrixRotationY( &matWorld, timeGetTime()/150.0f ); 

g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld ); 

第一步是通过调用 D3DXMatrixRotationY 函数使三角形绕 y-轴 旋转。函数第一个参数是指向 D3DMATRIX 结构的指针用于返回操作结果。第二个参数是以弧度表示的旋转角度。 


下一步是调用 IDirect3DDevice8::SetTransform 给 Direct3D 设备设置世界变换。SetTransform 接受的第一个参数通知 Direct3D 被设置的是哪个转换。这个例子用 D3DTS_WORLD 宏指定被设置的是世界变换。第二个参数是一个指向被设为当前变换之矩阵的指针。 


关于世界变换的更多信息,见:SDK: World Transformation 


定义完场景的世界变换后,你可以准备观察变换矩阵了。再一次请注意:定义任一变换的顺序不是关键。无论如何,Direct3D 采用以下顺序将这些矩阵作用于场景:(1) 世界,(2) 观察,(3) 映射。 


定义观察变换矩阵请参看 第二步:定义观察变换矩阵 


第二步:定义观察变换矩阵(View Transformation Matrix) 


观察变换矩阵定义了观察的位置和旋转角度。此观察矩阵就相当于场景的摄影机。 


以下代码片段创建了一个观察变换矩阵并将其设置为 Microsoft Direct3D 设备的当前观察矩阵。 

D3DXMATRIX matView; 

D3DXMatrixLookAtLH( &matView, &D3DXVECTOR3( 0.0f, 3.0f,-5.0f ), 

&D3DXVECTOR3( 0.0f, 0.0f, 0.0f ), 

&D3DXVECTOR3( 0.0f, 1.0f, 0.0f ) ); 

g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView ); 


第一步是通过调用 D3DXMatrixLookAtLH 定义观察矩阵。第一个参数是一个指向 D3DXMATRIX 结构的指针,用来接受操作结果。第二、三、四个参数定义了观察点、注视点、以及方向“上”。这儿设置观察点为沿 Z-轴 反方向 5 单位再往上 3 单位,注视点为原点,以及作为“上”的方向为 Y-轴。 

下一步是调用 IDirect3DDevice8::SetTransform 给 Direct3D 设备设置观察矩阵。SetTransform 接受的第一个参数通知 Direct3D 哪一个变换将要被设置。该例程使用 D3DTS_VIEW 标记指定为观察矩阵。第二个参数是一个指向矩阵的指针,它被设为当前的变换。 


关于观察矩阵的更多信息,见:SDK: View Transformation 


定义了场景的世界变换后,你可以开始准备映射变换矩阵了。再一次提醒,定义每一变换的顺序不是关键性的。无论如何,Direct3D 总是采用以下顺序将矩阵应用于场景:(1) 世界,(2) 观察,(3) 映射。 

定义映射变换矩阵的工作被描述在 第三步:定义映射变换矩阵 


第三步:定义映射变换矩阵(Projection Transformation Matrix) 

映射变换矩阵定义了将 3-D 观察空间转换为 2-D 视口空间的几何学方法。 

以下代码片段创建映射变换矩阵并将其设为 Microsoft Direct3D 设备的当前映射变换。 

D3DXMATRIX matProj; 

D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f ); 

g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj ); 


第一步是调用 D3DXMatrixPerspectiveFovLH 设置映射矩阵。函数第一个参数是一个指向 D3DXMATRIX 的结构,用于接受操作的结果。第二个参数定义视场,它说明物体如何随着距离而缩小。一个典型的视场是 1/4 π,就像这个例子使用的一样。第三个参数定义了屏幕纵横比。本示例采用典型的纵横比 1。第四和第五个参数定义最近和最远剪切平面。这是用于确定位于何种距离之外的几何物体无需再绘制。本 Matrices 示例设置它的最近剪切平面为 1,最远剪切平面为 100。 


下一步是调用 IDirect3DDevice8::SetTransfrom 对 Direct3D 应用变换。SetTransfrom 接受的第一个参数通知 Direct3D 何种变换被设置。本例程使用 D3DTS_PROJECTION 标志指定映射变换将被设置。第二个参数是一个指向矩阵的指针,它将被设置为当前的变换。 

关于映射变换的更多信息,参见:“映射变换” 

本指南已经提示你如何使用矩阵。指南四:创建和使用光源 将揭示如何在你的场景中添加光源以增加真实性。 

指南四:创建和使用光源 

Microsoft Direc3D 光照系统给 3-D 物体提供更多的真实性。当使用它时,每个场景中的几何对象将被照亮,基于它们的位置和使用的光源类型。这个指南的例程将介绍关于光照和材质的主题。 

本指南包含以下步骤用于创建材质与光照: 


·第一步:创始化场景几何 

·第二步:设置材置与光照 

注意:Lights 示例程序的路径为: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut04_Lights. 

注意:Lights 例程中的代码和 Matrices 例程的代码几乎完全一样。“创建和使用光源”指南仅仅关注于有关创建和使用光照的独特代码,而并不重复有关设置 Direct3D,处理 Microsoft Windows 消息,绘制,或者清理的内容。关于这些任务的其他信息,见:指南一:创建设备。 

本指南使用自定义顶点和顶点缓冲区呈递几何形体。关于选择一个自定义顶点格式并执行顶点缓冲的更多信息,见:指南二:演示顶点。 

本指南采用矩阵变换几何对象。关于矩阵和变换的更多信息,参见:指南三:使用矩阵。 

第一步:创始化场景几何 


使用光照的一个前提是每个表面都应该有法向量。为此,Lights 例程使用一个稍微不同的自定义顶点格式,新的自定义顶点格式具有一个 3-D 位置坐标和一个表面法向量。这个表面法向量被用于 Microsoft Direct3D 光照计算的核心。 

struct CUSTOMVERTEX 



D3DXVECTOR3 position; // The 3-D position for the vertex. 

D3DXVECTOR3 normal; // The surface normal for the vertex. 

}; 


// Custom FVF. 

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL) 

现在适当的矢量格式定义好了,Lights 例程调用 InitGeometry(),一个程序自定义的函数以创建一个圆柱体。最初的步骤是创建一个顶点缓冲区并用它保存这个圆柱体的各点,如以下例代码所示: 

// Create the vertex buffer. 

if( FAILED( g_pd3dDevice->CreateVertexBuffer( 502sizeof(CUSTOMVERTEX), 

0 /* Usage /, D3DFVF_CUSTOMVERTEX, 

D3DPOOL_DEFAULT, &g_pVB ) ) ) 

return E_FAIL; 


下一步是使用圆柱体的顶点填充顶点缓冲区。注意下面的示例代码,每个点都被定义了一个位置和一个法向量。 


for( DWORD i=0; i<50; i++ ) 



FLOAT theta = (2
D3DX_PIi)/(50-1); 

pVertices[2
i+0].position = D3DXVECTOR3( sinf(theta),-1.0f, cosf(theta) ); 

pVertices[2i+0].normal = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) ); 

pVertices[2
i+1].position = D3DXVECTOR3( sinf(theta), 1.0f, cosf(theta) ); 

pVertices[2i+1].normal = D3DXVECTOR3( sinf(theta), 0.0f, cosf(theta) ); 




在前述例程使用圆柱体顶点填充了顶点缓冲区之后,这个顶点缓冲区已经准备好用于呈递了。但是首先,这个场景的材质与光照必须在绘制圆柱体之前被设置。这些描述在 第二步:设置材质与光照。 


第二步:设置材质与光照 


为了在 Microsoft Direct3D 中使用光照,你必须创建一个或多个光源。为了确定一个几何物体放射何种颜色的光线,材质必须被创建于绘制几何对象。在绘制这个场景之前,Lights 例程调用 SetupLights,一个程序自定义函数来设置材质和一个方向性光源。 


创建一种材质 


材质被定义为当一束光照到几何物体表面后,反射出的颜色。以下代码片段使用 D3DMATERIAL8 结构来创建一个黄色的材质。 


D3DMATERIAL8 mtrl; 

ZeroMemory( &mtrl, sizeof(D3DMATERIAL8) ); 

mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f; 

mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f; 

mtrl.Diffuse.b = mtrl.Ambient.b = 0.0f; 

mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f; 

g_pd3dDevice->SetMaterial( &mtrl ); 


这个材质的漫射光颜色与环境光颜色都被设为黄色。对 IDirect3DDevice8::SetMaterial 函数的调用将应用此材质到用于绘制场景的 Microsoft Direct3D 设备。SetMaterial() 接受的唯一参数是设置材质的指针。在这个调用完成以后,每个物件都将使用这个材质绘制直到另一次对 SetMaterial 的调用指定了一个不同的材质为止。 


现在材质已经被应用到场景,下一个步骤是创建光源。 


创建一个光源 

Microsoft Direct3D 里有三种可用的光源:点光源,方向形光源,与聚光灯光源。本示例代码创建一个方向形光源,它向一个方向发光,并且不停的变换发光的方向。 

下列代码片段使用 D3DLIGHT8 结构创建一个方向性光源。 


D3DXVECTOR3 vecDir; 

D3DLIGHT8 light; 

ZeroMemory( &light, sizeof(D3DLIGHT8) ); 

light.Type = D3DLIGHT_DIRECTIONAL; 


下列代码片设置光源的漫射光为白色。 

light.Diffuse.r = 1.0f; 

light.Diffuse.g = 1.0f; 

light.Diffuse.b = 1.0f; 


以下代码片在一个环内旋转光源的方向。 

vecDir = D3DXVECTOR3(cosf(timeGetTime()/360.0f), 

0.0f, 

sinf(timeGetTime()/360.0f) ); 

D3DXVec3Normalize( (D3DXVECTOR3
)&light.Direction, &vecDir ); 

对 D3DXVec3Normalize 函数的调用将归一化方向矢量并初始化光源的方向。 

可以设置一个范围告诉 Direct3D 此光源能影响多远的距离。这个成员参数对方向性光源无效。以下代码片指定此光源的范围为 1000 单位。 


light.Range = 1000.0f; 


下面的代码片将这个光源分配到当前的 Direct3D 设备,通过调用 IDirect3DDevice8::SetLight。 


g_pd3dDevice->SetLight( 0, &light ); 


SetLight 接受的第一个参数是此光源被分配的索引号。注意如果在此索引已存在一个光源,它将被新光源覆盖。第二个参数是一个指向新定义光源数据结构的指针。本 Lights 例程设置这个光源位于 0 号索引。 


下列代码片激活这个光源,通过调用 IDirect3DDevice8::LightEnable。 


g_pd3dDevice->LightEnable( 0, TRUE); 


LightEnable 接受的第一个参数是激活光源的索引。第二个参数是一个布尔量通知此光源是开 (TRUE) 还是闭 (FALSE)。在上面的例程中,索引 0 上的光源被打开。 


以下代码片通知 Direct3D 呈递此光源,通过调用 IDirect3DDevice8::SetRenderState。 


g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE ); 


SetRenderState 接受的头两个参数是哪一个设备状态变量被改写以及写入何种值。本例程设置 D3DRS_LIGHTING 设备变量为 TRUE,这将使设备能够演示光照效果。 


本例程的最后一步是通过再一次调用 SetRenderState 打开环境照明光。 


g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00202020 ); 


当前代码段设置 D3DRS_AMBIENT 设备变量为一种浅灰色 (0x00202020)。环境照明将使用所给的颜色照亮所有的物体。 


关于照明及材质的更多信息,参见 SDK: Lights and Materials。 


本例程向你说明了如何使用照明与材质。指南五:使用纹理映射 将向你说明如何将纹理添加到物体表面上。 



指南五:使用纹理映射 


尽管光照和材质大大增加了场景的真实感,但没有比在表面上添加纹理更能增加真实感的了。纹理能够被想象为一层紧紧包装在表面的贴纸。你能在一个立方体上放置一层木质纹理使它看起来就象用木头制成的一样。本 Texture 例程将在 指南四:创建和使用光照 中构造的圆柱上添加一幅类似香蕉的纹理。此指南介绍的内容包括如何载入纹理,设置纹理,与呈递带有纹理的物体。 


本指南采用以下步骤实现纹理: 


·第一步:定义一个定制顶点格式 

·第二步:初始化屏幕几何 

·第三步:演示场景 

注意:Texture 示例程序的路径为: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut05_Textures. 


注意:除了 Texture 示例不创建材质和光照以外,Texture 工程中的示例代码与 Lights 工程的几乎完全一样。本“使用纹理映射”指南仅仅关注于有关于纹理的独特代码,而并不重复有关初始化 Microsoft Direct3D,处理 Microsoft Windows 消息,演示,或清理的内容。关于这些工作的信息,见:指南一:创建设备。 

本指南使用自定义顶点和顶点缓冲区显示几何物体。关于选择一个自定义顶点格式并执行顶点缓冲的更多信息,见:指南二:演示顶点。 


本指南采用矩阵进行几何变换。关于矩阵和变换的更多信息,参见:指南三:使用矩阵。 


第一步:定义一个定制顶点格式 


在使用纹理以前,必须使用包含纹理坐标的自定义顶点格式。纹理坐标告诉 Microsoft Direct3D 在物件上如何将纹理定位于每个顶点上。纹理坐标范围从 0.0 到 1.0,(0.0, 0.0) 的位置代表纹理贴图的左上角而 (1.0, 1.0) 代表纹理贴图的右下角。 


以下示例代码说明了 Texture 例程是如何通过设置它的自定义顶点格式来包含纹理坐标的。 

struct CUSTOMVERTEX 



D3DXVECTOR3 position; // The position. 

D3DCOLOR color; // The color. 

FLOAT tu, tv; // The texture coordinates. 

}; 


// The custom FVF, which describes the custom vertex structure. 

#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE|D3DFVF_TEX1) 


关于纹理坐标的进一步信息,参见 SDK: Texture Coordinates 一章。 


现在自定义顶点格式已经准备好了,下一步将是载入一幅纹理并创建一个圆柱体,见 第二步:初始化屏幕几何。 


第二步:初始化屏幕几何 


在绘制之前,Texture 例程调用 InitGeometry,一个程序自定义的函数用于创建一幅纹理并初始化圆柱体的几何参数。 


纹理是由基于文件的图像构造的。以下示例代码使用 D3DXCreateTextureFromFile 从 Banana.bmp 文件创建一幅纹理并用它覆盖圆柱的表面。 


if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, “Banana.bmp”, 

&g_pTexture ) ) ) 

return E_FAIL; 


D3DXCreateTextureFromFile 接受的第一个参数是一个指向 Microsoft Direct3D 设备的指针,这个设备将用于绘制纹理。第二个参数是一个指向 ANSI 字符串的指针,它指定用于创建纹理的文件名。本例程指定从此文件:“Banana.bmp” 来装载图像。第三个参数是一个指向纹理对象指针的地址。 

当这个类似香蕉的纹理被装载并准备好之后,下一个步骤是创建圆柱体。以下示例代码用一个圆柱体填充顶点缓冲区。注意每一点都具备了纹理坐标 (tu, tv)。 


for( DWORD i=0; i<50; i++ ) 



FLOAT theta = (2D3DX_PIi)/(50-1); 


pVertices[2i+0].position = D3DXVECTOR3( sinf(theta),-1.0, cosf(theta) ); 

pVertices[2
i+0].color = 0xffffffff; 

pVertices[2i+0].tu = ((FLOAT)i)/(50-1); 

pVertices[2
i+0].tv = 1.0f; 


pVertices[2i+1].position = D3DXVECTOR3( sinf(theta), 1.0, cosf(theta) ); 

pVertices[2
i+1].color = 0xff808080; 

pVertices[2i+1].tu = ((FLOAT)i)/(50-1); 

pVertices[2
i+1].tv = 0.0f; 





每一个顶点包括位置,颜色,以及纹理坐标。上面的例程给每一点设置了纹理坐标并使此纹理能够平滑的包裹在圆柱体周围。 


现在纹理和顶点缓冲区已经准备好用于演示了,现在能够呈递和着色图形了,参见 第三步:演示场景。 


第三步:演示场景 


在场景几何被初始化之后,应该是绘制场景的时候了。为了绘制一个带有纹理的物体,使用的纹理必须要设置成当前纹理中的一个。下一步将是设置纹存储器的状态。纹理存储器状态使你能够定义一个或者多个纹理被呈递的方式。比如说,你能将多个纹理混合在一起。 


现在 Texture 示例开始设置需要使用的纹理。以下代码段使用 IDirect3DDevice8::SetTexture 设置 Microsoft Direct3D 设备用于绘制的纹理。 


g_pd3dDevice->SetTexture( 0, g_pTexture ); 


SetTexture 接受的第一个参数是设置纹理存储器的标示符。一个设备能够支持八个已初始化的纹理,所以这儿的最大值是 7。本 Texture 示例仅仅使用一个纹理并且把它设置在存储器 0。第二个参数是一个指向纹理对象的指针。在这儿,Texture 示例使用由它的程序自定义函数 InitGeometry 创建的纹理。 



以下代码片设置纹理存储器状态的值,通过调用 IDirect3DDevice8::SetTextureStageState 方法。 


g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_MODULATE ); 

g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE ); 

g_pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_DIFFUSE ); 

g_pd3dDevice->SetTextureStageState( 0, D3DTSS_ALPHAOP, D3DTOP_DISABLE ); 



SetTextureState 的第一个参数是需要改变状态的存储器的索引。本示例代码改变位于存储器 0 的纹理,所以这儿置为 0。下一个参数是要设置的纹理状态。关于所有有效的纹理状态以及它们的意义,见 “SDK: D3DTEXTURESTAGESTATETYPE”。再下一个参数是设置为此纹理状态的参数。你放置这儿的值应取决于你要改变的纹理存储器状态。 


在设置完每个纹理存储器状态的合适值之后,这个圆柱体可以被呈递了,现在纹理将被添加在它的表面上。 


使用纹理坐标的其他方法是使它们自动的生成。这是用一种纹理坐标索引 (TCI) 实现的。TCI 使用一个纹理矩阵来变换 (x,y,z) TCI 坐标为 (tu, tv) 纹理坐标。在 Texture 例程中,位于摄像机空间中的顶点位置被用来产生纹理坐标。 


第一步是创建用于转换的矩阵,示范在以下代码片段中: 


D3DXMATRIX mat; 

mat._11 = 0.25f; mat._12 = 0.00f; mat._13 = 0.00f; mat._14 = 0.00f; 

mat._21 = 0.00f; mat._22 =-0.25f; mat._23 = 0.00f; mat._24 = 0.00f; 

mat._31 = 0.00f; mat._32 = 0.00f; mat._33 = 1.00f; mat._34 = 0.00f; 

mat._41 = 0.50f; mat._42 = 0.50f; mat._43 = 0.00f; mat._44 = 1.00f; 



在矩阵创建好之后,它必须通过调用 IDirect3DDevice8::SetTransform 来设置它,如以下代码段所示: 


g_pd3dDevice->SetTransform( D3DTS_TEXTURE0, &mat ); 


D3DTS_TEXTURE0 标志告诉 Direct3D 应用此变换到位于纹理存储器 0 的纹理。本示例的下一步是设置其他的存储器状态值,以得到所需的效果。这些处理在以下代码段中。 


g_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 ); 

g_pd3dDevice->SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEPOSITION ); 


纹理坐标被设置后,现在此场景已准备好被呈递了。注意到现在的坐标是自动设置到圆柱上的。这样精确的设置使几何物体被演示时纹理好象是覆盖在绘制的屏幕上。 


关于纹理的更多信息,见 SDK: Texture 一章。 


本指南已经向你说明了如何给表面添加纹理。指南六:使用Mesh模型 将告诉你如何应用Mesh模型呈递复杂的几何形体。 



指南六:使用Mesh模型 


复杂的几何形状常常使用 3-D 建模软件构造模型并保存为文件。一个例子就是 .x 文件格式。Microsoft Direct3D 使用Mesh对象从文件装载这些物体。Mesh对象稍微有点复杂,但是 Microsoft Direct3DX 包含的函数使应用Mesh对象变的简单。Meshed 例程介绍关于Mesh的话题并展示如何装载,演示,以及卸载一个Mesh对象。 


本指南使用以下步骤说明如何装载,演示,以及卸载一个Mesh对象: 


·第一步:装载一个Mesh对象 

·第二步:演示一个Mesh对象 

·第三步:卸载一个Mesh对象 

注意:Methes 示例程序的路径为: 

(SDK root)\Samples\Multimedia\Direct3D\Tutorials\Tut06_Meshes. 


注意:除了 Meshes 工程中的示例代码不创建材值与光照以外,此工程的示例代码与 Lights 工程几乎完全一样。“使用Mesh模型”指南仅仅关注于有关于Mesh对象的独特代码,而并不重复有关设置 Microsoft Direct3D,处理 Microsoft Windows 消息,演示,或清理的工作。关于这些工作的信息,见:指南一:创建设备。 


本指南使用自定义顶点和顶点缓冲区显示几何物体。关于选择一个自定义顶点格式并执行顶点缓冲的更多信息,见:指南二:演示顶点。 


本指南采用矩阵进行几何变换。关于矩阵和变换的更多信息,参见:指南三:使用矩阵。 


本指南使用纹理覆盖Mesh模型的表面。关于装载和使用纹理的更多信息,参见:指南五:使用纹理映射。 


第一步:装载一个Mesh对象 


在使用之前,Microsoft Direct3D 应用程序必须先装载一个Mesh对象。Meshes 例程通过调用 InitGeometry,一个该程序自定义的函数,装载一只虎的Mesh模型,当然这是在已经装载了必需的 Direct3D 对象以后。 


一个Mesh对象需要用一个材质缓冲保存所有将要用到的材质与纹理。所以该函数最初定义了一个材质缓冲,如以下代码段所示: 


LPD3DXBUFFER pD3DXMtrlBuffer; 


以下代码段使用 D3DXLoadMethFromX 函数装载Mesh对象。 


// Load the mesh from the specified file. 

if( FAILED( D3DXLoadMeshFromX( “tiger.x”, D3DXMESH_SYSTEMMEM, 

g_pd3dDevice, NULL, 

&pD3DXMtrlBuffer, &g_dwNumMaterials, 

&g_pMesh ) ) ) 

return E_FAIL; 


D3DXLoadMeshFromX 接受的第一个参数是一个指向字符串的指针告诉 Microsoft Direct3D 要装载的文件。本例程从 Tiger.x 读取一只虎的Mesh模型。 


第二个参数通知 Direct3D 如何创建Mesh对象。本示例采用 D3DXMESH_SYSTEMMEM 标记,它等于同时指定 D3DXMESH_VB_SYSTEMMEM 与 D3DXMESH_IB_SYSTEMMEM,这两个参数告诉 Direct3D 把Mesh对象的索引缓冲区和顶点缓冲区都放到系统内存中。 


第三个参数是指向将被用于绘制Mesh对象的 Direct3D 设备的指针。 


第四个参数是一个指向 ID3DXBuffer 对象的指针。这个对象装入关于各个面相邻与否的信息。在本例程中此信息是不需要的,所以这个参数被设为 NULL。 


第五个参数同样取得一个指向 ID3DXBuffer 的指针。在函数执行完以后,此对象将被填入该Mesh对象使用的 D3DXMATERIAL 结构。 


第六个参数是一个指针,指向函数执行结束后,返回的置入 ppMaterials 队列中的 D3DXMATERIAL 结构数目。 


第七个参数是一个Mesh对象指针的地址,返回装载的Mesh对象。 


在装载了这个Mesh对象和相关材质信息之后,你需要从材质缓冲区中分解出材质属性及纹理名称。 


本 Mesh 例程先需要得到材质缓冲区指针才能处理这些事情。以下代码段使用 ID3DXBuffer::GetBufferPointer 函数得到这个指针。 


D3DXMATERIAL* d3dxMaterials = 

(D3DXMATERIAL*)pD3DXMtrlBuffer->GetBufferPointer(); 


以下代码段创建了一个新的Mesh和纹理对象基于Mesh对象中材质的最大数目。 


g_pMeshMaterials = new D3DMATERIAL8[g_dwNumMaterials]; 

g_pMeshTextures = new LPDIRECT3DTEXTURE8[g_dwNumMaterials]; 


对于每个Mesh对象里的材质都必须进行以下步骤。 


第一步是拷贝材质,如以下代码段所示. 

g_pMeshMaterials[i] = d3dxMaterials[i].MatD3D; 


第二步是设置材值的环境色,见以下代码段。 


g_pMeshMaterials[i].Ambient = g_pMeshMaterials[i].Diffuse; 


最后一步是为该材质创建纹理,如以下代码段。 


// Create the texture. 

if( FAILED( D3DXCreateTextureFromFile( g_pd3dDevice, 

d3dxMaterials[i].pTextureFilename, 

&g_pMeshTextures[i] ) ) ) 

g_pMeshTextures[i] = NULL; 




装载了每个材质以后,你使用完毕了这个材质缓冲区,必须调用 IUnknown::Release 来释放它。 


pD3DXMtrlBuffer->Release(); 


现在,Mesh对象,连同其相应的材质与纹理都已经装载好了。这个Mesh物体已准备好呈递到屏幕上,参看 第二步:演示一个Mesh对象。 


第二步:演示一个Mesh对象 


在第一步中,Mesh对象已经准备号被呈递了。该对象被Mesh对象装载的每个材质分成若干个子集。为了绘制每个子集,应该在一个循环中绘制此Mesh对象。循环的第一步是为每个子集设置材质,如以下代码段所示: 


g_pd3dDevice->SetMaterial( &g_pMeshMaterials[i] ); 


循环的第二步是给每个子集设置纹理,如以下代码段所示。 


g_pd3dDevice->SetTexture( 0, g_pMeshTextures[i] ); 


在给每个子集设置完材质与纹理之后,子集被 ID3DXBaseMesh::DrawSubset 函数所绘制,如以下代码段所示。 


g_pMesh->DrawSubset( i ); 


DrawSubset 函数带有一个 DWORD 参数用于指定Mesh对象的哪个子集被绘制。本例程使这个参数的值每循环一次就被加一。 


在使用完Mesh对象之后,重要的事是要将此Mesh对象完全移出内存,参见 第三步:卸载一个Mesh对象。 


第三步:卸载一个Mesh对象 


在任何 Microsoft Direct3D 程序结束前,它有必要解构它使用的所有 DirectX 对象并且使指向它们的指针无效。本例程使用的Mesh对象同样需要被解构。当它接收到一个 WM_DESTROY 消息时,Meshes 例程调用 Cleanup,一个该程序自定义的函数,来处理此事。 


以下代码段删除材质队列。 


if( g_pMeshMaterials ) 

delete[] g_pMeshMaterials; 


以下代码解构每个装载过的单独纹理并删除纹理队列。 


if( g_pMeshTextures ) 



for( DWORD i = 0; i < g_dwNumMaterials; i++ ) 



if( g_pMeshTextures[i] ) 

g_pMeshTextures[i]->Release(); 



delete[] g_pMeshTextures; 


以下代码段解构Mesh对象。 


Delete the mesh object 

if( g_pMesh ) 

g_pMesh->Release(); 


本指南已经向你说明了如何装载和绘制Mesh对象。这是此区域最后一个指南。如果需要了解一个典型的 Direc3D 应用程序是如何写成的,见:“SDK: DirectX Graphics C/C++ Samples” 。

【ZT】再论所谓“国医”      摘要: 8月5日我在《大公报·星期论文》一栏中发表了一篇文章,叫做“所谓国医”,引起了一群“所谓国医”的攻击,并有几个南京的记者,在那里胡言乱道一阵,肆 力作个人攻击。和国医谈科学,和如此一流的记者谈伦理,皆所谓对驴弹琴,白费精神,我所不取。然《大公报》上的两篇宣扬国医的文字由我引起,理宜再申说我 的意思一下。且前一文中,我犹未尽之意,亦应再补充说几句。

有一位程序员,喜欢新东西,经常引入新方法新思路试图改变现状。
有一次,他觉得部门在测试手段太依赖手工测试了,于是就想引入自动测试。他调研试用了几种工具以后,选择了某大公司的产品作为主要工具。
他计算了一下,一共有1000条测试用例需要自动化,于是他定下计划:一个人一天可以写5个测试用例,所以需要200个人天;计划要在一个季度完成,按一个人一季度共60个人天的话,需要4个人还有余。这样,全部用例做好以后,他估计,至少减少一半测试工作量。
于是,这老兄报告给总经理,总经理同意了。于是就给他招了4个人,成立了自动测试组,风风火火就干起来了。
他的做法是,手工测试有多少条用例,他就用自动测试工具实现多少条。
一个季度下来了,全部测试用例完成了。当然大家敲锣打鼓的庆祝。但接下来,却遇到了意想不到的问题。
第一、所有的测试人员都认为自动测试组写出来的脚本没有用。为什么呢?因为自动测试组的员工都是新招的,对产品不熟;因此他们只好对照着手工测试用例一条条的做,就像做翻译一样把手工测试用例变成自动脚本。但测试人员说,手工测试用例本身就不够完善,很多测试的验证点是凭经验的,这样翻译出来的用例当然不过关。
第二、新产品特性已经改变了,写出得脚本过期了。因为是比照着手工用例,自动测试组使用的用例是产品的上一个版本的,这样写出来的用例当然不适合现状。
第三、短期内投入产出比很低。手工测试一天能走100个测试用例,1000个用例10天走完。但4个自动测试工程师3个月才完成1000个用例的开发,也就是花了4×3×20=240个人天,就算测试用例100%可用,也需要240÷10=24轮才能在成本上持平,如果每个版本测3轮的话,相当于8个版本。而8个版本,产品还在不在都难说了。
 
程序员做了反省,发现自己犯的最大错误是:自己提出问题的初衷是减少测试工作量,但执行的时候却把“翻译”完所有的测试作为了目的,而忘记了最初的目标。因为只顾着往前赶数量,从来没有请手工测试的工程师来看看,是不是可以100%替代手工测试;也没有在小模块上试试,看看开发人员有什么意见。
 
这样,程序员改变了做法。和开发、手工测试和手下沟通后,他决定把自动测试工程师分散到模块去,和相应模块的开发,测试成为一个工作小组。开发人员设计编码的时候,他们就设计自动测试用例,充分听取手工测试工程师的经验,并且每天都运行一遍。这样,自动测试的脚本就完全和产品同步。他们把自动测试用例和产品代码签入到同一个代码库,同样的版本具有同样的标签,这样,每个版本的产品都有了自动测试的脚本。
 
这样,又过了一个季度,团队开始接受自动测试了。而且有了一个意外的收获,开发人员现在乐于用自动脚本做单元测试,居然开发效率和质量都提高了。
 
程序员得出一个经验,目标在最初的设定,一般都会比较清楚的,但在漫长的实现过程中很容易忘了原来的目标是什么,而把一些表面的指标当成重要的东西。因此,经常看看今天所做的努力和原先的目标是否一致,和能否一致,是很重要的。

某公司启动了一个大项目。
这个项目对公司很重要,于是,研发部门开动马力,辛辛苦苦干了3个月,顺利结项了。
项目经理很高兴,就向老总申请庆祝会。老总说:“项目虽然结了,不过客户还没给钱。大的庆祝会安排在客户给钱以后;现在我先给你1000块钱,你可以安排一个小的庆祝会,给大家开开香槟。”
项目经理同意了,找了几个活跃分子,买香槟,水果,点心,彩球,统计报告,还安排了谜语小游戏……一个小会场搞得生气盎然。到了时间,公司放起来轻音乐,程序员们款款步入会场……一切似乎都那么完美。项目经理高兴极了。
不料,接下去,项目经理傻眼了。
象这样的开场,当然先领导讲话。老总讲完话后,走了。
于是,项目经理说:“我们给大家安排了香槟水果点心和游戏,还有一些项目中的资料,大家可以边吃边看边玩……”,谁料到,随着项目经理话声落下,几十个工程师一拥而上,抢水果,拿点心,在项目经理:“唉!唉!慢点”的声音中不一会就盘空食尽。通通把吃的拿回自己的座位上享受去了。
香槟,没来得及开。
字谜,没人猜。
资料,当然,更没人看了。
只剩下几个小组组长和活跃分子陪着项目经理,站在空荡荡的会场上。
回到家,项目经理打开香槟,喝醉了。
 
不知道中国的程序员群体,是不是中国最不快乐的群体。但是,很多程序员似乎都找不到人生乐趣。特别是被互联网泡沫熏过鼻子的一代。
他们对于团队活动的,通常是:吃饭?没意思,发钱给我吧!如果不是技术培训,通常都不参加;如果是技术培训,不管有用没用都参加。永远在诉苦。你去看招聘网站的IT企业评论版,你会发现,如果是个烂公司,臭鸡蛋臭番茄一起上;如果是个好公司,就说:很累的,性价比不高!
程序员啊,钱,比一般人多点;快乐,比常人少很多。
有人大概会拿IT企业多苦多累说事。我不相信这个茬。任何行业,想干好,不脱层皮不可能干好!去开饭店,你得5点钟出去买菜;做建筑师,半夜上工地的事有的是;什么机械化工,各有各的苦和难。有一天我加班到十一点打的回家,的哥对我羡慕得不得了:“白领啊,赚钱多;我每天开12个小时车,还没赚你一半”。
程序员不快乐还表现在喜欢发牢骚上。有时我在想这么一个有趣的事情。通常谁最占据媒体的发布权,谁就是最会发牢骚的人,通常也是最不快活的人。试看中国古代,主要的信息传播方式是著书立传,所以文人墨客是最会发牢骚的人;近代社会,报纸杂志是主流媒体,所以各种知识分子成了最会发牢骚上;当今世界,互联网为王,程序员挟近水楼台之便利,成为最会发牢骚的群体。可是,古代文人发牢骚,通常是悲叹个人命运之不济;知识分子是追求社会进步,思想自由;程序员呢?通常是怪老板给钱太少。大概是这三种人中铜臭味最足的了。
实际上,人生最重要的事情是要快乐。这大概是软件这行还太年轻的缘故吧,过高得期望值和相对较多的成功案例,刺激得人们心态失衡。希望随着时间的推移,30以上的程序员成为普遍现象的时候,人们会拿起香槟酒,在这种小的庆祝会上互相碰一下杯,很轻松的聊聊经验感受,猜猜字谜,Enjoy the life。

有一阵子,公司疯传一封电邮,就是Google公司的办公室靓照。红红绿绿的熔岩灯,独具匠心的小装饰,特立独行的张帖画……一时大家艳慕不已。于是有一个逻辑流传开来:Google之所以这么牛,是他的办公室牛。如果所有的公司都和Google一样打扮办公室,创造性环境自然来了……你看Google