实时阴影绘制技术研究

C++博客 首页 新随笔 联系 聚合 管理
  48 Posts :: 20 Stories :: 57 Comments :: 0 Trackbacks
让人很不爽的前两天装的directX9(decemeber)版和我看的这本《Direct3D游戏编程入门教程》有版本冲突。书里边使用了 direct3D自己带的框架,而这个框架和我装的这个directX9是不一样的,听说这个版本包含了许多directX10的东西,可能是这个原因吧。
我反复想是用新的框架还是装回较早的版本,看了directX自带的tutorail和文档是英文的,不利于我速成。要是用原来的版本呢,可能换回来还需要一些代价。想来想去最后还是把原来那个卸了,装回原来的版本。先速成再说。
好,下面进入第5章的第一个例子-Basic。
 
  • CD3DApplication类

这个是d3d通用框架中的应用程序基类,在WinMain()里边声明一个CD3DApplication对象,然后调用其Run()方法程序就跑起来了。简单代码如下:

INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )
{
    CMyD3DApplication d3dApp;

    InitCommonControls();
    if( FAILED( d3dApp.Create( hInst ) ) )
        return 0;

    return d3dApp.Run();
}

我们再来看一下CMyD3DApplication的声明

class CMyD3DApplication : public CD3DApplication
{
 LPDIRECT3DVERTEXBUFFER9 m_pVB;  //vertice buffer的指针 
    DWORD m_dwSizeofVertices;    //顶点数

protected:
    HRESULT OneTimeSceneInit();
    HRESULT InitDeviceObjects();
    HRESULT RestoreDeviceObjects();
    HRESULT InvalidateDeviceObjects();
    HRESULT DeleteDeviceObjects();
    HRESULT FinalCleanup();
    HRESULT Render();
    HRESULT FrameMove();
    HRESULT ConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior,
  D3DFORMAT adapterFormat, D3DFORMAT backBufferFormat );

public:
    CMyD3DApplication();
};

也很简单,两个成员变量的含义已经注释出来。需要说明一下的就是该程序使用vertex buffer来处理顶点,也就是直接把顶点复制到显卡可以直接访问到的驱动程序缓冲中,这样就不必每次复制顶点,提高了效率。下面介绍一下该类的方法的调用:

  1. 程序启动的执行顺序是:ConfirmDevice()->OneTimeSceneInit()->InitDeviceObject()->RestoreDeviceObjects().
  2. 程序运行期间会执行一个循环:FrameMove()->Render()
  3. 如果运行期间改变窗口大小,框架会调用:InvalidateDeviceObjects()->RestoreDeviceObjects();
  4. 如果通过F2或者菜单来更改设备(HAL或REF),调用:InvalidateDeviceObjects()-> DeleteDeviceObjects()->InitDeviceObjects()->RestoreDeviceObjects();
  5. 程序退出时执行:InvalidateDeviceObjects()->DeleteDeviceObjects()->FinalCleanUp().

下面说一下具体函数的含义:

  1. ConfirmDevice():最先被执行,用来检查显卡能力,如果显卡不支持,框架会切换到使用参考光栅器,或切换为顶点软件处理。具体参数见教材。
  2. OneTimeSceneInit():一次性永久初始化,初始化与设备无关的东西,与FinalCleanUp()搭配,初始化的数据可以在FinalCleanUp()中销毁。
  3. InitDeviceObjects():初始化与设备相关的对象,如果通过F2使设备发生改变,那么就需要重新执行该初始化。与DeleteDeviceObjects()搭配。
  4. InvalidateDeviceObjects():对于窗口的大小改变做出响应。
  5. RestoreDeviceObjects():和InvalidateDeviceObjects()搭配,可以在窗口大小改变时重新设置投影矩阵和渲染状态等等。除了在程序开始时以为,都会和InvalidateDeviceObjects()成对调用。
  6. DeleteDeviceObjects():在设备改变时对设备资源进行销毁。
  7. Render():作为3D渲染的入口点在每一帧时被调用,可以设置render state,clear buffer,渲染场景。
  8. FrameMove():容纳所有动画的代码。
  9. FinalCleanUp():销毁几何数据和文件对象等等非设备资源。

