topameng's profileQuake3 启示录PhotosBlogListsMore Tools Help

Blog


    July 19

    Diffuse Lighting (Direct3D 9)

    After adjusting the light intensity for any attenuation effects, the lighting engine computes how much of the remaining light reflects from a vertex, given the angle of the vertex normal and the direction of the incident light. The lighting engine skips to this step for directional lights because they do not attenuate over distance. The system considers two reflection types, diffuse and specular, and uses a different formula to determine how much light is reflected for each. After calculating the amounts of light reflected, Direct3D applies these new values to the diffuse and specular reflectance properties of the current material. The resulting color values are the diffuse and specular components that the rasterizer uses to produce Gouraud shading and specular highlighting.

    Diffuse lighting is described by the following equation.

    Diffuse Lighting = sum[Cd*Ld*(N.Ldir)*Atten*Spot]
    
    Parameter Default value Type Description
    sum N/A N/A Summation of each light's diffuse component.
    Cd (0,0,0,0) D3DCOLORVALUE Diffuse color.
    Ld (0,0,0,0) D3DCOLORVALUE Light diffuse color.
    N N/A D3DVECTOR Vertex normal
    Ldir N/A D3DVECTOR Direction vector from object vertex to the light.
    Atten N/A FLOAT Light attenuation. See Attenuation and Spotlight Factor (Direct3D 9).
    Spot N/A FLOAT Spotlight factor. See Attenuation and Spotlight Factor (Direct3D 9).

    The value for Cd is either:

    • vertex color1, if DIFFUSEMATERIALSOURCE = D3DMCS_COLOR1, and the first vertex color is supplied in the vertex declaration.
    • vertex color2, if DIFFUSEMATERIALSOURCE = D3DMCS_COLOR2, and the second vertex color is supplied in the vertex declaration.
    • material diffuse color

    Note     If either DIFFUSEMATERIALSOURCE option is used, and the vertex color is not provided, the material diffuse color is used.

    To calculate the attenuation (Atten) or the spotlight characteristics (Spot), see Attenuation and Spotlight Factor (Direct3D 9).

    Diffuse components are clamped to be from 0 to 255, after all lights are processed and interpolated separately. The resulting diffuse lighting value is a combination of the ambient, diffuse and emissive light values.

    Example

    In this example, the object is colored using the light diffuse color and a material diffuse color. The code is shown below.

    D3DMATERIAL9 mtrl;
    ZeroMemory( &mtrl, sizeof(mtrl) );
    
    D3DLIGHT9 light;
    ZeroMemory( &light, sizeof(light) );
    light.Type = D3DLIGHT_DIRECTIONAL;
    
    D3DXVECTOR3 vecDir;
    vecDir = D3DXVECTOR3(0.5f, 0.0f, -0.5f);
    D3DXVec3Normalize( (D3DXVECTOR3*)&light.Direction, &vecDir );
    
    // set directional light diffuse color
    light.Diffuse.r = 1.0f;
    light.Diffuse.g = 1.0f;
    light.Diffuse.b = 1.0f;
    light.Diffuse.a = 1.0f;
    m_pd3dDevice->SetLight( 0, &light );
    m_pd3dDevice->LightEnable( 0, TRUE );
    
    // if a material is used, SetRenderState must be used
    // vertex color = light diffuse color * material diffuse color
    mtrl.Diffuse.r = 0.75f;
    mtrl.Diffuse.g = 0.0f;
    mtrl.Diffuse.b = 0.0f;
    mtrl.Diffuse.a = 0.0f;
    m_pd3dDevice->SetMaterial( &mtrl );
    m_pd3dDevice->SetRenderState(D3DRS_DIFFUSEMATERIALSOURCE, D3DMCS_MATERIAL);
    

    According to the equation, the resulting color for the object vertices is a combination of the material color and the light color.

    These two images show the material color, which is gray, and the light color, which is bright red.

    untitled red

    The resulting scene is shown below. The only object in the scene is a sphere. The diffuse lighting calculation takes the material and light diffuse color and modifies it by the angle between the light direction and the vertex normal using the dot product. As a result, the backside of the sphere gets darker as the surface of the sphere curves away from the light.

    lightd

    Combining the diffuse lighting with the ambient lighting from the previous example shades the entire surface of the object. The ambient light shades the entire surface and the diffuse light helps reveal the object's 3D shape.

    lightad

    Diffuse lighting is more intensive to calculate than ambient lighting. Because it depends on the vertex normals and light direction, you can see the objects geometry in 3D space, which produces a more realistic lighting than ambient lighting. You can use specular highlights to achieve a more realistic look.

    Ambient Lighting (Direct3D 9) 环境光

    Ambient lighting provides constant lighting for a scene. It lights all object vertices the same because it is not dependent on any other lighting factors such as vertex normals, light direction, light position, range, or attenuation. It is the fastest type of lighting but it produces the least realistic results. Direct3D contains a single global ambient light property that you can use without creating any light. Alternatively, you can set any light object to provide ambient lighting. The ambient lighting for a scene is described by the following equation.

    环境光为场景提供了一种恒定不变的光照。环境光对所有物体的顶点的照明效果相同,因为它与其余光照因子,如顶点法向、光的方向、光的位置、范围或衰减等无关。环境光是最快的一种类型,但它提供的真实感最少。Direct3D 包含了一个全局的环境光属性,应用程序可以直接使用而无需创建任何光源。另外,应用程序也可以设定光源提供环境光照。场景中环境光的计算由以下公式描述。

    Ambient Lighting = Ca*[Ga + sum(Atti*Spoti*Lai)] 
    

    Where:

    参数 默认值 类型 描述
    Ca (0,0,0,0) D3DCOLORVALUE Material ambient color  材质环境光颜色
    Ga (0,0,0,0) D3DCOLORVALUE Global ambient color  全局的环境光颜色
    Atteni (0,0,0,0) D3DCOLORVALUE Light attenuation of the ith light. See Attenuation and Spotlight Factor (Direct3D 9).
    第i个光源的衰减因子
    Spoti (0,0,0,0) D3DVECTOR Spotlight factor of the ith light. See Attenuation and Spotlight Factor (Direct3D 9).
    第i个光源的聚光灯因子
    sum N/A N/A Sum of the ambient light  环境光总和
    Lai (0,0,0,0) D3DVECTOR Light ambient color of the ith light  第i个光源环境光颜色

    The value for Ca is either:

    • vertex color1, if AMBIENTMATERIALSOURCE = D3DMCS_COLOR1, and the first vertex color is supplied in the vertex declaration.
    • vertex color2, if AMBIENTMATERIALSOURCE = D3DMCS_COLOR2, and the second vertex color is supplied in vertex declaration.
    • material ambient color.

    Note    If either AMBIENTMATERIALSOURCE option is used, and the vertex color is not provided, then the material ambient color is used.
               m_pDevice9->SetRenderState(D3DRS_AMBIENTMATERIALSOURCE , D3DMCS_MATERIAL);

    To use the material ambient color, use SetMaterial as shown in the example code below.

    Ga is the global ambient color. It is set using SetRenderState(D3DRS_AMBIENT). There is one global ambient color in a Direct3D scene. This parameter is not associated with a Direct3D light object.

    Lai is the ambient color of the ith light in the scene. Each Direct3D light has a set of properties, one of which is the ambient color. The term, sum(Lai) is a sum of all the ambient colors in the scene.

    Ca的值可以是:

    顶点颜色1,如果AMBIENTMATERIALSOURCE = D3DMCS_COLOR1,并且顶点声明中给出了第一个顶点的颜色。
    顶点颜色2,如果AMBIENTMATERIALSOURCE = D3DMCS_COLOR2,并且顶点声明中给出了第二个顶点的颜色。
    材质的环境反射色。

    注意 如果使用了任何一种AMBIENTMATERIALSOURCE,但是没有提供顶点颜色,那么系统会使用材质的环境反射色。

    要使用材质的环境反射色,按以下示例代码使用SetMaterial方法。

    Ga为全局的环境反射色,通过SetRenderState(D3DRENDERSTATE_AMBIENT)设置。Direct3D场景中只有一个全局环境反射色,它与其余Direct3D光源无关。

    Lai为场景中第i个光源的环境反射色。每个Direct3D光源都有一组属性,其中一个就是环境反射色。符号sum(Lai)表示场景中所有环境反射色的总和。

    Example

    In this example, the object is colored using the scene ambient light and a material ambient color.

    #define GRAY_COLOR	0x00bfbfbf
    
    // create material
    D3DMATERIAL9 mtrl;
    ZeroMemory(&mtrl, sizeof(mtrl));
    mtrl.Ambient.r = 0.75f;
    mtrl.Ambient.g = 0.0f;
    mtrl.Ambient.b = 0.0f;
    mtrl.Ambient.a = 0.0f;
    m_pd3dDevice->SetMaterial(&mtrl);
    m_pd3dDevice->SetRenderState(D3DRS_AMBIENT, GRAY_COLOR);
    

    According to the equation, the resulting color for the object vertices is a combination of the material color and the light color.

    These two images show the material color, which is gray, and the light color, which is bright red.

    untitled red

    The resulting scene is shown below. The only object in the scene is a sphere. Ambient light lights all object vertices with the same color. It is not dependent on the vertex normal or the light direction. As a result, the sphere looks like a 2D circle because there is no difference in shading around the surface of the object.
    渲染得到的场景如下所示。场景中唯一的物体是一个球体。环境光用相同的颜色对物体的所有顶点进行光照计算,它不依赖于顶点法向和光的方向。因此,球体看起来像是二维的圆,因为物体的表面没有明暗变化。

    light

    To give objects a more realistic look, apply diffuse or specular lighting in addition to ambient lighting.

    May 05

    多流提高效率?

    用了dx9的多流,在nvida 8600gt 上确实能提高效率,不过不是很明显,高10%多点吧,可能我画的顶点还少,只有900个左右。
    在nvida mx400上比没有使用流的差了很多(这个也不是dx9显卡,可能支持的不好),只有1半的fps,不过奇怪的是切换到全屏,fps就差不太多了,还是略低。
    在nvida 6200 上窗口模式使用流fps 略低。但全屏时能高10%左右。
    奇怪即使窗口不能独占设备,也不能相差这么大啊。
    或许多流这个东西到了dx10显卡才被很好的支持吧。
    越看越像opengl的机制. opengl1.0 开始的颜色、顶点、法线缓冲区都是分开的。

    May 04

    使用 d3d 游标

    函数:
    //采用32*32像素矩形图片。
    //hotx,hoty 为点击的热点,即判断游标是否点中的点。
    m_pDevice9->SetCursorProperties(hotx,hoty,surface);
    m_pDevice9->ShowCursor(TRUE);
    检测是否支持:
    if(m_Caps.CursorCaps & D3DCURSORCAPS_COLOR)
    {
      //_T("...支持高分辨率游标400\n")
    }
    else if(m_Caps.CursorCaps & D3DCURSORCAPS_LOWRES)
    {
      //T("...支持高分辨率游标\n");
    }
    在dx2006 中不需要调用 SetCursorPosition 函数.dx会自动更新数据
    注意事项:
    1 传递给SetCursorProperties函数的 LPDIRECT3DSURFACE9 参数必须是 default  a8r8b8g8资源 :
    LPDIRECT3DSURFACE9 surface;
    m_pDevice9->CreateOffscreenPlainSurface(32,32,D3DFMT_A8R8G8B8,D3DPOOL_DEFAULT,&surface,NULL);
    2 当使用dinput鼠标时不能设置独占模式 DISCL_EXCLUSIVE , 否则不能显示游标.
    3 不管源图片多大最终是以32*32大小呈现
    4 不需要修改 WM_SETCURSOR 消息。return false 就好。只需要在win32 和 dinput控制切换的时候调用
    SetCursor(NULL);
    m_pDevice9->ShowCursor(TRUE);
    好处:
    当硬件支持高分辨率鼠标时(基本都支持)。鼠标刷新不受图像刷新速度影响,即使你的图形fps<5,游标一样能快速移动。就像windows操作系统即使挂掉了,鼠标还是能移动

    April 30

    DINPUT8 Buffer overflow

    long long ago。。。
    DIPROPDWORD property;
    property.dwData = 16;
    这个值基本上够用,但调试的时候会报 buffer overflow 的警告
    所以调高其到255(最大可以调整到1023)。

    在GetDeviceData() 之前如果设备丢失必须先 Acquire() 获取设备。
    不然会有个警告
    看了quake3 可以在GetDeviceData 前调用 Acquire一次, 无论设备是否丢失,这个函数不怎么影响效率。
    当鼠标移动到NC区域,如标题栏,这时候也要卸载dinput鼠标,从dinput获得鼠标位置使用 PtInRect 判断是否在客户区域内,
    但较慢的机器往往有几率不能判断出来,使得dinput鼠标没有卸载,这样拖动标题栏会没有反应。但很多时候又正常。
    只能通过GetCursorPos 来获得鼠标位置再来判断是否出了客户区。

    April 21

    dxcpl 调试

    设置 dxcpl debug output level 为最大,出现大量下面的警告:
    Direct3D9: (WARN) :Ignoring redundant SetRenderState。。。
    说明某个SetRenderState 反复设置相同的值。如:
    Direct3D9: (WARN) :Ignoring redundant SetRenderState - 24
    反复设置 D3DRS_ALPHAREF 为相同值。
    这样会降低效率。既然已经设置过了除非设置不同的值,否则就无需再设置了,应该减少的函数调用还是可以减少的。
    检查dx内存泄漏可以用dcpl 中的 break on allocid 来观察,好像不行
    可以,往往一个资源未卸载导致一堆泄漏,从最后一个allocid 查起。有时看运气,跟运行时间也有关系。

    debug 版本工程预处理定义加入 D3D_DEBUG_INFO
    用的2006的dxcpl,里面的设置似乎只影响dx9程序,当在调试和运行版切换时,dx9库的程序效率下降,dx8的反倒没有什么变化
    几次忘掉这个事,以为那里错了,效率差了这么多,忙忙呼呼找上半天。

    April 15

    SwapEffect

    使用D3DSWAPEFFECT_DISCARD 才能支持全屏抗锯齿,但使用GetBackBuffer可能获得不到后台缓冲。用这个方法截屏就不行了。而且DISCARD 并不像某些书写的效率高,在我的8600GT 上还没有COPY效率好。
    或者3D游戏多边形多起来才能体现效率吧,毕竟3D游戏都是需要抗锯齿给玩家选择的
    主要是因为设置了puredevice才造成不能GetBackBuffer.但有些显卡又可以调用
    DISCARD 是要用的。puredevice 也需要。截屏似乎用截屏键更有效,或者还有其他方法来实现

    D3DCREATE_FPU_PRESERVE

    在FPU中,却存在着三种运算精度:single precision(24bits),double precision(53bits),double extended precision(64bits)。FPU的默认精度是53bits的double precision。D3D的CreateDevice函数会将FPU的运算精度改成24bits,除非指定了D3DCREATE_FPU_PRESERVE参数。
    March 29

    锁定压缩纹理

    锁定纹理是指定区域是不需要除4 (压缩纹理是4*4块元素作为一个单位,如下面结构,即一个单位可以计算出16个像素值)。
    typedef struct ddsColorBlock_s
    {
        unsigned short          colors[ 2 ];
        unsigned char           row[ 4 ];
    }
    ddsColorBlock_t;
    所以压缩纹理长度宽度必须是4的倍数。
    dx9可能内部做了处理.使用LockRect是不需要自己对应到指定的块(如64,64 像素在压缩纹理文件中属于16*16 块的位置).
    //x,y 为像素位置
    D3DLOCKED_RECT LockedRect;
    RECT rt = {x,y,x+1,y+1};
    pTex->LockRect(0,&LockedRect,&rt,D3DLOCK_READONLY);

    //其实对于4*4 个像素,例如下面
    {0,0}{1,0}{2,0}{3,0}
    {0,1}{1,1}{2,1}{3,1}
    {0,2}{1,2}{2,2}{3,2}
    {0,3}{1,3}{2,3}{3,3}
    LockedRect.pBits 内容是相同的即一个ddsColorBlock_t结构元素(解压这个结构即可获得上面的数据)

    February 26

    FreeType字体显示模糊

    我设置的是让左上角做为原点(0,0), 用正交投影来实现的2D显示, 同样的2d 引擎HGE也这样做的。但我用FreeType实现的文字比起HGE模糊暗上一些。打印出来的点阵都是相同的,开始还以为 alphablend 哪里弄错了。偶然才发现是设置投影矩阵的影响。HGE 做法:
    void HGE_Impl::_SetProjectionMatrix(int width, int height)
    {
    D3DXMATRIX tmp;
    D3DXMatrixScaling(&matProj, 1.0f, -1.0f, 1.0f);
    D3DXMatrixTranslation(&tmp, -0.5f, height+0.5f, 0.0f);
    D3DXMatrixMultiply(&matProj, &matProj, &tmp);
    D3DXMatrixOrthoOffCenterLH(&tmp, 0, (float)width, 0, (float)height, 0.0f, 1.0f);
    D3DXMatrixMultiply(&matProj, &matProj, &tmp);
    }
    而我只调用了:
    D3DXMatrixOrthoOffCenterLH(&m_matProj,0,(float)width,(float)height,0,0.0f,1.0f);
    d3dOK( m_pDevice9->SetTransform(D3DTS_PROJECTION, &m_matProj) );
    作用是相同的:把左上角作为原点,不同的是 HGE 相当于(-1,-1)作为了原点往左上移动了一个像素
    结果如下:
    freetype
     freetype1
    奇怪的问题。
    我也同样向左上移动:字体显示就亮了些,特别是对于笔画粗的文字,也都细了。
    D3DXMATRIX tmp;
    D3DXMatrixOrthoOffCenterLH(&m_matProj,0,(float)m_iWidth,(float)m_iHeight,0,0.0f,1.0f);
    D3DXMatrixTranslation(&tmp, -0.5f, -0.5f, 0.0f);
    D3DXMatrixMultiply(&m_matProj, &tmp, &m_matProj);
    不知道原因,如果有高手明白请不吝赐教。

    重新编译了FreeType ,打开了freetype 对bytecode interpreter 的支持。修改ftoption.h
    即使用字体本身的HINTING来读取小字体
    #define TT_CONFIG_OPTION_BYTECODE_INTERPRETER
    屏蔽了
    /*#define TT_CONFIG_OPTION_UNPATENTED_HINTING*/

    打开了对液晶显示器的支持
    #define FT_CONFIG_OPTION_SUBPIXEL_RENDERING

    上面的图显示的英文要比汉字暗一些。设置纹理操作为modulate2x
    m_pDevice9->SetTextureStageState( 0,D3DTSS_ALPHAOP,D3DTOP_MODULATE2X );
    提高alpha值。结果:
    finial 

    终于找到了原因,其实跟正交投影没有一点关系。主要是纹理滤波的问题,浪费了大量时间,不过也得到了不少,
    1 如FreeType  使用字体文件bytecode interpreter (版权归字体创建者所有),来显示小的文字
    2 对于正交投影的理解。经过正交矩阵相乘最终的点映射到2维坐标(-1,-1) 和(1,1)之间。中心点为(0,0)
    当正交矩阵移动0.5,其实相当于坐标系内物体移动了1个像素,因为被正交坐标系(0,0)分成了2个部分。而这个偏移导致字体不在模糊是因为把变换的点,再映射到视口的时候产生了0.5像素大小,如300.5, 这样的0.5被舍弃,字体大小略有变化,这个变化也影响了纹理滤波,所以显示文字时设置滤波最好是: 系统默认D3DTEXF_NONE或者D3DTEXF_POINT,不过字体大的时候相对影响不明显,到是可以用来加粗字体(不推荐)
    //缩小滤波
    m_pDevice9->SetSamplerState( i,D3DSAMP_MINFILTER,D3DTEXF_POINT) ;
    //放大滤波
    m_pDevice9->SetSamplerState( i,D3DSAMP_MAGFILTER,D3DTEXF_POINT);
    //mipmap 选择
    m_pDevice9->SetSamplerState( i,D3DSAMP_MIPFILTER,D3DTEXF_LINEAR);

    找到了实现文字粗体的函数:

    if(!FT_Load_Glyph(m_FTFace,idx,FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP))
    {
        FT_GlyphSlot glyph = m_FTFace->glyph;

        if (glyph->format == ft_glyph_format_outline)
        {

            if(m_bShadow)
            {
                //加粗文字
                FT_Outline_Embolden( &(glyph->outline), 260 );
            }
            if (!FT_Render_Glyph( glyph, m_bAntiAliased ?  FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO))
            {
              //更新到纹理中
            }
        }

    }

    //用粗体文字做底纹实现类似魔兽任务tips的文字
     wow

    January 20

    我对D3D中的顶点缓存的概念感到迷惑,特别是其分配和锁定的操作,不知道这些名称的含义是什么(转载)?

    当"allocating vertex buffers_分配顶点缓存"时:

    当使用 STATIC(这个是.net 情况,对于非.net版本dx是没有指定D3DUSAGE_DYNAMIC)标识分配顶点缓存时,这块缓存位于显存中。它的典型应用是只写一次并不被读回内存的情况。
    只有当标志 D3DUSAGE_DYNAMIC被设置时 D3DUSAGE_WRITEONLY标志才有意义。
    如果使用DYNAMIC标志创建缓存 (使用D3DUSAGE_DYNAMIC创建的缓存) ,并且D3DUSAGE_WRITEONLY 被使用,缓存被分配到显存中。

    如果使用DYNAMIC标志创建缓存 (使用D3DUSAGE_DYNAMIC创建的缓存) ,并且D3DUSAGE_WRITEONLY 不被使用, 缓存被分配到AGP 缓存中。(CPU从AGP缓存中读取的速度比显存中快,但比内存慢,是个很好的折中)
    如果没有足够的显存,顶点缓存被分配到AGP缓存中;如果AGP缓存也不够时,创建失败,除非使用POOL_ MANAGED 创建。在这种情况下,D3D的运行库会释放足够多的显存,用来创建顶点缓存,并在内存中保存创建的缓存的一个拷贝。那些被运行库释放的显存,在需要它们时可以从内存中复制到显存中。注意标志POOL_ MANAGED 并不被发送到驱动程序,因为这是D3D的附加的功能。

    对于NV1X 系列的GPUs, 在进行多数据流渲染得时候,如果有一个顶点缓存位于AGP缓存中,所有的其他位于显存中的顶点缓存会被移动到AGP缓存中。 

    当" locking vertex buffers_锁定顶点缓存"时:

    如果顶点缓存的创建标志是POOL_DEFAULT: 

    如果没有指定任何标志,程序将被暂停,因为它强制程序和GPU同步操作。->低效

    如果指定D3DLOCK_NOOVERWRITE 标志,应用程序不会改变缓存区已存在的内容,运行库会在以后继续使用这块缓存。当应用程序调用这个函数时,驱动程序立即返回。->高效

    如果指定D3DLOCK_DISCARD 标志,程序将会更新整个缓存的值,实际上是分配一块新的缓存,即驱动程序重命名它。->高效

    [注解:因为CPU和GPU是异步的操作,所以当CPU通过系统总线和GPU同步时,需要等到GPU把当前的工作做完。例如,当GPU正在对一块缓存进行DMA操作时,但往往CPU并不对GPU操作的那块缓存进行操作,所以CPU可以和GPU一起工作。当不指定操作标志时,CPU等待GPU完成绘制工作才更新顶点缓存,所以低效。如果指定D3DLOCK_NOOVERWRITE,表示CPU只更新顶点缓存中剩余的缓存,不考虑是否有其他图形绘制是正在使用这个的缓冲区段绘制图形,强制更新那段缓存并返回,而不像默认参数0那样等待前面的绘制结束,而不更新已经写入的顶点值,所以在CPU写入的时候,GPU可以并行的对那些已经存在的顶点值进行DMA等操作,所以高效;如果使用D3DLOCK_DISCARD 标志,说明当前分配的缓存大小不够了,需要重新使用缓存,CPU对这些新分配的缓存区域进行写操作,GPU这时可能还在异步处理旧的缓存区,所以这种调用也是高效的。调用完毕,收回释放的缓存。]


    来自 GameRes 说明了 Lock 的标志含义,我改了一部分内容。因为 D3DLOCK_NOOVERWRITE 和 D3DLOCK_DISCARD 是要经常使用的。越熟练越好
    NOOVERWRITE 并不是不覆盖数据,如果2个三角形在缓存中地址相同。后面定点内容会覆盖前面的。而前面的三角形将不能画出来。

    如果缓冲区开的足够大,render 几帧才能使用完这个缓冲。可以一直使用NOOVERWRITE 就可以。当不够使用时在从头开始使用这个顶点缓冲。如果足够大。开头的顶点基本确定已经画完(都是前几帧的图形顶点内容了。被覆盖也无所谓),如果缓冲很小lock时可以调用DISCARD,表示丢弃缓冲区内容(被丢弃的内容如果在使用中还可以被GPU继续使用。NOOVERWRITE 不会保留,所以速度更好)。如下使用开头的6个顶点画2个四边形。但该用NOOVERWRITE 将之后后面的四边形

       Quad->Lock(0, 6*sizeof(Vertex), (void**)&v, D3DLOCK_DISCARD);

       // quad built from two triangles, note texture coordinates:
       //v += 6;
       v[0] = Vertex(0.0f, 0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
       v[1] = Vertex(0.0f,  1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
       v[2] = Vertex( 1.0f,  1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

       v[3] = Vertex(0.0f, 0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
       v[4] = Vertex( 1.0f,  1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
       v[5] = Vertex( 1.0f, 0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);

       Quad->Unlock();

       Device->SetTexture(0, Tex1);
       Device->SetStreamSource(0, Quad, 0, sizeof(Vertex));
       Device->SetFVF(Vertex::FVF);
       Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

       Quad->Lock(0, 6*sizeof(Vertex), (void**)&v, D3DLOCK_DISCARD);
         // quad built from two triangles, note texture coordinates:
       v[0] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
       v[1] = Vertex(-1.0f,  0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 0.0f);
       v[2] = Vertex( 0.0f,  0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);

       v[3] = Vertex(-1.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 0.0f, 1.0f);
       v[4] = Vertex( 0.0f,  0.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 0.0f);
       v[5] = Vertex( 0.0f, -1.0f, 1.25f, 0.0f, 0.0f, -1.0f, 1.0f, 1.0f);

       Quad->Unlock();

       Device->SetTexture(0, Tex);
       Device->SetStreamSource(0, Quad, 0, sizeof(Vertex));
       Device->SetFVF(Vertex::FVF);
       Device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);

    当使用 D3DLOCK_NOOVERWRITE  标志时,似乎Lock 使用偏移地址没什么意义.或许可以提高性能

    January 10

    Alpha Blend 和 SetTextureStageState

    使用 alpha Blend 必须激活 D3DRS_ALPHABLENDENABLE
    m_pDevice9->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);
    而使用 SetTextureStageState  混合多纹理时必须关闭 D3DRS_ALPHABLENDENABLE .使用D3DRS_SRCBLEND 和 D3DRS_DESTBLEND 时候。当前纹理层的颜色操作也必须是默认的 D3DTA_MODULATE.
    否者。。。。
    m_pDevice9->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE);
    m_pDevice9->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE);
    相当于 D3DTA_ADD (发光映射)
    m_pDevice9->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_DESTCOLOR);
    m_pDevice9->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
    相当于 D3DTA_ADDSIGNED (细节映射)
    m_pDevice9->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_ZERO);
    m_pDevice9->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_SRCCOLOR);
    相当于 D3DTA_MODULATE (黑暗映射)

    因为我公司的破机器只能支持2个纹理层混合(尽管dx有8个纹理stage,但家里的nvdia 6200 才支持3个纹理,该换了)。所有当纹理过多时。2层混合后就要提交顶点。后面纹理采用SetRenderState 的办法和已经提交的纹理进行混合(当然后面的也可以是2个纹理混合的)
    7个纹理,出了第一个是单层外。后面都是2层纹理一画。(法线贴图 + 2个火颜色混合 + 火和灯光混合)
    m_pDevice9->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE); 
    m_pDevice9->SetRenderState( D3DRS_SRCBLEND, D3DBLEND_ONE);
    m_pDevice9->SetRenderState( D3DRS_DESTBLEND, D3DBLEND_ONE);

    BindTexture(0,g_ptex1);         //火
    DxUpdateTSS(0,0,NULL);        //设置纹理层0为默认 MODULATE 操作
    DisableStage(2);                  //不使用纹理层2
    BindTexture(1,g_ptex6);        //灯
    DxUpdateTSS(1,RSS_TSS_COLOR_ADD,NULL);
    m_pDevice9->SetStreamSource(0, g_pvbQuad1, 0, sizeof(Vertex));
    m_pDevice9->SetFVF(Vertex::FVF);       
    m_pDevice9->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 2);



                                             多层纹理
    似乎 D3DCREATE_MULTITHREADED 也有些问题。我一直用多线程load free 纹理。一定时间后,最后屏幕上纹理消失了。奇怪的是跟踪进去。发现纹理指针还在。而且很不容易出现,有时放2个小时都不会出现。不过本来该避免这个参数,把load 文件放入了线程。而加载纹理放在主进程。

    Normal map 法线贴图

        D3DXVECTOR3 vLight;
        POINT kPoint;
        g_pDIMouse->GetAbsolutePos(&kPoint);
        // Create a vector from the cursor position (no Z axis for this, the mouse is in 2D)
        //主要记住如何用 鼠标坐标获得光照方向 L xy 范围在 -1 ~ 1 . 当然也可以调整成一个固定值
        vLight.x = ((2*(float)kPoint.x / (float)g_D3DSettings.m_nDeviceWidth) - 1);
        vLight.y = ((2*(float)kPoint.y / (float)g_D3DSettings.m_nDeviceHeight) - 1);
        vLight.z = 1.0f;
        // Normalize the vector. See the tutorial for more info! Normalization reduces the
        // magnitude of a vector to 1, but keeps the original direction. This makes it a
        // unit vector. Also see the Cheaters Guide to 3D Maths!
        // 归一化
        D3DXVec3Normalize( &vLight, &vLight );
        // Now that we have our light vector, we need to turn it back into a greyscale
        // RGB to use to modulate the texture with. Remember that we've normalized this
        // vector, so .x and .y will be between 0.0 and 1.0f (which is why we need to
        // * 127.0f and + 128.0f). Calculate it yourself, if vLight.x was 0.5f.
        DWORD r = (DWORD)(127.0f * vLight.x + 128.0f);
        DWORD g = (DWORD)(127.0f * vLight.y + 128.0f);
        DWORD b = (DWORD)(127.0f * vLight.z + 128.0f);
        DWORD dwFactor = D3DCOLOR_XRGB(r, g, b);
        // Set the texture factor to the rgb calculated above. For more info on the texture
        // factor and how it works with the D3DTA_TFACTOR state, refer to DirectX Basics
        // Series 3 tutorial 4.
        g_pDevice->SetRenderState(D3DRS_TEXTUREFACTOR, dwFactor);  (N * L)
        // Modulate the texture (the normal map) with the light vector (stored
        // above in the texture factor). From the SDK: "Modulate the components of each argument
        // as signed components, add their products; then replicate the sum to all color
        // channels, including alpha".
        g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
        g_pDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_DOTPRODUCT3 );
        g_pDevice->SetTextureStageState( 0, D3DTSS_COLORARG2, D3DTA_TFACTOR );
        // Set the texture to be the normal map
        g_pDevice->SetTexture( 0, g_pNormalMapTexture );
    法线贴图使用 Phong 着色会好一些:
        m_pDevice9->SetRenderState(D3DRS_SHADEMODE,D3DSHADE_PHONG);
    例子地址
    http://www.thehavok.co.uk/scene/32bits/tutorials/directx/techniques/source/dot3dinput.zip

    December 27

    3D图形渲染通道负载优化的几种方法

     

    一般来说, 定位渲染通道瓶颈的方法就是改变渲染通道每个步骤的工作量, 如果吞吐量也改变了, 那个步骤就是瓶颈. 找到了瓶颈就要想办法消除瓶颈, 可以减少该步骤的工作量, 增加其他步骤的工作量.

    一般在光栅化之前的瓶颈称作”transform bound”, 三角形设置处理后的瓶颈称作”fill bound”

    定位瓶颈的办法

    1. 改变帧缓冲或者渲染目标(Render Target)的颜色深度(16 到 32 位), 如果帧速改变了, 那么瓶颈应该在帧缓冲(RenderTarget)的填充率上.

    2. 否则试试改变贴图大小和贴图过滤设置, 如果帧速变了,那么瓶颈应该是在贴图这里.

    3. 否则改变分辨率.如果帧速改变了, 那么改变一下pixel shader的指令数量, 如果帧速变了, 那么瓶颈应该就是pixel shader. 否则瓶颈就在光栅化过程中.

    4. 否则, 改变顶点格式的大小, 如果帧速改变了, 那么瓶颈应该在显卡带宽上.

    5. 如果以上都不是, 那么瓶颈就在CPU这一边.

    优化方法36条:

    1. 尽量减少无用的顶点数据, 比如贴图坐标, 如果有Object使用2组有的使用1组, 那么不 要将他们放在一个vertex buffer中, 这样可以减少传输的数据量.

    2. 使用多个streamsource, 比如SkinMesh渲染, 可以把顶点坐标和法线这些每一帧都要修改的数据放在一个动态VB中, 其它不需要修改的(如贴图坐标)放到一个静态VB中, 这样就减少了数据传输量.

    3. 尽量使用16位的索引缓冲,避免32位的. 一方面浪费带宽, 一方面也不是所有的显卡都支持32位的索引缓冲.

    4. 可以考虑使用vertex shader来计算静态VB中的数据.比如SkinMesh的顶点可以放到vectex shader中计算, 这样就可以避免每一帧都从AGP内存中向显存传送数据. 这样也可以使用静态VB了.

    5. 坚决避免使用Draw**UP一族的函数来绘制多边形.

    6. 在设计程序之前好好规划一下显卡内存的使用, 确保framebuffer, 贴图, 静态VB能够正好放入显卡的本地内存中.

    7. 尽量使顶点格式大小是32字节的倍数.可以考虑使用压缩过的顶点格式然后用vertex shader去解. 或者留下冗余的部分, 使顶点大小刚好使32字节的倍数.

    8. 顶点在顶点缓冲中的顺序尽量符合绘制的顺序, 考虑使用strips来代替list.

    9. 如果可能尽量多的使用static vertex buffer代替dynamic vertex buffer

    10. 动态VB使用DISCARD参数来lock更新, 使用NOOVERWRITE来添加.尽量不要使用不带参数的lock调用(0)

    11. 尽量减少lock的次数, 有些东西并不一定非要每一帧都更新VB, 比如人物动画一般每秒钟更新30次VB基本上就够了.

    12. 如果是因为需要绘制的顶点数据太多了可以考虑使用LOD, 但是现在的显卡的绘制能力都很强劲, 所以需要权衡一下LOD是否能够带来相应的好处, 如果过分的强化LOD很可能将瓶颈转移到CPU这边.

    13. 避免过多的顶点计算,比如过多的光源, 过于复杂的光照计算(复杂的光照模型), 纹理自动生成的开启也会增加顶点的计算量. 如果贴图坐标变换矩阵不是单位矩阵, 也会造成顶点计算量的增加, 所以如果纹理变换已经结束, 记得要将纹理变换矩阵设为单位矩阵同时调整贴图坐标.

    14. 避免Vertex shader指令数量太多或者分支过多, 尽量减少vertex shader的长度和复杂程度. 尽量使用swizzling代替mov

    15. 如果图象质量方面的计算(pixel shader)范围很大, 并且很复杂, 可以考虑试试全屏反走样.说不定更快.

    16. 尽量按照front – back的顺序来绘制.

    17. 在shader中判断Z值可以避免绘制不可见的象素, 但是nvidia建议简单的shader不要这么做.(Don’t do this in a simple shader)

    18. 如果可能, 尽量使用vertex shader来代替pixel shader.将计算从逐象素变成逐顶点.

    19. 尽量降低贴图的大小.过大的贴图可能造成贴图cache过载, 从而导致贴图cache命中降低.过大的贴图会导致显存过载, 这时候贴图是从系统内存中取的.

    20. 只要可能就用16位色的贴图, 如环境贴图或者shadow map.它们用32位色的贴图实在是浪费.

    21. 考虑使用DXT 贴图压缩

    22. 如果可能,使用简单的贴图过滤或者mip map, 除非必要否则尽量不要使用三线过滤和各项异性过滤. light map 和 环境贴图基本上都不需要使用它们.

    23. 只有真正需要修改的贴图才使用Dynamic, 并且使用DISCRAD和WRITEONLY来lock

    24. 太多的帧缓冲读写可以考虑关闭Z-Writes如有些多pass的渲染中的后续pass或者粒子系统等半透明几何物体(如果可以)

    25. 可能的话尽量使用alpha test代替alpha blending

    26. 如果不需要stencil buffer就尽量使用16位的Z buffer

    27. 减小RenderTarget 贴图的大小, 如shadow map 环境贴图. 可能根本不需要那么大效果就很好.

    28. Stencil 和 Z buffer 尽量一起clear. 他们本来就是一块缓冲.

    29. 尽量减少渲染状态的切换, 尽量一次画尽可能多的多边形.(根据显卡性能决定最多画多少,不过一般再多也不会多到哪里去。 除非你根本不需要贴图和渲染状态的切换)

    30. 尽量使用shader来代替Fixed Pipeline.

    31. 尽量使用shader来实现来取代Multipass渲染效果.

    32. 尽量优先先建立重要的资源, 如Render target, shaders, 贴图, VB, IB等等.以免显存过载的时候它们被创建到系统内存中.

    33. 坚决不要在渲染循环中调用创建资源

    34. 按照shader和贴图分组后再渲染.先按照shaders分组再按贴图.

    35. Color Stencil Z buffer尽量在一次Clear调用中清除.

    36. 一个Vertex buffer 的大小在2M-4M之间最好.

    参考文档: http://developer.nvidia.com/view.asp?IO=gdc_pipeperf

    lythm@citiz.net

    May 27

    学习directx遇到的问题

    1 MaxSimultaneousTextures 纹理同时叠加最大数目
    device->GetDeviceCaps( &Caps );
    int maxActiveTextures = Caps.MaxSimultaneousTextures;
    即在 DrawPrimitive  顶点之前,最多设置的纹理数量。如果你的机器显卡较差,这个数为2,就是刚刚好支持多纹理。
    如下代码所示如果MaxSimultaneousTextures < 4 将不能输出图形。注意:即使相同的纹理也算1个数量。如果做的纹理效果多于MaxSimultaneousTextures ,可以拆成多次调用DrawPrimitive 函数,达到相同效果。不过对于速度有影响
    例如 quake3 最多有10次渲染之多
     pd3dDevice->SetTexture( 0, g_pBackgroundTexture );
      pd3dDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
      pd3dDevice->SetTextureStageState( 0, D3DTSS_COLOROP,   D3DTOP_SELECTARG1 );
      pd3dDevice->SetTexture( 1, g_pWallTexture);
      pd3dDevice->SetTextureStageState( 1, D3DTSS_TEXCOORDINDEX,  0);
      pd3dDevice->SetTextureStageState( 1, D3DTSS_COLOROP,   D3DTOP_MODULATE );
      pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG1, D3DTA_TEXTURE );
      pd3dDevice->SetTextureStageState( 1, D3DTSS_COLORARG2, D3DTA_CURRENT );
      pd3dDevice->SetTexture(2, g_pEnvTexture);
      pd3dDevice->SetTextureStageState( 2, D3DTSS_TEXCOORDINDEX, 0);
      pd3dDevice->SetTextureStageState( 2, D3DTSS_COLORARG1, D3DTA_TEXTURE );
      pd3dDevice->SetTextureStageState( 2, D3DTSS_COLORARG2, D3DTA_CURRENT );
      pd3dDevice->SetTextureStageState( 2, D3DTSS_COLOROP,   D3DTOP_ADD );
      pd3dDevice->SetTexture(3, g_pEnvTexture);
      pd3dDevice->SetTextureStageState( 3, D3DTSS_TEXCOORDINDEX, 0);
      pd3dDevice->SetTextureStageState( 3, D3DTSS_COLORARG1, D3DTA_TEXTURE );
      pd3dDevice->SetTextureStageState( 3, D3DTSS_COLORARG2, D3DTA_CURRENT );
      pd3dDevice->SetTextureStageState( 3, D3DTSS_COLOROP,   D3DTOP_ADD );

      pd3dDevice->SetFVF(FVF );
      pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(VERTEX) );
      pd3dDevice->DrawPrimitive (D3DPT_TRIANGLESTRIP, 0, 2);
     
    2 DrawPrimitive()函数的 D3DPT_TRIANGLESTRIP  标志用的顶点第一个三角形为顺时针,第二个逆时针。。依次迭代。
    例如:记住uv 顺序即可 (0,0)->(1,0)->(0,1)->(1,1)
    VERTEX Vertices[] =
     {
      {  0.0f, 0.0f, 0.5f, 1.0f, 0.0f, 0.0f,},
      {  width,0.0f, 0.5f, 1.0f, 1.0f,0.0f, },
      {  0.0f,height, 0.5f, 1.0f, 0.0f, 1.0f,}, // x, y, z, rhw, tu, tv
      {  width,height, 0.5f, 1.0f, 1.0f,1.0f,},
     };
     
    3 获取窗口高度和宽度 IDirect3DSurface9,注意IDirect3DSurface9 Height 和 Width成员都是1。 #ifdef D3D_DEBUG_INFO这个宏时包围了这些成员。应该通过GetDesc获得表面属性,D3DSURFACE_DESC里面的height和width 才正确。
     IDirect3DSurface9* m_d3dsdBackBuffer;
     pd3dDevice->GetBackBuffer(0,0,D3DBACKBUFFER_TYPE_MONO,&m_d3dsdBackBuffer);
     //int height = m_d3dsdBackBuffer->Height;
     //int width = m_d3dsdBackBuffer->Width;
     m_d3dsdBackBuffer->GetDesc(pBackBufferSurfaceDesc)
     int height = pBackBufferSurfaceDesc->Height;
     int width = pBackBufferSurfaceDesc->Width;
     m_d3dsdBackBuffer->Release();  //一定要释放。
     
    4 创建设备时指定D3DCREATE_PUREDEVICE标志。可以参考D3DPRESENT_PARAMETERS参数说明
     
    5 无效纹理区域
      通过给纹理指定无效区域,应用程序可以对需要复制纹理的哪些子集进行优化,只有那些被标记为无效的区域才会被IDirect3DDevice9::UpdateTexture方法更新。当创建纹理时,整个纹理被标记为无效的。只有以下几种操作可以改变纹理的无效状态。
    • 给一个纹理添加一个无效区域。
    • 锁定纹理中的一些区域。此操作会把被锁定的区域添加到无效区域中,如果应用程序明确知道哪些是真正的无效区域,那么也可以关闭对无效区域的自动更新。
    • 将纹理作为目标表面进行更新的话会把整个纹理标记为无效的。
    • 对纹理调用IDirect3DDevice9::UpdateTexture方法会清除该纹理的所有无效区域。
    • 为了得到设备上下文(device context)而调用IDirect3DDevice9::GetDC

    对于mipmap纹理而言,无效区域被设在最高一级的纹理上,为了最小化对mipmap纹理中每一级的纹理更新所需复制的字节数,IDirect3DDevice9::UpdateTexture方法可以扩展无效区域并沿mipmap链更新子纹理。注意子级中无效区域的纹理坐标被向上舍入,也就是说,它们的小数部分被向上取整到纹理中最近的像素。

    因为每种类型的纹理具有不同类型的无效区域,所以每种类型的纹理都有相应的方法表示无效区域。二维纹理使用矩形,立体纹理使用立方体。

    • IDirect3DCubeTexture9::AddDirtyRect
    • IDirect3DTexture9::AddDirtyRect
    • IDirect3DVolumeTexture9::AddDirtyBox

    把以上方法的pDirtyRectpDirtyBox参数设置为NULL会扩大无效区域并使之覆盖整个纹理。
    每种锁定方法都有D3DLOCK_NO_DIRTY_UPDATE标志,使用这个标志可以防止对纹理无效区域的改变。更多信息,请参阅锁定资源
    如果在锁定操作时可以得到已改变区域的完整集合,那么应用程序应该使用D3DLOCK_NO_DIRTY_UPDATE标志。注意,对纹理一个的子级的锁定或复制操作(也就是说,未对纹理的最高一级进行锁定或复制操作)不会更新该纹理的无效区域。当应用程序锁定了纹理的子级而没有锁定纹理的最高一级时,它同样有责任对无效区域进行更新。

    6 固定管线怎么没有使用 SetFVF
    看了一个程序dxquake3中没有使用SetFVF,但也没有顶点或者像素shader。其实FVF在内部被DX转换为VertexDeclaration,所以用FVF或是VertexDeclaration都行。不使用SetFVF而是使用SetVertexDeclaration和SetVertexShader代替,SetVertexDeclaration声明数据存储格式,SetVertexShader提供数据处理方法SetFVF 是 dx9 新函数为了使写法更简单。dxquake3 中使用SetVertexDeclaration来提供的顶点格式,函数参数类似于
    D3DVERTEXELEMENT9 decl[] = 
    {
    { 0, 0,  D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0 },
    { 0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0 },
    D3DDECL_END()
    };
    但没有使用SetVertexShader设置着色器。相对于使用SetFVF,使用SetVertexDeclaration更有利于以后的扩展,实现可编程shader

    7 使用mesh调用ConvertToBlendedMesh 失败, 如果创建设备时设置 D3DCREATE_HARDWARE_VERTEXPROCESSING 很可能因为顶点不够用而失败
    使用 D3DCREATE_MIXED_VERTEXPROCESSING 参数会更好(使用skinmesh noindex 例子时候)。

    8  加入d3dx9dt.lib 而不是d3dx9.lib 这样对于没有 Release() 的资源会才报内存泄漏(调用 _CrtSetDbgFlag ) 。而 vld 之类的内存泄漏库才能检测到没有Release 的对象

    9 关键色
    加载图片的时候:  
      D3DXCreateTextureFromFileEx(m_pd3dDevice, "test.bmp", D3DX_DEFAULT, D3DX_DEFAULT,1,0,   D3DFMT_UNKNOWN,  
               D3DPOOL_DEFAULT,D3DX_FILTER_LINEAR,  D3DX_FILTER_LINEAR,0xffff00ff,NULL,NULL,&m_texture);   
                                                                       ~~~~~~~~~~ColorKey
      渲染的时候:  
      渲染前  
      m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,TRUE);  
      m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_SRCALPHA);        //使用加载图片 srcalpha  
      m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_INVSRCALPHA);   //使用 1-srcalpha
      渲染  
      render()  
      渲染后恢复  
      m_pd3dDevice->SetRenderState(D3DRS_ALPHABLENDENABLE,FALSE);  
      m_pd3dDevice->SetRenderState(D3DRS_SRCBLEND,D3DBLEND_ONE);  
      m_pd3dDevice->SetRenderState(D3DRS_DESTBLEND,D3DBLEND_ZERO);
    估计是加载的时候判断颜色相同。设置该像素 alpha 为 0

    10
    Lock VertexBuffer 时。使用偏移地址要同时指标值 D3DLOCK_NOOVERWRITE .这样除了更新的一小部分。其他部分不会被丢弃掉,当然是用默认参数0也可以。但用0效率很差
    对于动态缓存。使用偏移地址并不能增加多效效率。因为偏移的时候不能使用 DISCARD.不然没被更新的部分也会被丢弃。而往往希望没有更新的保留。不使用偏移地址将更新缓存所有地方。(特别是更新完整个缓存才unlock,反倒能快一些)

    上面认识错误。DISCARD 表示自己不再用那块显存。马上给我一块新的。原来用的位置可以被设备使用(如果还用完里面的顶点数据)。数据不会丢弃里面的内容。"丢弃"意味开发人员不再用了。至于何时扔掉有directx 管理
     同样NOOVERWRITE 也不是不覆盖的意思。只是表示开发者认为写入的位置不会覆盖被dx正在使用的顶点(draw),不用等待显卡draw结束(默认参数0表示等待结束),直接覆盖掉相应位置顶点数据(即使该顶点正在用来画图也会被强制覆盖)。这样反倒是表现起来截然"相反"了


    如果缓存数据没有变换。不要更新他.(只更新变化的数据能提高速度).一段缓存需要几次更新。可以开始锁定一次。最后unlock 就好。这个似乎不太影响性能

    11  D3DXLoadSurfaceFromMemory 的使用。可以把一种格式转换为另一种格式
     IDirect3DSurface9* temp;
     d3dOK( m_aTex[tex].m_pTex->GetSurfaceLevel(level,&temp) );

     int w = Get2PowerHigh(sz.cx);   //即 pitch 对齐到32位置
     //源缓存的尺寸
     RECT srcRc = { 0, 0, w, sz.cy };
     RECT dstRc = rt;
     //pixelFormatBits 得到的是每种表面格式的字节数. 可以参考dxtex 程序 如 D3DFMT_A8R8G8B8 返回 32
     unsigned char bytes = PixelFormatBits(pf)/8;
     
     HRESULT hr;
     //pitch = w * bytes  必须用纹理的宽度。这个宽度不一定等于参数width。指纹理pitch而不是buffer缓存数组的宽度
     //IDirect3DSurface9 是目标区域的 surface,第二个可以是NULL(256色的时代早就过去了). rt 是目标纹理需要更新的区域(2的幂次方可以有效防止失真)
     //data 是颜色缓冲数据。每个元素(a8r8g8b8)代表一个颜色值。pf data 的颜色格式(如 D3DFMT_A8R8G8B8)
     //NULL 为调色板,srcRc 为缓冲区的一块区域。D3DX_FILTER_NONE 过滤模式
     //最后参数为关键色
     if( FAILED(hr = D3DXLoadSurfaceFromMemory(temp,NULL,&rt,data,pf,w*bytes,NULL,&srcRc,D3DX_FILTER_NONE,0)))
     {
      ri->Error(ERR_D3D,_T("更新纹理,转换表面格式时失败:%0x\n"),hr);
     }

     if(m_aTex[tex].m_bmipmap)
     {
      d3dOK( D3DXFilterTexture( m_aTex[tex].m_pTex,NULL,0,D3DX_DEFAULT ) );
     }

    May 21

    directx8 faq

     

    Microsoft DirectX 8 开发人员常见问题

    摘要:本文对与 Microsoft DirectX 8.0 版有关的常见开发问题进行解答,其中包括有关 Direct3D、DirectSound 和 DirectPlay 的章节。

    目录

    一般性 DirectX 开发事宜
    Direct3D
       一般问题
       几何(顶点)处理
       性能调谐
       Direct3DX 实用程序库
    DirectSound
    DirectPlay

    一般性 DirectX 开发事宜

    我在试图编译示例时,为何得到那么多错误消息?

    您可能没有将 include 路径设置正确。许多编译器(包括 Microsoft® Visual C++®)包含 SDK 的一个较早版本,因此如果您的 include 路径首先搜索标准的编译器 include 目录,则您会得到不正确版本的头文件。为解决这一问题,请确保 include 路径和库路径被设为搜索 DirectX include 和库路径。另请参见 SDK 中的 dxreadme.txt 文件。如果您安装 DirectX SDK 而您又在使用 Visual C++,则可以选择让安装程序为您设置各个 include 路径。

    我得到关于全局唯一标识符 (GUID) 符号重复或缺失的连接器错误,怎么办?

    您使用的各种 GUID 应该得到一次性定义,且只能定义一次。如果您在插入 DirectX 头文件之前用 #define 定义 INITGUID 符号,则会插入 GUID 的定义。因此,你应确保只对一个编译单元进行此类操作。这一方法的一个替代方案就是用 dxguid.lib 库进行连接,其中包含所有 DirectX GUID 的定义。 如果您使用这一方法(建议),则您永远不要通过 #define 定义 INITGUID 符号。

    我能否将指针指向一个到较低版本号的 DirectX 接口?

    不能。DirectX 接口属于 COM 接口。 这意味着不要求将较高版本号的接口从相应的低版本号导出。因此,获得到 DirectX 对象的一个不同接口的唯一安全方法就是使用接口的 QueryInterface 方法。该方法是标准的 IUnknown 接口的一部分,所有 COM 接口必须从其导出。

    我能在同一应用程序中将 DirectX 8 组件和 DirectX 7 或更早的组件混用吗?

    您可以随意混用不同版本的“不同组件”;例如,您可以在将 DirectPlay 8 和 DirectDraw 7 用在同一应用程序中。但是,您通常不可以将“同一组件”的不同版本混用在同一应用程序中;例如,您不能混用 DirectDraw 7 和 Direct3D 8 (鉴于这些实际上是同一组件,因为 DirectDraw 已被含入 DirectX 8 的 Direct3D)。

    Release 或 AddRef 方法的返回值有何含义?

    返回值将是对象的当前参照计数。但是,COM 规范声明,您不应依赖该返回值;该值通常仅供用于调试目的。您观察到的值可能并非所期待的,因为各种其它系统对象可能保持着对你所创建的 DirectX 对象的参照。 因此,您不应编写反复调用 Release 的代码,一直到参照计数为零,因为此时可以将对象释放,即使另一组件可能仍旧在对其进行参照。

    我释放 DirectX 接口的次序很重要吗?

    应当没有问题,因为 COM 接口是参照计数的。 但是,在某些 DirectX 版本中,接口的释放次序有一些已知的缺陷。 安全起见,在可能的情况下,建议您以与创建时相反的次序释放接口。

    智能指针是什么,我要用它们吗?

    智能指针是一个 C++ 模板类,旨在封装指针功能。尤其有一些标准智能指针类,用于封装 COM 接口指针。这些指针自动进行 QueryInterface,而不是进行造型,并替您处理 AddRefRelease。您是否使用这些指针,大体上是个人偏好。如果您的代码包含大量的接口指针复制操作,即使用多重 AddRefsReleases,则智能指针可能会使您的代码更加简洁和不易出错。否则,不用也罢。Visual C++ 包含一个标准的 Microsoft COM 智能指针,是在 "comdef.h" 头文件中定义的(请在帮助中查找 com_ptr_t)。

    我在调试 DirectX 应用程序时遇到问题,能提示一下吗?

    调试 DirectX 应用程序时最常见的问题就是试图在 DirectDraw 表面被锁定时进行调试。这一情形会在 Microsoft Windows® 9x 系统上导致 "Win16 Lock" ,因此而无法绘制调试窗口。在锁定表面时指定 D3DLOCK_NOSYSLOCK 旗标,通常会消除该现象。Windows 2000 没有这一问题。在开发一个应用程序时,最好运行调试版本的 DirectX 运行时(在安装 SDK 时进行选择);该版本进行某些参数证实,并将一些有用的消息输出到调试程序的输出窗口。

    如何正确地检查返回代码?

    使用 SUCCEEDED 和 FAILED 宏。 DirectX 方法可以返回多个成功和失败代码,因此一个简单的 "==D3D_OK" 或类似的测试结果不总是够用的。

    DirectDraw 有何变化?

    DirectDraw 的大多数功能现已被含入新的 Direct3D8 接口。编制单纯 2D 应用程序的开发人员可能会希望继续使用旧的 DirectX 7 接口。对于编制带有某些 2D 元素的 3D 应用程序的开发人员,鼓励其使用 Direct3D 替代程序(例如点对象和公告牌纹理),因为这会改善性能和灵活性。

    我如何禁用 ALT+TAB 以及其它的任务切换功能?

    请切勿这样做。

    有什么值得推荐的对 COM 进行解释的书吗?

    Inside COM (《COM 内幕》),Dale Rogerson 著,Microsoft Press 出版,其中对 COM 进行了很好的介绍。如要详细考察 COM,Essential COM (《COM 精解》),Don Box 著,Longman 出版,这本书也很值得推荐。

    有什么关于一般性 Windows 编程的书吗?

    很多。但笔者认为值得推荐的书有:

    • Programming Windows (《Windows 编程》),Charles Petzold 著(Microsoft Press 出版)

    • Advanced Windows (《Windows 进阶》),Jeffrey Richter 著(Microsoft Press 出版)

    Direct3D

    一般问题

    我在何处可以找到有关 3D 图形技巧的信息?

    关于这一主题的标准书是 Computer Graphics: Principles and Practice (《计算机图形:原理与实践》),Foley, Van Dam 等著;对于任何想要了解几何、光栅化以及照明技巧的人来讲,这都是一个宝贵的资源。comp.graphics.algorithms 新闻组的 FAQ (“常见问题解答”)也包含有用的资料。

    Direct3D 能够仿真硬件未提供的功能吗?

    这要依具体情况而定。Direct3D 具有一个特性齐全的顶点处理流水线(包含对定制顶点着色器的支持)。但是,没有为像素级操作提供任何的仿真功能;应用程序必须检查相应的帽位,并使用 ValidateDevice API 来确定是否支持。

    Direct3D 包含有软件光栅器吗?

    没有。Direct3D 现在支持插件式软件光栅器。但是,目前并不默认提供任何软件光栅器。

    Direct3D 几何代码使用 3DNow! 和/或 Pentium III SIMD 指令吗?

    使用。 Direct3D 几何流水线有多个不同的代码路径(具体取决于处理器类型),并将使用 3DNow! 或 Pentium III SIMD 指令所提供的特殊的浮点操作(如果这些指令可用的话)。其中包括定制顶点着色器的处理。

    我如何防止透明的像素被写到 z 缓冲区?

    您可以借助高于或低于某一给定门限的 alpha 值来将像素滤除。您通过描绘状态 ALPHATESTENABLE、ALPHAREF 和 ALPHAFUNC 来控制这一操作。

    模板缓冲区是什么?

    模板缓冲区是一个记录每个像素信息的附加的缓冲区,很象一个 z 缓冲区。 实际上,该缓冲区就驻在 z 缓冲区的某些位中。常见的模板/z 缓冲区格式为 15 位的 z 和 1 位的模板,或 24 位的 z 和 8 位的模板。在描绘多边形时,可以针对每个像素,对模板的内容进行简单的算术操作。例如,可以增加或减少模板缓冲区,或在模板值没能通过一项简单的比较测试时,拒绝像素。可以将帧缓冲区的一个区域标出,然后只对标出(或未标出)的区域进行描绘,上述操作对于此类效果十分有用。各种体积效果就是很好的例子,比如阴影量。

    我如何使用模板缓冲区来描绘阴影量?

    这一效果以及其它体积模板缓冲区效果的关键在于模板缓冲区和 z 缓冲区之间的交互作用。带有阴影量的场景是分三个阶段描绘的。首先,照常使用 z 缓冲区来描绘没有阴影的场景。然后,在模板缓冲区中将阴影标出,如下所示。使用不可见的多边形绘制阴影量的正面,其中 z 测试被启用,而 z 写入被禁用,且在每有一个像素通过 z 测试时,就将模板缓冲区增加一次。以同样方式描绘阴影量的背面,但是要减少模板值。

    现在,请考虑单独一个像素的情形。假设摄像机不在阴影量中,场景中的相应点就有有四种可能性。如果从摄像机到点的光线不与阴影量相交,则不会绘制任何的阴影多边形,而模板缓冲区依旧为零。否则,如果点在阴影量的前面,则阴影多边形会被输出 z 缓冲区,而模板依旧保持不变。如果点位于阴影量下面,则会描绘同样多的正面阴影,而模板为零,即增加的次数与减少的次数一样多。

    最后一种可能性就是点位于阴影量中。在这种情况下,阴影量的背面会被输出 z 缓冲区,但正面则不然,因而模板缓冲区会为非零。结果就是,帧缓冲区位于阴影中的一些部分具有非零的模板值。最后,要实际描绘阴影,整个场景会被一个 alpha 混色的多边形集充溢一遍,其中仅模板值非零的像素受到影响。在随 DirectX SDK 一起提供的 "Shadow Volume" 示例中有关于该技巧的一个示例。

    Texel (特塞尔)对齐规则是什么?我怎样才能得到一一映射?

    这一点在 DirectX 8 文档中得到了全面的解释(在文章标题 Directly Mapping Texels to Pixels(直接将 Texel 映射到像素)下)。 但是总而言之,您应将屏幕坐标偏移一个像素的 –0.5,以便正确地与 texel 对齐。 大多数的插卡正确符合对齐规则,但是有一些较老的插卡或驱动程序则不然。要解决这些问题,建议您最好与相关的硬件厂商取得联系,请求得到更新过的应驱动程序或其所建议的变通办法。

    D3DCREATE_PUREDEVICE 旗标有何用途?

    如果在创建设备时指定了 D3DCREATE_PUREDEVICE 旗标,则 Direct3D 将创建一个“纯粹”的设备。这禁用 Get* 族类的方法,并使顶点处理仅限于硬件。这使得 Direct3D 运行时能够进行某些优化,以提供到驱动程序的最佳路径,而不必跟踪那么多的内部状态。也就是说,您使用 PUREDEVICE 时可以见到一定的性能优势,但却牺牲了某些便利条件。

    我如何使用颜色键控?

    DirectX 8 并不支持颜色键控。您应当换用 alpha 混色/测试,大体上这是一个更加灵活的技巧,没有与颜色键控相关的一些问题。

    我如何枚举多监视器系统中的所有显示设备?

    与其它的枚举功能相同,该功能已从基于回调变为由应用程序借助 IDirect3D8 接口的各种方法来进行简单的反复。调用 GetAdapterCount 来确定系统中显示适配器的数目。调用 GetAdapterMonitor 来确定适配器所连接的物理监视器(该方法返回一个 HMONITOR,您然后就可以在 Win32 API GetMonitorInfo 中使用该值,以确定关于物理监视器的信息)。确定某一具体显示适配器的特征,或在该适配器上创建一个 Direct3D 设备,简单到在调用 GetDeviceCapsCreateDevice 或其它方法时,通过替代 D3DADAPTER_DEFAULT 来传递相应的适配器编号。

    几何(顶点)处理

    D3DVERTEX 等等顶点类型有何变动?

    不再显式支持“预罐装”的顶点类型。多重顶点流系统允许对顶点数据进行更加灵活的装配。如果您想使用其中一个“传统”的顶点格式,则您可以建立一套相应的 FVF 代码。

    我对顶点流不大清楚,其工作原理如何?

    Direct3D 对从一个或多个顶点流馈入流水线的每个顶点进行组装。只有一个顶点流时,就对应于 DirectX 8 以前的老模型,即顶点来自单独一个源。借助 DirectX 8,不同的顶点组件可以来自不同的源;例如,一个顶点缓冲区可能含有位置和法线,而另一个则含有颜色值和纹理坐标。

    顶点着色器是什么?

    顶点着色器是一个用于处理单一顶点的过程。这是借助一种类似于汇编的简单语言来进行定义的,由 D3DX 实用程序汇编为一个 Direct3D 接受的令牌流。顶点着色器接受单独一个顶点和一组常量值的输入,并输出一个顶点位置(在剪贴空间),还可能输出一组用于光栅化的颜色和纹理坐标。请注意,在您有一个定制的顶点着色器时,顶点组件就不再有任何由 Direct3D 施加给它们的语义,而顶点就只是由您所创建的顶点着色器进行解释的任意数据。

    顶点着色器进行透视划分或剪裁吗?

    不。顶点着色器在已变换的顶点位置的剪贴空间输出一个纯系坐标。透视分割和剪裁是由后着色器自动进行的。

    我能借助顶点着色器生成几何图形吗?

    顶点着色器无法创建或消灭顶点;其一次只对单一顶点进行操作,即作为输入接收一个未经处理的顶点,而输出单独一个经过处理的顶点。因此可以将其用于操作已有的几何图形(应用变形或进行外观变换操作),但实际上无法生成新的几何图形。

    我能将一个定制的顶点着色器应用到固定功能几何流水线(或者进行相反的操作〕吗?

    不能。您必须选择其一。如果您正在使用一个定制的顶点着色器,则您负责进行整个顶点变换操作。

    如果我的硬件并不支持定制的顶点着色器,我可以使用吗?

    可以。Direct3D 软件顶点处理引擎完全支持定制的顶点着色器,且性能指标出奇的高。

    我如何确定硬件是否支持我的定制的顶点着色器?

    能够硬件支持顶点着色器的设备被要求填充 D3DCAPS8::VertexShaderVersion 字段,以指示其所支持的顶点着色器的版本级别。所有声称支持某一级别的顶点着色器的设备,必须支持所有合法的顶点着色器,这些顶点着色器符合针对该级别或较低级别的规范。

    有多少个常量寄存器可以用于顶点着色器?

    要求支持 DX8 顶点着色器的设备至少支持 96 个常量寄存器。设备的支持能力可能会超过这一最低数目,且可以通过 D3DCAPS8::MaxVertexShaderConst 字段进行报告。

    我可以在带有不同纹理坐标的顶点之间共享位置数据吗?

    该情形的一个通常示例就是一个立方体,其中您想为每个面使用一个不同的纹理。很不幸,答案是不行;目前还还不能独立索引每个顶点组件。即使是多顶点流,也是所有的顶点一起索引。

    在我提交一列带索引的原语时,Direct3D 是处理缓冲区中所有的顶点,还是只处理我索引过的顶点?

    在使用软件几何流水线时,Direct3D 首先转换您所提交的范围中的所有的顶点,而不是“根据要求”按照索引对其进行转换。这对于密集数据(即其中使用了大多数的顶点)效率更高,尤其是在可以使用 SIMD 指令时。如果您的数据比较松散(即很多顶点未被使用),则您可能需要考虑重新排列您的数据,以避免多余的转换。在使用硬件几何加速时,顶点经常是根据需要进行转换的。

    索引缓冲区是什么?

    索引缓冲区与顶点缓冲区极其类似,但其包含的是用于 DrawIndexedPrimitive 调用的索引。强烈建议您尽可能使用索引缓冲区,而不要使用原始的由应用程序分配的内存,其道理与顶点缓冲区相同。

    我注意到 32 位的索引现在是一种支持类型;我可以将其用在所有的设备上吗?

    不可以。你必须检查 D3DCAPS8::MaxVertexIndex 字段,以确定设备所支持的最大索引值。该值必须大于 216-1 (0xffff) 才能支持 D3DFMT_INDEX32 类型的索引缓冲区。另外请注意,某些设备可能支持 32 位的索引,但其所支持的最大索引值却小于 232-1 (0xffffffff);这样,应用程序必须遵从设备所报告的限制。

    将多重顶点流用于固定功能流水线有何限制?

    固定功能流水线要求每条顶点流水线是一个严格的 FVF 子集,即根据一个完整的 FVF 声明预定的。 另外请注意,您必须遵从 D3DCAPS8::MaxStreams 字段所报告的流水线数目的限制(现在的许多设备和/或驱动程序仅支持单一流水线)。

    性能调谐

    我如何能够改善我的 Direct3D 应用程序的性能?

    在优化性能时,需要考察下列几个关键问题:

    • 批处理大小:Direct3D 已为大批量原语进行过优化。一次调用所能发送的多边形越多,其效果也就越好。凭经验而论,建议平均每次调用 100 个以上的多边形。低于该水平,您可能不会得到最好的性能,而高于该水平,您收到的效果会递减,且可能会与并行事项发生冲突(参见下面的论述)。

    • 状态更改:更改描绘状态这种操作可能会很昂贵,尤其是在更改纹理时。因此,每帧所作的状态更改应尽可能地少,这一点很重要。另外,请尽量降低对顶点或索引缓冲区的更改。

      注意:在 DirectX 8 中更改缓冲区已不在象在以前版本中那样昂贵了,但依旧建议尽量避免更改顶点缓冲区。

    • 并行:如果能够安排描绘与其它处理同时进行,则您可以充分利用系统性能。这一目标可能会与降低描绘状态更改的目标相抵触。您需要在进行批处理以降低状态更改和较早将数据推出到驱动程序以达到并行目的之间找到一个平衡点。以循环方式使用多个顶点缓冲区,会有助于并行功能。

    • 纹理上载:将纹理上载到设备会消耗带宽并导致与顶点数据争用带宽。 因此,不要占用过多的内存这一点很重要,因为这会强制缓存方案为每一帧上载过多的纹理。

    • 顶点缓冲区和索引缓冲区: 您应当一直使用顶点缓冲区和索引缓冲区,而不是由应用程序分配的普通内存块。顶点缓冲区和索引缓冲区的锁定语义至少可以避免多于的复制操作。对于某些驱动程序,顶点缓冲区和索引缓冲区可能是更理想的存储器中的某些区域(可能是在视频或 AGP 存储器中),供硬件进行访问。

    • 状态宏锁定:这些是在 DX 7.0 中推出的,为将一系列的状态更改(包括照明、材质和矩阵更改)记录成一个宏提供了一个机制,该宏然后就可以通过单一的调用来重放了。这有两个优势:

      1. 您进行一次调用而不是多次调用,从而降低调用开销。

      2. 有感悟能力的驱动程序可以对状态更改进行预分析和预编译,从而使到图形硬件的提交速度快得多。

      状态更改可能依旧很昂贵,但使用状态宏至少会有助于降低部分成本。

    • 仅使用单独一个 Direct3D 设备:如果您需要描绘到多个目标,则请使用 SetRenderTarget。如果您是要创建一个带有多个 3D 窗口的窗口化程序,则请使用 CreateAdditionalSwapChain API。运行时已为单一设备进行过优化,使用多个设备时会有相当多的速度折扣。

    我应当使用哪些基本类型(条形、扇形、列表等)?

    在真实数据中遇到的许多网格,都具有多个多边形共享顶点的特性。为将性能最大化,最好将所转换的顶点中的重复率降低,并横跨总线将其发给描绘设备。使用简单的三角列表根本实现不了任何顶点共享,因而这是最不理想的方法。这一点已很清楚。然后所要作的选择就是使用条形和扇形(暗示多边形之间的具体连接关系),还是使用索引列表。在数据自然归入各种条形和扇形的情况下,这些就是最为合适的选择,因为发给驱动程序的数据被降至最低。但是,将网格分解条形和扇形经常会造成大量分离块,暗示有大量的 DrawPrimitive 调用。因此,最富效率的方法通常是使用带三角列表的单独一个 DrawIndexedPrimitive 调用。使用索引列表的另一个优势就是,这在连续三角形仅共享单独一个顶点时也有益处。总而言之,如果您的数据自然归入各种较大的条形和扇形,就使用条形和扇形,否则就使用索引列表。

    如果我要生成动态数据,怎样才算是较好地使用顶点缓冲区了呢?

    1. 借助 D3DUSAGE_DYNAMIC 和 D3DUSAGE_WRITEONLY 使用旗标以及 D3DPOOL_DEFAULT 缓冲池旗标来创建一个顶点缓冲区。(如果您正使用软件顶点处理功能的话,还要指定 D3DUSAGE_SOFTWAREPROCESSING。)

    2. I = 0。

    3. 设置状态(纹理、描绘状态等)。

    4. 检查缓冲区内是否有空间,即 i.e.I + M <= N? (其中 M 是新顶点的数目)。

    5. 如果为真,则借助 D3DLOCK_NOOVERWRITE 对 VB 进行 Lock (锁定)。这告知 Direct3D 和驱动程序,您将要添加顶点,而并不修改您先前批处理过的顶点。 因此,如果当时正在进行一项 DMA 操作,则并不中断该操作。否则,转至 11。

    6. 在 I 处填充 M 个顶点。

    7. Unlock (解锁)。

    8. 调用 Draw[Indexed]Primitive。对于未经索引的,请将 I 用作 StartVertex 参数。对于已编索的基本类型,请确保索引指向顶点缓冲区的正确部分(要做到这一点,使用 SetIndices 调用的 BaseVertexIndex 参数可能最为容易)。

    9. I += M。

    10. 转到 3。

    11. 好,空间已用尽,就让我们开始一个新的顶点缓冲区。 我们不想使用同一个顶点缓冲区,因为有可能有一个 DMA 操作正在进行之中。我们将这一情形告知 Direct3D,方法是借助 D3DLOCK_DISCARD 旗标,将“同一个”顶点缓冲区锁定。 这意思是,“你可以给我一个新的指针了,因为我已用完旧的指针,不再在意旧的内容。”

    12. I = 0。

    13. 转至 4 (或 6)。

    Direct3DX 实用程序库

    D3DX 图象文件加载器函数支持哪些文件格式?

    D3DX 图象文件加载器函数支持 BMP、TGA、PNG、JPG、DIB、PPM 和 DDS 文件。

    D3DX 中的文字描绘函数好象失效,我的操作有什么问题吗?

    使用 ID3DXFont::DrawText 功能时的一个常见错误就是为颜色参数指定一个零 alpha 组件,从而导致完全透明(即不可见)的文本。对于完全不透明的文本,请确保色彩参数的 alpha 成分完全饱和 (255)。

    对于字体描绘,我应当使用 ID3DXFont 还是 SDK 框架 CD3DFont 类?

    ID3DXFont 类能够处理字间距,因为它使用 GDI 来绘制字符串。这可能会有点慢,因为每次均需要调用 GDI。

    CD3DFont 设计用于加速和使用纹理化基本类型来绘制字符。它只能处理简单字体,并不支持 ID3DXFont 可用的全套格式选项,但适用于简单而快速的显示,诸如帧速率计数等等。

    对于产品代码,您可能需要实施您自己的字形描绘功能,即借助纹理化基本类型和/或基于 GDI 的方案(带避免重新绘制的缓存功能)。

    DirectSound

    我的应用程序启动时,为何为发出一阵净噪声?我注意到其它应用程序也有这个问题。

    您可能安装了调试用 DirectX 运行时。运行时的调试版本用静噪声填充缓冲区,以便帮助开发人员捕捉未经初始化缓冲区的缺陷。您无法保证 DirectSound 缓冲区在创建后的内容;尤其是您无法将缓冲区清零。

    DirectPlay

    我如何确保我的游戏将能够与各种 Network Address Translator (网络地址转换器,NAT) 和 Internet Connection Sharing (Internet 连接共享,ICS)设置正常工作?

    NAT 和 ICS 是较为复杂的主题,在 MSDN 上的另一篇文章中有更详尽的论述。但是,下列提示可以作为很好的一般性指导:

    • 通过 IDirectPlay8ClientIDirectPlay8Server 接口,使用一种客户端-服务器而不是点对点网络拓扑。

    • 将服务器放在清澈的 Internet 上,而不是在一个 NAT 后面。

    • 直接枚举游戏端口,而不是使用 DPNSVR。

    • 不要在您的消息中内嵌 IP 地址或端口号。

    有关点对点游戏、将服务器驻于 NAT 后面的事宜,以及针对各种不同的 Windows 操作系统上的 ICS 的具体建议,请参考更详尽的文档。

    DPNSVR 是作什么用的?

    DPNSVR 是一种用于枚举请求的转发服务,消除了多个 DirectPlay 应用程序在端口使用上的冲突所导致的各种问题。使用 DPNSVR 使得 DirectPlay 能够自动选择要使用的端口,同时又允许客户端对您的游戏进行枚举。默认为,DirectPlay 会使用 DPNSVR,因为这通常为应用程序提供了最大的灵活性;但是,您可以将其禁用,方法是在创建您的会话时指定 DPNSESSION_NODPNSVR 旗标。如果客户端使用 DPNSVR 端口来枚举主机,而主机又使用自己的端口来作出响应的话,使用 DPNSVR 可能会导致客户端一侧的 NAT 发生问题;NAT 可能会拒绝将数据包转递给客户端,因为数据包不是来自请求被发往的同一端口。

    IDirectPlay8LobbyClient::Initialize 为何返回 DPNERR_NOTALLOWED?

    DirectPlay 不允许每个进程有一个以上的前端客户端或应用程序,因而试图创建多个客户端会导致返回这一错误。

    May 17

    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;
    };

    BackBufferWidth和BackBufferHeight:后备缓冲的宽度和高度。在全屏模式下,这两者的值必需符合显卡所支持的分辨率。例如(800,600),(640,480)。
    BackBufferFormat:后备缓冲的格式。这个参数是一个D3DFORMAT枚举类型,它的值有很多种,例如D3DFMT_R5G6B5、D3DFMT_X8R8G8B8为游戏后备缓冲常用格式,这说明后备缓冲的格式是每个像素16位,其实红色(R)占5位,绿色(G)占6位,蓝色(B)占5位,为什么绿色会多一位呢?据说是因为人的眼睛对绿色比较敏感。DX9只支持16位和32位的后备缓冲格式,24位并不支持。如果对这D3DFORMAT不熟悉的话,可以把它设为D3DFMT_UNKNOWN,这时候它将使用桌面的格式。

    BackBufferCount:后备缓冲的数目,范围是从0到3,如果为0,那就当成1来处理。大多数情况我们只使用一个后备缓冲。使用多个后备缓冲可以使画面很流畅,但是却会造成输入设备响应过慢,还会消耗很多内存。
    MultiSampleType和MultiSampleQuality:前者指的是全屏抗锯齿的类型,后者指的是全屏抗锯齿的质量等级。这两个参数可以使你的渲染场景变得更好看,但是却消耗你很多内存资源,而且,并不是所有的显卡都支持这两者的所设定的功能的。在这里我们分别把它们设为D3DMULTISAMPLE_NONE和0。
    可以通过CheckDeviceMultiSampleType 来检测当前装置是否支持某个抗锯齿类型
    typedef enum _D3DMULTISAMPLE_TYPE
    {
        D3DMULTISAMPLE_NONE               =  0,
        D3DMULTISAMPLE_2_SAMPLES       =  2,
        D3DMULTISAMPLE_3_SAMPLES       =  3,
        D3DMULTISAMPLE_4_SAMPLES       =  4,
        D3DMULTISAMPLE_5_SAMPLES       =  5,
        D3DMULTISAMPLE_6_SAMPLES       =  6,
        D3DMULTISAMPLE_7_SAMPLES       =  7,
        D3DMULTISAMPLE_8_SAMPLES       =  8,
        D3DMULTISAMPLE_9_SAMPLES       =  9,
        D3DMULTISAMPLE_10_SAMPLES      = 10,
        D3DMULTISAMPLE_11_SAMPLES      = 11,
        D3DMULTISAMPLE_12_SAMPLES      = 12,
        D3DMULTISAMPLE_13_SAMPLES      = 13,
        D3DMULTISAMPLE_14_SAMPLES      = 14,
        D3DMULTISAMPLE_15_SAMPLES      = 15,
        D3DMULTISAMPLE_16_SAMPLES      = 16,
        D3DMULTISAMPLE_FORCE_DWORD     = 0x7fffffff
    } D3DMULTISAMPLE_TYPE;
    //type 为以上的枚举值
    D3DMULTISAMPLE_TYPE type;
    int quality; //获取相对于type的最大质量等级(设置type抗拒值类型时,设置的质量等级只能小于这个数
    //m_d3dBackFmt 为后备缓冲格式,m_bWindowed 是否为窗口模式,
    m_pD3D9->CheckDeviceMultiSampleType( D3DADAPTER_DEFAULT,D3DDEVTYPE_HAL,m_d3dBackFmt, m_bWindowed,type,&quality );

    SwapEffect:交换缓冲支持的效果类型,指定表面在交换链中是如何被交换的。它是D3DSWAPEFFECT枚举类型,可以设定为以下三者之一:D3DSWAPEFFECT_DISCARD,D3DSWAPEFFECT_FLIP,D3DSWAPEFFECT_COPY。
    如果设定为D3DSWAPEFFECT_DISCARD,则后备缓冲区的东西被复制到屏幕上后,后备缓冲区的东西就没有什么用了,可以丢弃(discard 是否丢弃由显卡决定,但不再等待)了。
    如果设定为D3DSWAPEFFECT_FLIP,后备缓冲拷贝到前台缓冲,保持后备缓冲内容不变。当后备缓冲大于1个时使用
    设定D3DSWAPEFFECT_COPY的话, 后备缓冲拷贝到前台缓冲,保持后备缓冲内容不变。当后备缓冲等于1个时使用

    一般我们是把这个参数设为D3DSWAPEFFECT_DISCARD。如果想使用GetBackBuffer 获得后备缓冲内容打印屏幕画面。则不能使用DISCARD.
    很怀疑用DISCARD效率会好的说法。在我的8600GT 上_COPY 明显好于DISCARD. discard 做法是再次使用后备缓冲时 new 一个新的缓冲,旧的缓冲内容遗弃,如果还有使用旧的缓冲地方,不会影响新的内容,如使用抗锯齿必须是DISCARD。这样不用等待硬件同步。不过大部分是一次Present操作。用COPY在新机器上面反倒效率好些。毕竟new 一个 1440*900 的后台缓冲也是有消耗的。(对于DISCARD 做法仅仅是猜测。不同显卡可能不同。)
    hDeviceWindow:显示设备输出窗口的句柄
    Windowed:如果为FALSE,表示要渲染全屏。如果为TRUE,表示要渲染窗口。渲染全屏的时候,BackBufferWidth和BackBufferHeight的值就得符合显示模式中所设定的值。
    EnableAutoDepthStencil:如果要使用Z缓冲或模板缓冲,则把它设为TRUE。
    AutoDepthStencilFormat:如果不使用深度缓冲,那么这个参数将没有用。如果启动了深度缓冲,那么这个参数将为深度缓冲设定缓冲格式。常用值D3DFMT_24S8 (24 深度缓冲,8模板缓冲),D3DFMT_24X8(24 深度缓冲),D3DFMT_16(16深度缓冲)等等。
    //深度缓存和模板缓存的象素格式,如 D3DFMT_D24S8 , 24 位表示深度,8位为模板缓存。一般不会用到32位深度:D3DFMT_32
    //注意如果设置模板缓冲在Clear() 函数中也要清楚模板缓冲:设置参数 D3DCLEAR_ZBUFFER|D3DCLEAR_STENCIL

    Flags:通常为0 或D3DPRESENTFLAG_LOCKABLE_BACKBUFFER。不太清楚是用来做什么的,看字面好像是一个能否锁定后备缓冲区的标记。D3DPRESENTFLAG_DISCARD_DEPTHSTENCIL 丢弃模板缓冲区
    FullScreen_RefreshRateInHz:显示器的刷新率,单位是HZ,如果设定了一个显示器不支持的刷新率,将会不能创建设备或发出警告信息。为了方便,一般设为D3DPRESENT_RATE_DEFAULT就行了。
    PresentationInterval:如果设置为D3DPRENSENT_INTERVAL_DEFAULT,则说明在显示一个渲染画面的时候必要等候显示器刷新完一次屏幕。例如你的显示器刷新率设为80HZ的话,则一秒内你最多可以显示80个渲染画面。另外你也可以设置在显示器刷新一次屏幕的时间内显示1到4个画面。如果设置为D3DPRENSENT_INTERVAL_IMMEDIATE,则表示可以以实时的方式来显示渲染画面,虽然这样可以提高帧速(FPS),如果速度过快却会产生图像撕裂的情况,但当游戏完成时,帧速度一般不会过快。

    Dxut 框架 ModifyDeviceSettings 函数中修改 DXUTDeviceSettings* pDeviceSettings 成员(D3DPRESENT_PARAMETERS变量) pp来修改
    May 16

    Gamma 校正

         Gamma校正是全屏的, 现在的显卡都支持, 同样, Gamma校正可以实现一些特殊效果,各种图像淡入、淡出效果渐变等等。
       
    DirectX Graphics中, 色彩到屏幕输出通过Gamma梯度映射校正, 如图12.2,实际不完全这样,参看下面的英文内容,一般是个抛物线.这里讲的只相当于亮度调整而已。

    12.2

        图中梯度映射校正最大为65535, 表示梯度使用WORD表示. 每种原色都有各自的梯度映射校正, 默认的三原色梯度映射校正都是相同的梯度斜率1, 例如对于RGB(0, 128, 255)进行校正, 如图12.3

    图12.3

    0   [梯度映射校正]--> 0     [梯度到原色] --> 0   [最后输出颜色的RED分量]
    128 [梯度映射校正]--> 32896 [梯度到原色] --> 128 [最后输出颜色的GREEN分量]
    255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的BLUE分量]
    现在我们改变GREEN分量的梯度, 使其斜率为2, 如图12.4, 则有

    12.4

    0   [梯度映射校正]--> 0     [梯度到原色] --> 0   [最后输出颜色的RED分量]
    128 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的GREEN分量]
    255 [梯度映射校正]--> 65535 [梯度到原色] --> 255 [最后输出颜色的BLUE分量]
    使用这种Gamma校正, 屏幕会明显偏向于绿色.
    DirectX Graphics, RED, GREEN, BLUE梯度通过D3DGAMMARAMP结构定义, 
    typedef struct _D3DGAMMARAMP
    { 
        WORD                red  [256]; 
        WORD                green[256]; 
        WORD                blue [256];
    } D3DGAMMARAMP;

    然后通过IDirect3DDevice9的函数SetGammaRamp设置,
    void SetGammaRamp(UINT  iSwapChain, DWORD Flags, CONST D3DGAMMARAMP * pRamp);
    void GetGammaRamp(UINT  iSwapChain, D3DGAMMARAMP * pRamp);
    也可以使用WIN32函数:
    SetDeviceGammaRamp 设置gamma 和 GetDeviceGammaRamp 读取gamma(如果返回false则不支持gamma)
    交换链 iSwapChain 一般选0,Flags 为:D3DSGR_NO_CALIBRATION 和 D3DSGR_CALIBRATE
    控制应用gamma坡道之前是否对其进行校正,校正保证显示更加连续,但处理速度慢。通常仅设置gamma一次,这样不需要校正。如果想不断调整gamma制作动画效果可以使用坡道校正。
    这里注意有些显卡使用WORD中的高位字节, 有些使用低位字节, 所以最好两个字节设置相同的数值, 0xCOCO.
    Directx 只支持对整个屏幕的Gamma 校正
    SetGammaRamp will set the gamma ramp only on a full-screen mode application
    Gamma 值范围 0.6 - 3.0
    可用Gamma 实现淡入淡出效果:让所有数据由0渐变到索引值即可。
    检测硬件是否支持Gamma校正:
    device->GetDeviceCaps( &Caps );
     if( !(Caps.Caps2 & D3DCAPS2_FULLSCREENGAMMA) )
     {
           //不支持全屏Gamma校正
     }
    if( !(Caps.Caps2 & D3DCAPS2_CANCALIBRATEGAMMA) )   
     {
           //不支持应用Gamma坡道之前进行校正效果,SetGammaRamp() 不能使用 D3DSGR_CALIBRATE 标志
     }

    //quake3 中的调整gamma 方法。对应配置参数 r_gamma 调整 gamma, overbrightbits 调整亮度,r_intensity 调整对比度

     D3DGAMMARAMP GammaRamp; 
     float r_gamma = 1.6f; //gamma值,范围0.6-3.0
     static WORD s_gammatable[256];
     int  inf;
     int  shift = 0;   //亮度校正。范围视色深而定24位色及以上最大为2,16位最大为1 

     int i = 0;
     for ( i = 0; i < 256; i++ ) {
      if ( r_gamma == 1 ) {
       inf = i;
      } else {
       // gamma 校正算法,正常显示器gamma情况如下面的 fig1 图所示,要调节gamma 就要取 1/gamma 做指数,
       //得到 fig2 图示曲线, 从而叠加获得剃度直线
       inf = 255 * pow ( i/255.0f, 1.0f /r_gamma ) + 0.5f;   
      }
      inf <<= shift;   //overbright,对gamma剃度校正。感觉有了gamma校正可以不用这个.类似于gamma校正中的亮度校正
      if (inf < 0) {
       inf = 0;
      }
      if (inf > 255) {
       inf = 255;
      }
      s_gammatable[i] = inf<<8 | inf;     //使 word 的高位字节和低位字节相同
     }

     for ( i = 1 ; i < 256 ; i++ )
     {
      if ( s_gammatable[i] < s_gammatable[i-1] )
      {
       s_gammatable[i] = s_gammatable[i-1];
      }
     }

     for(i=0;i<256;++i) {
      GammaRamp.red[i] = s_gammatable[i];
      GammaRamp.green[i] = s_gammatable[i];
      GammaRamp.blue[i] = s_gammatable[i];
     }

     // 下面这种相对不够细腻,只相当于 overbright
     //int Overbrighten = 2;
     //int val;

     //for(i=0;i<256;++i) {
     // val = 256 * min( 255, i*Overbrighten );
     // GammaRamp.red[i] = val;
     // GammaRamp.green[i] = val;
     // GammaRamp.blue[i] = val;
     //}


     pd3dDevice->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &GammaRamp );
     Sleep( 200 ); //fix occasional gamma problem (Gamma won't change when told to)
     pd3dDevice->SetGammaRamp( 0, D3DSGR_NO_CALIBRATION, &GammaRamp );

    还有对比度:需要调整的是载入的图片的象素值。
    每次退出要恢复成 gamma 线性值:即
        for (int g = 0; g < 255; g++ )
        {
         s_oldHardwareGamma[0][g] = g << 8;
         s_oldHardwareGamma[1][g] = g << 8;
         s_oldHardwareGamma[2][g] = g << 8;
        }
    然后设置 gamma,因为程序可能非正常退出如:崩溃,或者调试退出.恢复gamma函数没有调用,几次之后 开始读取得gamma变成了上次非正常退出后调整过的值.所以恢复成线性值才能恢复到最原始的状态

    I'm sure you've all seen gamma correction before, but you might not know exactly how it works or why it is necessary. In this article, I'll try to explain both, as well as provide practical information on how to implement gamma correction in your apps.

    Intuitively, you might think of gamma correction as some sort of image brightness adjustment. That's not entirely wrong, but to understand why gamma correction is necessary, you need to look a little further. More precisely, you'll need to look inside your computer's display.

    Chances are that your monitor is based on a cathode ray tube (CRT). A CRT uses an electron beam to make the phosphor coating inside the tube emit light. The brightness of a pixel is changed by varying the voltage of the electron beam as it touches that pixel. Unfortunately, the relation between CRT voltage and pixel brightness isn't linear. It looks something more like this:

     

    This non-linearity effect is called gamma. Unfortunately, it causes the image displayed on your computer screen to be different from the image that is actually in memory. What you really want to see is a nice linear mapping between voltage and brightness. In other words: the graph above should be a straight line.

    That's why you should perform gamma correction. Gamma correction is performed by remapping the colors in the image as such: 

    The gamma-corrected color ramp can be calculated by raising the original color values to some power:

    The exponent to use is the so-called "gamma value", which you can see in paint programs that support gamma correction. The gamma value should be user-configurable, of course.

    You've probably already noticed: by using the gamma-corrected color ramp, you can cancel out the gamma effect of your monitor, resulting in a correct display of the image:

    This leaves one thing to be discussed: how to implement gamma correction. If you're just working with bitmap images, you can perform it manually for each pixel. This being an OpenGL site and all, I suspect that this isn't really what you want to do. Luckily, you can ask your graphics card to perform the correction for you (in real-time, of course). To this effect, you can use the GetDeviceGammaRamp() and SetDeviceGammaRamp() API calls. I've written a small OpenGL demo that illustrates the use of these functions. Download it here.

    That's all there's to it! Gamma correction is easy enough to understand and implement - and especially for games with dark environments, it can make a huge difference. Gamma correction will allow users to adjust the image brightness to suit their monitor, allowing them to see your game or application as you intended them to see it.

    用MFC 对话框写了一个调整 Gamma 的程序,
    没有使用directx 函数,替代的是操作系统的函数。下载地址
    http://www.91files.com/?2LKXQ8ZGOX9C8MEKN34X
    参考网址:
    http://www.cameraunion.net/forum/showthread.php?threadid=179245
    http://bbs.photoshopcn.com/archiver/tid-376356.html
    http://www.delphi3d.net/articles/viewarticle.php?article=gamma.htm

    October 23

    引用 OGRE + CEGUI :强大的用户界面

    CEGUI ,全称 "Crazy Eddie's GUI System" ,是一个专门的用户界面库,开源并且免费,它支持 DirectX8 、 DirectX9 ,除了可以作为 OGRE 的界面外挂,还支持另一个免费开源的 3D 引擎 Irrlicht 。由于它功能的相对强大和灵活, OGRE 的开发团队一直在推荐 OGRE 用户使用这个 CEGUI 来开发用户界面,逐渐抛弃 OGRE 本身过于简陋的 GUI 插件。尤其是在行将到来的新版本 OGRE 1.5 的声明中特别强调了这一点,尽管这个版本仍然暂时保留内置 GUI 系统,但 OGRE 1.5 将会是最后一个保留内置 GUI 的版本。 OGRE 看来似乎将专注于向一个纯粹的、然而富于协作和扩展性的图形引擎发展,这应该得益于它的庞大的社群支持,使得很多事情可以通过外挂一些更专业的引擎来实现,物理引擎使用 ODE 、 Tokamak 、 NovodeX ,网络引擎使用 openTNL 、 RakNet 、 eNet ,声音引擎使用 FMod 、 OpenAL ,以及这个界面引擎,使用 CEGUI 。外挂现成模块的好处就是可以专注于一个方面,开发一个五脏俱全的游戏引擎并不是个容易的事情,市面上最负盛名的几个商业引擎的开发, Unreal 、Renderware 、 Lithtech ,往往要耗费数百人年,并且在这些商业引擎中同样会使用外挂的商业库,在这个年代,没有人可以从头创建一切。

    CEGUI 基于 Unicode ,所以它支持中文字体的显示, 但是对中文字符的输入现在尚未解决,因为不断有来自中国的 OGRE + CEGUI 用户的质询和申请,对中文输入的支持可能已在考虑中。

    CEGUI 的官方主页在这里:
    Crazy Eddie's GUI System


    1. CEGUI 的 CVS 下载
    使用 WinCVS ,使用方法参考 OGRE 一起学 (1) —— 获取 OGRE

    (1) 在 WinCVS 主界面左栏选中 "cvshome" ;

    (2) 从主菜单选择 [Admin]-[Command Line...] ,弹出 "Command line settings" 面板,在 "Settings" 选项卡的上部输入框里,输入如下指令进行登陆:
    cvs -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/crayzedsgui login
    当弹出对话框问讯密码时直接按回车。

    (3) 成功登陆以后, WinCVS 主界面的下部信息栏会显示这样一条信息:

    ***** CVS exited normally with code 0 *****

    code 0 就表示登陆成功,如果不成功,显示为 code 1 。

    (4) 登陆成功以后,再次打开 "Command line settings" 面板,输入并运行如下指令:

    cvs -z3 -d:pserver:anonymous@cvs.sourceforge.net:/cvsroot/crayzedsgui co cegui_mk2

    CVS 源代码就开始下载了。耐心等它下载完毕。

    (5) 这时候在 WinCVS 主界面左栏的 “cvshome” 下边,多了一个 "cegui_mk2" 的文件夹。你可以到硬盘的 F:\cvshome\cegui_mk2\ 看一下,所有的源代码都在那里了。


    2. CEGUI 本体 lib 和 dll 的编译:
    最好使用 VS.Net 2003 的 VC7.1 来编译,它对 OGRE 和 CEGUI 的支持都很好。 VC7.0 和 VC7.1 都可以。

    (1) 首先要下载编译 CEGUI 所需的依赖项,包括头文件和库,下载页在这里:
    http://crayzedsgui.sourceforge.net/modules.php?name=Downloads&d_op=viewdownload&cid=6
    首先下载 CEGUI Mk-2 Win32 Dependencies: Common Files ,这些是公用的头文件;
    其次根据你的编译器版本选择对应的库,例如我使用 VC.Net 2003 ,就选择下载 CEGUI Mk-2 Win32 Dependencies: Libs for MSVC 7.1
    下载以后分别解压缩,把头文件 cegui_mk2-deps-common\dependencies\include 和库文件cegui_mk2-deps-vc7.1\dependencies\lib 这两个目录全部复制到 cvshome\cegui_mk2\dependencies 目录下。

    (2) 现在设置工作环境。打开 VS.NET 或 VS.NET 2003 ,在主菜单中选择 [工具]-[选项...] ,打开 "选项" 面板,在左边目录选择 [项目]-[VC++ 目录] ,在右栏的 Include 和 Lib 目录下添加以下路径:

    包含文件:
    X:\cvshome\cegui_mk2\dependencies\include
    库文件:
    X:\cvshome\cegui_mk2\dependencies\lib
    注意把这几条目录放在目录列表的最上面。

    (3) OK. 现在打开 cvshome\cegui_mk2\makefiles\win32\VC++7.1\CEGUI.sln ,从主菜单中选择 [生成]-[批生成...] ,打开 "批生成" 面板,点击右边的 [全选] 按钮,然后点击 [重新生成] 按钮开始生成,静待生成结束。

    编译生成的 *.dll 文件在 cvshome\cegui_mk2\bin 目录下, *.lib 文件在 cvshome\cegui_mk2\lib 目录下。

    编译成功以后,为了编译以后的 CEGUI 相关项目,我们要在 VS.Net 的工作环境中添加 CEGUI 的 include 和 lib 目录:

    包含文件:
    X:\cvshome\cegui_mk2\include
    库文件:
    X:\cvshome\cegui_mk2\lib


    3. 编译 CEGUI 示例

    (1) 首先来这里下载几个 CEGUI 示例:
    http://crayzedsgui.sourceforge.net/modules.php?name=Downloads&d_op=viewdownload&cid=5
    其中的 CEGUI under Ogre - Demo 4 和 CEGUI under Ogre - Demo 7 是带有源文件的,我们挑选 CEGUI under Ogre - Demo 4 来尝试编译。

    (2) Demo 下载以后解压缩,文件目录不要乱放,要放到 cvshome 目录下。
    然后双击 cvshome\ogre_gui_demo4\CEGUIOgre_TestDriver1.sln 打开解决方案。
    由于 CEGUI 作者的疏忽, Release 的项目设置里有几个多余的 lua.lib ,会导致编译时因找不到这些库而失败,所以我们需要删除这几个库。打开 CEGUIOgre_Demo4 的项目属性,删除 Release 配置属性的 [链接器]-[输入]-[附加依赖项] 条目中的 liblua.lib liblualib.lib libluabind.lib 这几个库。

    (3) 然后生成。
    生成的 *.exe 文件在 cvshome\ogre_gui_demo4 的 Release 和 Debug 目录下。但是你现在就打开运行肯定会失败。

    失败的原因首先是因为这个 Demo 使用的是最新版本的 OGRE 的头文件和库来编译,但是 Release 和 Debug 目录下放的是老版本的 *.dll ,所以你需要把 cvshome\ogrenew\Samples\Common\bin 的 Release 和 Debug 目录下的所有 *.dll 都复制到 cvshome\ogre_gui_demo4 的 Release 和 Debug 目录下,用你的最新版本覆盖旧版本,这是一个;

    然后, CEGUI 的依赖项中需要一个 *.dll ,在 cvshome\cegui_mk2\dependencies\lib 中有两个 *.dll ,把 xerces-c_2_5_0.dll 复制到 cvshome\ogre_gui_demo4\Release ,把 xerces-c_2_5_0D.dll 复制到 cvshome\ogre_gui_demo4\Debug 。

    还有 CEGUI 本体的 *.dll ,在 cvshome\cegui_mk2\bin 目录下,也要全部复制过来。 *.dll 文件名带 _d 的是 Debug 版本。

    OK. 现在示例可以运行了。
    http://www.yanchen.com/attachments/month_0410/olds_Demo7_shot.jpg">


    作为对 CEGUI 的一个应用实例,你可以下载下面这个 OGRE 粒子编辑器来体验一下:
    ParticleEditor_AlphaDemo



    October 22

    DXUT 框架入门 2

      这章主要介绍一下DXUT 里面的GUI元素。要在图形界面中添加GUI元素,首先要定义一个DialogResourceManager对象用来管理对话框资源。DialogResourceManager 管理渲染时状态、Sprite控制批量显示更新、对话框字体、纹理等等。CDXUTDialog 相当于MFC里面的对话框,作为各种控件资源的容器。CD3DSettingsDlg 是一个ms已经写好的对话框类,可以用来设置各种Direct3DDevice9 创建时的参数。点击该对话框的ok 按钮,D3D设备将会重建。
      这里先建立了DialogResourceManager全局变量g_DialogResourceManager和CD3DSettingsDlg 全局变量g_SettingsDlg。并且要在 OnCreateDevice OnResetDevice MsgProc OnLostDevice OnDestroyDevice 回调函数中调用自己相应的函数如g_DialogResourceManager.OnCreateDevice(...) 等等。
      对于对话框对象使用前必须初始化 init() 参数为DialogResourceManager类对象,即g_DialogResourceManager.之后对于CDXUTDialog类对象g_HUD需要设置自己的消息回调函数 OnGUIEvent()。并且在 dxut 的消息处理函数MsgProc中调用自己的消息处理函数 g_HUD->MsgProc(), 如果是该对话框的消息,Dxut回调函数将不再处理这个消息。而交由对话框处理。
    //使用DXUT 框架GUI程序
    #include <dxstdafx.h>
     
    //自定义顶点结构
    struct CUSTOMVERTEX
    {
     float x,y,z,rhw;
     DWORD diffuse;
    };

    ID3DXFont*     g_pFont = NULL;     // Font for drawing text
    ID3DXSprite*    g_pTextSprite = NULL;   // Sprite for batching draw text calls
    bool      g_bShowHelp = true;    // If true, it renders the UI control text
    CDXUTDialogResourceManager g_DialogResourceManager;  // manager for shared resources of dialogs
    CD3DSettingsDlg    g_SettingsDlg;     // Device settings dialog
    CDXUTDialog     g_HUD;       // dialog for standard controls
    static const int   D3DFVF_CUSTOMVERTEX = D3DFVF_XYZRHW | D3DFVF_DIFFUSE;
    LPDIRECT3DVERTEXBUFFER9  g_pVB;
    bool      g_vsb = true;
     
    //定义g_HUD对话框上按钮ID
    static const int IDC_TOGGLEFULLSCREEN=1; 
    static const int IDC_TOGGLEREF=2;
    static const int IDC_CHANGEDEVICE=3;

    HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
    HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext );
    void    CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
    void    CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext );
    LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext );
    void    CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext );
    void    CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext );
    void    CALLBACK OnLostDevice( void* pUserContext );
    void    CALLBACK OnDestroyDevice( void* pUserContext );
    void    InitApp();
    void    RenderText();

    INT WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int )
    {
     //为DEBUG模式激活运行时内存检查
    #if defined(DEBUG) | defined(_DEBUG)
     _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
    #endif
     // 设置回调函数,这些函数允许DXUT通知应用程序更换设备,用户输入和窗口消息。
     // 回调函数是可选的,因此你要做的仅是设置你感兴趣的事件的回调函数。
     DXUTSetCallbackDeviceCreated( OnCreateDevice );
     DXUTSetCallbackDeviceReset( OnResetDevice );
     DXUTSetCallbackDeviceLost( OnLostDevice );
     DXUTSetCallbackDeviceDestroyed( OnDestroyDevice );
     DXUTSetCallbackFrameRender( OnFrameRender );
     DXUTSetCallbackFrameMove( OnFrameMove );
     DXUTSetCallbackMsgProc( MsgProc );
     DXUTSetCallbackKeyboard( KeyboardProc );
     // 全屏时显示鼠标
     DXUTSetCursorSettings( true, true );
     //InitApp();
     // 初始化DXUT并创建想要的Win32窗口和应用程序的Direct3D设备。调用这些
     // 可选函数中的每一个,此外它们允许你设置几个选项来控制框架的行为。
     DXUTInit( TRUE, TRUE, TRUE );
     DXUTCreateWindow( L"ameng" );
     DXUTCreateDevice( D3DADAPTER_DEFAULT, TRUE, 640, 480 );
     // 通过DXUT来处理消息循环并分派渲染调用。当在空闲时间和处理窗口消息的
     // 时间间隔时,框架将调用OnFrameMove和OnFrameRender回调函数。
     DXUTMainLoop();
     return DXUTGetExitCode();
    }
    void InitApp()
    {
     // 初始化对话框
     g_SettingsDlg.Init(&g_DialogResourceManager );
     g_HUD.Init(&g_DialogResourceManager );
     //设置对话框消息处理函数。
     g_HUD.SetCallback( OnGUIEvent );
     int iY = 10;
     g_HUD.AddButton( IDC_TOGGLEFULLSCREEN, L"Toggle full screen", 35, iY, 125, 22 );
     g_HUD.AddButton( IDC_TOGGLEREF, L"Toggle REF (F3)", 35, iY += 24, 125, 22,VK_F3 );
     g_HUD.AddButton( IDC_CHANGEDEVICE, L"Change device (F2)", 35, iY += 24, 125, 22, VK_F2 );
    }
     

    HRESULT CALLBACK OnCreateDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {
     HRESULT hr;
     V_RETURN( g_DialogResourceManager.OnCreateDevice( pd3dDevice ) );
      InitApp();
     V_RETURN( g_SettingsDlg.OnCreateDevice( pd3dDevice ) );
     // Initialize the font
     V_RETURN( D3DXCreateFont( pd3dDevice, 15, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET,
      OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE,
      L"Arial", &g_pFont ) );
     return S_OK;
    }
    HRESULT CALLBACK OnResetDevice( IDirect3DDevice9* pd3dDevice, const D3DSURFACE_DESC* pBackBufferSurfaceDesc, void* pUserContext )
    {
     HRESULT hr;
     static CUSTOMVERTEX vertices[] =
     {
      { 300.0f,  50.0f, 0.1f, 1.0f, D3DCOLOR_ARGB(255,255,0,0) }, // x, y, z, rhw, color
      { 500.0f, 350.0f, 0.1f, 1.0f, D3DCOLOR_ARGB(255,0,255,0) },
      { 100.0f, 350.0f, 0.1f, 1.0f, D3DCOLOR_ARGB(255,0,0,255) },
     };
     pd3dDevice->CreateVertexBuffer(3*sizeof(CUSTOMVERTEX),0,D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT,&g_pVB,NULL);
     BYTE* pVertices;
     if( FAILED( g_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 )))
      return E_FAIL;
     memcpy(pVertices,vertices,sizeof(vertices));
     g_pVB->Unlock();
     V_RETURN( g_DialogResourceManager.OnResetDevice() );
     V_RETURN( g_SettingsDlg.OnResetDevice() );
     if( g_pFont )
      V_RETURN( g_pFont->OnResetDevice() );
     // Create a sprite to help batch calls when drawing many lines of text
     V_RETURN( D3DXCreateSprite( pd3dDevice, &g_pTextSprite ) );
     g_HUD.SetLocation( pBackBufferSurfaceDesc->Width-170, 0 );
     g_HUD.SetSize( 170, 170 );
     return S_OK;
    }
    void CALLBACK OnFrameMove( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
    {

    }
    void CALLBACK OnFrameRender( IDirect3DDevice9* pd3dDevice, double fTime, float fElapsedTime, void* pUserContext )
    {
     HRESULT hr;
     V( pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_ARGB(0, 0, 0, 0), 1.0f, 0) );
     V(pd3dDevice->SetStreamSource(0,g_pVB,0,sizeof(CUSTOMVERTEX)));
     V(pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX));
     if( g_SettingsDlg.IsActive() )
     {
      g_SettingsDlg.OnRender( fElapsedTime );
      return;
     }
     if(SUCCEEDED(pd3dDevice->BeginScene()))
     {
      //更新图像
      V(pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,1));
      if(g_vsb)
      {
       RenderText();
      }
      V( g_HUD.OnRender( fElapsedTime ) );
    //  V( g_SampleUI.OnRender( fElapsedTime ) );
      pd3dDevice->EndScene();
     }
    }
    void RenderText()
    {
     // The helper object simply helps keep track of text position, and color
     // and then it calls pFont->DrawText( m_pSprite, strMsg, -1, &rc, DT_NOCLIP, m_clr );
     // If NULL is passed in as the sprite object, then it will work however the
     // pFont->DrawText() will not be batched together.  Batching calls will improves performance.
     CDXUTTextHelper txtHelper( g_pFont, g_pTextSprite, 15 );
     // Output statistics
     txtHelper.Begin();
     txtHelper.SetInsertionPos( 5, 5 );
     txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ) );
     txtHelper.DrawTextLine( DXUTGetFrameStats(true) ); //为true 显现FPS,默认为false不显使FPS
     txtHelper.DrawTextLine( DXUTGetDeviceStats() );

     
     //TODO: add UI text as needed
     txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
     txtHelper.DrawTextLine( L"Put whatever misc status here" );

     // 获取图象后备缓冲区
     const D3DSURFACE_DESC* pd3dsdBackBuffer = DXUTGetBackBufferSurfaceDesc();
     if( g_bShowHelp )
     {
      txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*6 );
      txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 0.75f, 0.0f, 1.0f ) );
      txtHelper.DrawTextLine( L"Controls (F1 to hide):" );
      txtHelper.SetInsertionPos( 40, pd3dsdBackBuffer->Height-15*5 );
      txtHelper.DrawTextLine( L"Quit: ESC" );
     }
     else
     {
      //txtHelper.SetInsertionPos( 10, pd3dsdBackBuffer->Height-15*2 );
      txtHelper.SetForegroundColor( D3DXCOLOR( 1.0f, 1.0f, 1.0f, 1.0f ) );
      txtHelper.DrawTextLine( L"Press F1 for help" );
     }
     
     txtHelper.End();
    }

    //--------------------------------------------------------------------------------------
    // Before handling window messages, DXUT passes incoming windows
    // messages to the application through this callback function. If the application sets
    // *pbNoFurtherProcessing to TRUE, then DXUT will not process this message.
    //--------------------------------------------------------------------------------------
    LRESULT CALLBACK MsgProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool* pbNoFurtherProcessing, void* pUserContext )
    {
     // Always allow dialog resource manager calls to handle global messages
     // so GUI state is updated correctly
     *pbNoFurtherProcessing = g_DialogResourceManager.MsgProc( hWnd, uMsg, wParam, lParam );
     if( *pbNoFurtherProcessing )
      return 0;
     if( g_SettingsDlg.IsActive() )
     {
      g_SettingsDlg.MsgProc( hWnd, uMsg, wParam, lParam );
      return 0;
     }
     // Give the dialogs a chance to handle the message first
     *pbNoFurtherProcessing = g_HUD.MsgProc( hWnd, uMsg, wParam, lParam );
     if( *pbNoFurtherProcessing )
      return 0;
     return 0;
    }

    //--------------------------------------------------------------------------------------
    // As a convenience, DXUT inspects the incoming windows messages for
    // keystroke messages and decodes the message parameters to pass relevant keyboard
    // messages to the application.  The framework does not remove the underlying keystroke
    // messages, which are still passed to the application's MsgProc callback.
    //--------------------------------------------------------------------------------------
    void CALLBACK KeyboardProc( UINT nChar, bool bKeyDown, bool bAltDown, void* pUserContext )
    {
     if( bKeyDown )
     {
      switch( nChar )
      {
       case VK_F1:
        g_bShowHelp = !g_bShowHelp;
        break;
       case VK_F4:
        g_HUD.SetVisible(!g_HUD.GetVisible());
        g_vsb = !g_vsb;
        break;
       default:
        break;
      }
     }
    }

    //--------------------------------------------------------------------------------------
    // Handles the GUI events
    //--------------------------------------------------------------------------------------
    void CALLBACK OnGUIEvent( UINT nEvent, int nControlID, CDXUTControl* pControl, void* pUserContext )
    {
     switch( nControlID )
     {
      case IDC_TOGGLEFULLSCREEN:
       DXUTToggleFullScreen();
       break;
      case IDC_TOGGLEREF:       
       DXUTToggleREF();
       break;
      case IDC_CHANGEDEVICE:    
       g_SettingsDlg.SetActive( !g_SettingsDlg.IsActive() );
       break;
     }
    }
    void CALLBACK OnLostDevice( void* pUserContext )
    {
     if(g_pVB != NULL)
     {
      g_pVB->Release();
      g_pVB = NULL;
     }
     g_DialogResourceManager.OnLostDevice();
     g_SettingsDlg.OnLostDevice();
     if( g_pFont )
      g_pFont->OnLostDevice();
     
     SAFE_RELEASE( g_pTextSprite );
    }
    void CALLBACK OnDestroyDevice( void* pUserContext )
    {
     g_DialogResourceManager.OnDestroyDevice();
     g_SettingsDlg.OnDestroyDevice();
     SAFE_RELEASE( g_pFont );
    }