OK,了解了执行过程,下面看一下重要步骤。

在Basic例子程,程序较为简单,绘制了一个无须任何坐标变换和投影的四边形,并用 m_d3dEnumeration.AppUsesDepthBuffer = FALSE;设置了不使用深度缓存。这里边重要的方法就是RestoreDeviceObjects()中创建顶点缓冲区:

m_pd3dDevice->CreateVertexBuffer( m_dwSizeofVertices, D3DUSAGE_WRITEONLY, FVF,D3DPOOL_MANAGED,&m_pVB, NULL )

这里m_dwSizeofVertices是顶点数,m_pVB是缓冲区指针,比较好理解。FVF表示每个顶点的表示形式,定义成const DWORD FVF = (D3DFVF_XYZRHW | D3DFVF_DIFFUSE);表示每个顶点由(x,y,z,rhw,diffuse)构成,direct3D的顶点表示还有specular, normal,纹理等信息,分别对应不同的变量组合。

D3DPOOL_MANAGED是指定了D3D的资源管理器,负责对于纹理和几何数据实施统一管理。D3D的资源管理器分成五种:

  1. D3DPOOL_DEFAULT:资源存储在AGP或显存中,在顶点缓冲或索引缓冲经常更新的情况下使用,避免内存到显存的拷贝“过慢”。对于永远不变的纹理,用默认池就是一种好的选择。
  2. D3DPOOL_MANAGED:托管池,资源存储在AGP或者显存中,并在系统内存中有一份拷贝。只有内存发生变化才需要拷贝到显存,如果性能不是最重要的,托管池永远是一种最安全的选择。
  3. D3DPOOL_SYSTEMMEN:系统池,存储在系统内存中,设备丢失后(在全屏和窗口模式切换时或者改变分辨率)资源不需要重建。
  4. D3DPOOL_SCRATCH:临时池,资源存储在系统内存中,且设备丢失时无法重建,连同CreateOffScreenPlainSurface()一起使用,用于创建图像表面。
  5. D3DPOOL_FORCE_DWORD:

创建顶点缓冲器之后,是Lock()顶点缓冲器,然后执行memcpy()拷贝顶点,之后UnLock().这里Lock()包含四个参数, offsetToLock是锁定顶点缓冲区从哪个偏移开始,SizeToLock是锁定的大小,ppbData是返回指向内存缓冲区的指针,最后一个是指定如何使用顶点缓冲器.

最后看一下Render()方法:

HRESULT CMyD3DApplication::Render()
{
    // Begin the scene
    if( SUCCEEDED( m_pd3dDevice->BeginScene() ) )
    {
  // In DX8: m_pd3dDevice->SetVertexShader( D3DFVF_CUSTOMVERTEX );
  // Passing an FVF to IDirect3DDevice9::SetFVF specifies a legacy FVF with stream 0.
     m_pd3dDevice->SetFVF(FVF ); // new in DX9
  // DX8: only three parameters. In DX9 the third parameter is new
  m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof(VERTEX) );
     m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );

      // End the scene.
      m_pd3dDevice->EndScene();
    }

    return S_OK;
}

Render()方法中首先使用m_pd3dDevice初始化一个场景 BeginScene(),使场景处于处理过程中,这样后边的绘制才会奏效,否则都会返回D3DERR_SCENE_NOT_IN_SCENE的错误。结束绘制以后再调用EndScene()清除标识场景处于处理过程的标志,刷新缓存数据以及确认渲染表面完好。

SetFVF()只有一个参数,就是顶点的结构,该方法仅用于固定流水线,对于使用shader的情况要调用SetVertexShader().

SetStreamSource()设置了数据流,DrawPrimitive()把数据渲染出来。

over!

原文地址:http://blog.sina.com.cn/u/40d00f170100012z

posted on 2005-12-23 00:24 苦行僧 阅读(1205) 评论(0)  编辑 收藏 引用 所属分类: directX

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理