以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- 网格模型高级技术(3) (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=70890) |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:07:00 AM -- 网格模型高级技术(3) 类CDXUTMeshFrame的详细说明请参阅DXUT源码分析 ---- 类CDXUTMeshFrame。 类CDXUTMeshFile的详细说明请参阅DXUT源码分析 ---- 类CDXUTMeshFile。
使用类CDXUTMeshFile进行网格模型的绘制 首先在OnCreateDevice()里创建CDXUTMeshFile对象: g_dxut_mesh_file = new CDXUTMeshFile(); 接着在OnFrameRender()里渲染网格模型: // Render the sceneif( SUCCEEDED( pd3dDevice->BeginScene() ) ){ g_dxut_mesh_file->Render(pd3dDevice, &g_mat_world); RenderText(); V(g_button_dlg.OnRender(fElapsedTime)); g_dxut_mesh_file->Destroy();
示例截图:
主程序: #pragma warning(disable : 4127 4995) #define IDC_TOGGLE_FULLSCREEN 1 #define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0) ID3DXFont* g_font; CDXUTDialogResourceManager g_dlg_resource_manager; CDXUTMeshFile* g_dxut_mesh_file; //-------------------------------------------------------------------------------------- IDirect3D9* pD3D = DXUTGetD3DObject(); if( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, return true; static bool is_first_time = true; if(is_first_time) // if using reference device, then pop a warning message box. return true; //-------------------------------------------------------------------------------------- LPWSTR w_last_back_slash = wcsrchr(wbuf, '\\'); if(w_last_back_slash) V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice)); D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, return S_OK; V_RETURN(g_dlg_resource_manager.OnResetDevice()); // set dialog position and size g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0); // setup view matrix D3DXMATRIX mat_view; D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up); // set projection matrix // setup light light.Type = D3DLIGHT_DIRECTIONAL; D3DXVECTOR3 light_dir(0.0f, -1.0f, 1.0f); pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x0080FFFF); return S_OK; //-------------------------------------------------------------------------------------- release_com(g_text_sprite); release_com(g_font); g_dxut_mesh_file->Destroy(); //-------------------------------------------------------------------------------------- //-------------------------------------------------------------------------------------- // show frame and device states // show helper information if(g_show_help) text_helper.End(); //-------------------------------------------------------------------------------------- if(g_settings_dlg.IsActive()) // Clear the render target and the zbuffer // Render the scene V( pd3dDevice->EndScene() ); if(g_settings_dlg.IsActive()) *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam); return 0; //-------------------------------------------------------------------------------------- case IDC_TOGGLE_REF: case IDC_CHANGE_DEVICE: //-------------------------------------------------------------------------------------- g_button_dlg.SetCallback(OnGUIEvent); int x = 35, y = 10, width = 125, height = 22; g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y, width, height); //-------------------------------------------------------------------------------------- // Set the callback functions // Initialize DXUT and create the desired Win32 window and Direct3D device for the application // Start the render loop // TODO: Perform any application-level cleanup here return DXUTGetExitCode();
|
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:08:00 AM -- 网格模型动画一般有两种:一种是渐变动画;另一种是骨骼动画,这种动画包含在网格模型之中,通过网格模型不同部分之间的相对运动来实现动画。
骨骼动画基本原理 骨骼动画是目前最流行也最复杂的角色动画,它包含以下几个重要元素:骨骼、动画。骨骼动画思想的起源很简单,自然界中的大多数动物都拥有一套骨骼,身体的皮毛血肉都依附于骨骼,当骨骼开始运动的时候,依附于对应骨骼的皮毛血肉都随骨骼一起运动。在三维图形编程领域,角色的躯体是由网格模型来表示的,网格模型通常由大量三角形图元组成,而三角形又是由顶点组成的。为了模仿现实世界中角色自身的动作,就需要为角色网格模型添加一套骨骼,同时需要确定哪些顶点依附于哪块骨骼,这样当骨骼运动时就能牵引依附骨骼的顶点一起运动,这就是骨骼动画的基本原理。 骨骼动画模型的骨骼是以树状层次结构组织起来的,整个骨骼结构中有一块根骨骼,其他的骨骼都直接或间接连接到根骨骼上,形成角色模型的整个骨骼框架。 一般每块骨骼都带有两个矩阵,一个是初始变换矩阵(LocalTransformMatrix),表示骨骼的初始位置;另一个是组合变换矩阵(CombinedTransformMaitrx),用于对骨骼进行各种变换,从而实现角色动画。在每次渲染角色模型前,需要更新整个骨骼层次结构,组合每个连续的变换,将上层骨骼的运动传递到下层骨骼,这个原理可表示为: (子骨骼的)CombinedTransformMaitrx = (子骨骼的)LocalTransformMaitrx x (父骨骼的)CombinedTransformMaitrx 骨骼的组合变换矩阵是随动画的播放不断变化的,而它的初始变换矩阵一般是不改变的,正是所有骨骼的这些矩阵相互作用才牵引着顶点的变化,从而实现了骨骼动画。因为一次变换只能将骨骼变换到一个特定位置,要形成连续的动画就需要一帧一帧地连续改变骨骼的位置,每次改变骨骼的位置都需要一个骨骼变换矩阵,在网格模型中不可能保存任意时刻骨骼的变换矩阵,通常是保存关键时间点骨骼的变换矩阵(即关键桢),然后在播放角色动画时,根据播放时间进行插值得到任意时刻骨骼的变换矩阵,从而形成连续的角色动画。 骨骼动画是通过骨骼变换矩阵实现的,在网格模型中保存的也是关键时间点骨骼的变换矩阵,因此插值就是针对这些关键时间点上的骨骼变换矩阵进行的。假设在s1时刻骨骼变换矩阵是mat1,在s2时刻骨骼变换矩阵是mat2,在s1和s2之间的任意时刻s,其骨骼变换矩阵mat为: mat = (1-w) * mat1 + w * mat2 其中w是权值,通过这个权值来调节在s时刻骨骼变换矩阵中mat1和mat2所占的比重,对骨骼变换矩阵进行插值最简单的方法是线性插值,这时w = (s-s1) / (s2-s1)
骨骼动画类的设计与实现 我们需要将骨骼动画网格模型的相关操作封装到一组类和结构中,这一组类和结构可以看成一套完整的骨骼动画网格模型接口,它们之间的关系如下图所示: 其中,D3DXFRAME和D3DXMESHCONTAINER是Direct3D提供的两个结构,D3DXMESHCONTAINER结构用于保存模型的网格数据,D3DXFRAME用于保存模型的骨骼框架。结构D3DXMESHCONTAINER_DERIVED继承自Direct3D提供的结构D3DXMESHCONTAINER,结构D3DXFRAME_DERIVED继承自Direct3D提供的结构D3DXFRAME,分别进行了相应的扩充,使其能够保存所需要的其他数据。 cAllocateHierarchy类负责从动画网格模型文件加载各种数据,该类继承自Direct3D中的ID3DXAllocateHierarchy接口。 cAnimMesh类是唯一对外开放的类,它通过cAllocateHierarchy类的对象从模型文件中加载所需的数据,并负责处理骨骼动画信息以及网格模型的渲染。
继承并扩展结构体D3DXFRAME 为了在渲染网格模型的同时播放包含在网格模型的动画,需要处理两个单独的实体:骨骼结构(即框架结构)和网格模型。框架结构和网格模型的相关数据分别使用D3DXFRAME_DERIVED和D3DXMESHCONTAINER_DERIVED结构保存。需要指出的是.x文件中的一个网格模型可以由多个框架和多个网格组成,但具体到某一个框架时,它一般只有一个网格,当然它也可以有多个网格。 为了方便加载骨骼动画网格模型,Direct3D提供了两个重要的结构体:D3DXFRAME和D3DXMESHCONTAINER,其中D3DXFRAME用来加载框架,其定义如下: Encapsulates a transform frame in a transformation frame hierarchy. typedef struct D3DXFRAME { LPSTR Name; D3DXMATRIX TransformationMatrix; LPD3DXMESHCONTAINER pMeshContainer; D3DXFRAME * pFrameSibling; D3DXFRAME * pFrameFirstChild;} D3DXFRAME, *LPD3DXFRAME; 显然在实现动画网格模型的绘制前,不仅要得到每个框架的初始变换矩阵,同时还要得到从该框架的所有父节点到本级框架的组合变换矩阵,这是因为任何一个父框架的位置改变都会影响该框架自身位置的变化,所以在此将结构D3DXFRAME扩展为D3DXFRAME_DERIVED,在D3DXFRAME_DERIVED中添加一个成员变量CombinedTransformMaitrx,用TransformationMatrix记录在任何动画数据未加载前框架的初始变换矩阵,也就是该框架的初始位置,用CombinedTransformMaitrx来记录从所有的父框架到该框架自身所积累起来的组合变换矩阵,这样就将整个网格模型很方便地组织起来了。 结构D3DXFRAME_DERIVED的定义如下: struct D3DXFRAME_DERIVED : public D3DXFRAME{ D3DXMATRIX CombinedTransformMatrix;}; 继承并扩展结构体D3DXMESHCONTAINER 结构体D3DXMESHCONTAINER用来加载每个具体网格模型的数据,其定义如下: Encapsulates a mesh object in a transformation frame hierarchy. typedef struct D3DXMESHCONTAINER { LPSTR Name; D3DXMESHDATA MeshData; LPD3DXMATERIAL pMaterials; LPD3DXEFFECTINSTANCE pEffects; DWORD NumMaterials; DWORD * pAdjacency; LPD3DXSKININFO pSkinInfo; D3DXMESHCONTAINER * pNextMeshContainer;} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER; 结构体D3DXMESHCONTAINER中没有记录网格模型的纹理信息,所以将该结构体扩展为D3DXMESHCONTAINER_DERIVED,定义如下: struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER{ IDirect3DTexture9** ppTextures;}; |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:10:00 AM -- 为了方便加载.x文件中的框架和网格模型数据,Direct3D提供了一个ID3DXAllocateHierarchy接口,该接口中有4个纯虚函数:用来创建框架的CreateFrame(),创建网格容器的CreateMeshContainer(),销毁框架的DestroyFrame(),销毁网格容器的DestroyMeshContainer()。应用程序会在相应的时机自动调用这些对应的函数,以构建或者销毁对应的框架或网格模型。 This interface is implemented by the application to allocate or free frame and mesh container objects. Methods on this are called during loading and destroying frame hierarchies. Method Description
ID3DXAllocateHierarchy::CreateFrame HRESULT CreateFrame( LPCSTR Name, LPD3DXFRAME * ppNewFrame);
ID3DXAllocateHierarchy::CreateMeshContainer HRESULT CreateMeshContainer( LPCSTR Name, CONST D3DXMESHDATA * pMeshData, CONST D3DXMATERIAL * pMaterials, CONST D3DXEFFECTINSTANCE * pEffectInstances, DWORD NumMaterials, CONST DWORD * pAdjacency, LPD3DXSKININFO pSkinInfo, LPD3DXMESHCONTAINER * ppNewMeshContainer);
ID3DXAllocateHierarchy::DestroyFrame HRESULT DestroyFrame( LPD3DXFRAME pFrameToFree);
ID3DXAllocateHierarchy::DestroyMeshContainer HRESULT DestroyMeshContainer( LPD3DXMESHCONTAINER pMeshContainerToFree);
cAllocateHierarchy类继承自ID3DXAllocateHierarchy接口,在cAllocateHierarchy类需要重载这4个纯虚函数以实现动画网格模型数据的加载和释放。 该类的定义如下: class cAllocateHierarchy : public ID3DXAllocateHierarchy{private: HRESULT AllocateName(LPCSTR name, LPSTR* ret_name); HRESULT cAllocateHierarchy::AllocateName(LPCSTR name, LPSTR* ret_name){ if(name != NULL) { UINT length = (UINT)strlen(name) + 1; 函数CreateFrame()的作用在于生成一个新的扩展框架,并按照指定的参数为该框架命名: HRESULT cAllocateHierarchy::CreateFrame(LPCSTR name, LPD3DXFRAME* ret_frame){ *ret_frame = NULL; |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:10:00 AM -- 在一个框架创建好后,需要创建该框架的网格容器,这通过函数CreateMeshContainer()来实现: HRESULT cAllocateHierarchy::CreateMeshContainer(LPCSTR name, CONST D3DXMESHDATA* mesh_data, CONST D3DXMATERIAL* xmaterials, CONST D3DXEFFECTINSTANCE* effect_instances, DWORD num_materials, CONST DWORD* adjacency, LPD3DXSKININFO skin_info, LPD3DXMESHCONTAINER* ret_mesh_container) { *ret_mesh_container = NULL;
函数DestroyFrame()只有一个参数指向准备释放的框架对象: HRESULT cAllocateHierarchy::DestroyFrame(LPD3DXFRAME frame_to_free) { SAFE_DELETE_ARRAY(frame_to_free->Name); SAFE_DELETE(frame_to_free); 函数DestroyMeshContainer()也只有一个参数指向将要释放的网格容器对象: HRESULT cAllocateHierarchy::DestroyMeshContainer(LPD3DXMESHCONTAINER base_mesh_container){ if(base_mesh_container == NULL) return S_OK; |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:10:00 AM -- 类cAnimMesh是最关键的一个类,所有与骨骼动画相关的具体实现细节都封装在该类中,该类还定义了类cAllocateHierarchy的一个对象m_alloc_hierarchy,该对象完成从文件中加载动画网格模型的骨骼层次结构、动画数据以及其他用于绘制模型的几何数据。
class cAnimMesh{private: cAllocateHierarchy* m_alloc_hierarchy; IDirect3DDevice9* m_device; D3DXFRAME* m_root_frame; 构造函数负责分配资源和初始化成员变量,析构函数负责释放资源: cAnimMesh::cAnimMesh(){ m_is_play_anim = true; m_device = NULL; m_anim_controller = NULL; m_root_frame = NULL; 函数load_from_xfile()的主要任务是调用函数D3DXLoadMeshHierarchyFromX()从.x文件中加载动画模型,其实现如下: HRESULT cAnimMesh::load_from_xfile(CONST WCHAR* wfilename){ HRESULT hr; 来看看D3DXLoadMeshHierarchyFromX()的具体使用说明: Loads the first frame hierarchy from a .x file. HRESULT D3DXLoadMeshHierarchyFromX( LPCSTR Filename, DWORD MeshOptions, LPDIRECT3DDEVICE9 pDevice, LPD3DXALLOCATEHIERARCHY pAlloc, LPD3DXLOADUSERDATA pUserDataLoader, LPD3DXFRAME* ppFrameHierarchy, LPD3DXANIMATIONCONTROLLER* ppAnimController); Remarks All the meshes in the file will be collapsed into one output mesh. If the file contains a frame hierarchy, all the transformations will be applied to the mesh. D3DXLoadMeshHierarchyFromX loads the animation data and frame hierarchy from a .x file. It scans the .x file and builds a frame hierarchy and animation controller according to the ID3DXAllocateHierarchy-derived object passed to it through pAlloc. Loading the data requires several steps as follows: Derive ID3DXAllocateHierarchy, implementing each method. This controls how frames and meshes are allocated and freed. To free this data, call ID3DXAnimationController::Release to free the animation sets, and D3DXFRAMEDestroy, passing in the root node of the frame hierarchy and an object of your derived ID3DXAllocateHierarchy class. ID3DXAllocateHierarchy::DestroyFrame and ID3DXAllocateHierarchy::DestroyMeshContainer will each be called for every frame and mesh object in the frame hierarchy. Your implementation of ID3DXAllocateHierarchy::DestroyFrame should release everything allocated by ID3DXAllocateHierarchy::CreateFrame, and likewise for the mesh container methods.
因为在每次渲染网格模型前,只有知道每个框架的确切位置,才能在正确的位置上绘制出该框架包含的具体网格模型,所以需要计算得到各级框架的组合变换矩阵,函数update_frame_matrices()采用递归的方法计算各级框架的组合变换矩阵,具体实现代码如下: void cAnimMesh::update_frame_matrices(D3DXFRAME* base_frame, CONST D3DXMATRIX* parent_matrix){ D3DXFRAME_DERIVED* frame = (D3DXFRAME_DERIVED*) base_frame; 因为骨骼动画网格模型是通过框架按照树状结构组织起来的,而网格模型又包含在框架之中,所以在为了渲染网格模型的同时能将其中的动画播放出来,就需要逐个框架逐个网格模型地进行渲染,其中draw_mesh_container()负责渲染框架中包含的具体网格模型: void cAnimMesh::draw_mesh_container(CONST D3DXMESHCONTAINER* base_mesh_container, CONST D3DXFRAME* base_frame){ D3DXMESHCONTAINER_DERIVED* mesh_container = (D3DXMESHCONTAINER_DERIVED*) base_mesh_container; D3DXFRAME_DERIVED* frame = (D3DXFRAME_DERIVED*) base_frame;
函数draw_frame()以draw_mesh_container()为基础,采用递归的方法,将整个网格模型绘制出来: void cAnimMesh::draw_frame(CONST D3DXFRAME* frame){ D3DXMESHCONTAINER* mesh_container = frame->pMeshContainer;
函数render()通过draw_frame()完成整个网格模型的渲染,其实现如下: void cAnimMesh::render(CONST D3DXMATRIX* mat_world, double app_elapsed_time){ if(0.0f == app_elapsed_time) return;
create()函数用于根据参数指定的网格模型文件名创建骨骼动画网格模型: HRESULT cAnimMesh::create(IDirect3DDevice9* device, CONST WCHAR* wfilename){ m_device = device; void cAnimMesh::destroy(){ delete this;} |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:11:00 AM -- 使用cAnimMesh类包含3个步骤,首先在回调函数OnCreateDevice()中创建cAnimMesh类的实例: g_anim_mesh = new cAnimMesh(); 接着在回调函数OnFrameRender()中渲染网格模型: g_anim_mesh->render(&g_mat_world, fElapsedTime * 0.001); 最后在回调函数OnDestroy()中释放网格模型:
运行截图: |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:11:00 AM -- 主程序: #include "dxstdafx.h" #pragma warning(disable : 4127 4995) #define IDC_TOGGLE_FULLSCREEN 1 #define release_com(p) do { if(p) { (p)->Release(); (p) = NULL; } } while(0) ID3DXFont* g_font; CDXUTDialogResourceManager g_dlg_resource_manager; cAnimMesh* g_anim_mesh; //-------------------------------------------------------------------------------------- IDirect3D9* pD3D = DXUTGetD3DObject(); if ( FAILED( pD3D->CheckDeviceFormat( pCaps->AdapterOrdinal, pCaps->DeviceType, AdapterFormat, return true ; static bool is_first_time = true ; if (is_first_time) // if using reference device, then pop a warning message box. return true ; V_RETURN(g_dlg_resource_manager.OnCreateDevice(pd3dDevice)); D3DXCreateFont(pd3dDevice, 18, 0, FW_BOLD, 1, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, return S_OK; V_RETURN(g_dlg_resource_manager.OnResetDevice()); // set dialog position and size g_button_dlg.SetLocation(pBackBufferSurfaceDesc->Width - 170, 0); // setup view matrix D3DXMATRIX mat_view; D3DXMatrixLookAtLH(&mat_view, &eye, &at, &up); // set projection matrix // setup light light.Type = D3DLIGHT_DIRECTIONAL; D3DXVECTOR3 light_dir(0.0f, -1.0f, 1.0f); pd3dDevice->SetRenderState(D3DRS_AMBIENT, 0x00FFFFFF); return S_OK; //-------------------------------------------------------------------------------------- release_com(g_text_sprite); release_com(g_font); g_anim_mesh->destroy(); //-------------------------------------------------------------------------------------- D3DXMatrixRotationX(&mat_rot_x, -D3DX_PI / 2); //-------------------------------------------------------------------------------------- // show frame and device states // show helper information if (g_show_help) text_helper.End(); //-------------------------------------------------------------------------------------- if (g_settings_dlg.IsActive()) // Clear the render target and the zbuffer // Render the scene V( pd3dDevice->EndScene() ); if (g_settings_dlg.IsActive()) *pbNoFurtherProcessing = g_button_dlg.MsgProc(hWnd, uMsg, wParam, lParam); return 0; //-------------------------------------------------------------------------------------- case IDC_TOGGLE_REF: case IDC_CHANGE_DEVICE: //-------------------------------------------------------------------------------------- g_button_dlg.SetCallback(OnGUIEvent); int x = 35, y = 10, width = 125, height = 22; g_button_dlg.AddButton(IDC_TOGGLE_FULLSCREEN, L"Toggle full screen", x, y, width, height); //-------------------------------------------------------------------------------------- // Set the callback functions // Initialize DXUT and create the desired Win32 window and Direct3D device for the application // Start the render loop // TODO: Perform any application-level cleanup here return DXUTGetExitCode(); |
-- 作者:卷积内核 -- 发布时间:12/26/2008 11:12:00 AM -- 在骨骼动画网格模型中,骨骼的变动导致躯体网格模型的改变,于是就有了骨骼动画。网格模型是由一个个顶点构成的,如果任意一个顶点只受一块骨骼影响,那么网格模型在运动时很容易出现裂缝,特别是在一些结合部位,如肩关节、肘关节等。尽管可以使用缝合技术缩小这些裂痕,但当关节的旋转比较极端时,例如前臂绕肘关节旋转120度时,就会在肘关节处出现一个较大的剪缺。这是因为只有一个多边形跨越在上臂和前臂之间的整个间隙,间隙越大,裂缝就越大,最终的渲染效果也越差。 采用蒙皮骨骼动画网格模型则可以很好地解决这一问题,蒙皮骨骼动画网格模型假定其中的一个顶点可以受多块骨骼影响,网格中的每个顶点包含一个影响它的骨骼列表。骨骼列表提供了影响该顶点的每个骨骼的权重信息,这些权重信息表示具体的每块骨骼将对该顶点产生多大的影响,任何一个顶点的所有骨骼权重之和是1.0。对于蒙皮网格模型,Direct3D首先会根据骨骼权重和骨骼矩阵对顶点数据(包括顶点位置和顶点法向量)进行混合,然后进行渲染。 这项技术称为图形混合(或称为顶点混合)。假设某个顶点平均地附属到两块骨骼上,这意味着两块骨骼对于该顶点的权重分量都是0.5,即该顶点将从每块骨骼上各继承50%的运动,所以这项技术也称为"骨骼蒙皮调色",权重一般是根据顶点所附属的份数和从顶点到每块骨骼的距离进行划分的,不过这些工作在大多数三维建模软件中已经事先计算好了,程序员需要做的工作只是加载保存在网格模型中的蒙皮信息,即骨骼矩阵和相应的权重,并将其应用到网格模型各部分的变换以及最终的渲染。
图形混合 Direct3D提供了图形混合技术,即蒙皮技术,实现了具有平滑关节的效果,特别是角色模型的渲染,从而提高了场景的真实感。 下图演示了图形混合的基本原理,它对最初的图形进行了两次变换,第一次没有对顶点进行变换,相当于对顶点数据乘以一个单位矩阵,第二次对图形进行了简单的旋转变换,相当于对顶点数据乘以一个旋转矩阵,将两次变换后的顶点数据进行混合,然后渲染就得到了最终显示的图形。 在对顶点位置和法线进行混合时,每个混合矩阵对最终的顶点位置和法线的影响大小由混合权重来决定。混合权重是0.0 ~ 1.0之间的浮点数,0.0表示该权重对应的混合矩阵对顶点位置和法线向量没有影响,1.0表示顶点的位置和法线完全通过该权重对应的混合矩阵进行变换,权重也是顶点数据的一部分。 Direct3D根据混合矩阵和相应的权重对顶点位置和法线进行现行插值,公式如下: Vblend = V * (M0 * W0 + M1 * W1 + ... + Mn * Wn) 其中,Vblend表示混合后的顶点,V表示混合前的顶点,M0、M1、...,Mn表示混合矩阵,W0、W1、...,Wn表示混合权重。 因为所有混合权重之和为1.0,所以在顶点数据中只需保存前n-1个混合权重,第n个混合权重为1.0减去前n-1个混合权重之和。在Direct3D引擎中,影响每个顶点的混合矩阵最大数量限定为4,所以在Direct3D中顶点混合就有三种情况: Vblend = V * (M0 * W0 + M1 * (1 - W0)) Vblend = V * (M0 * W0 + M1 * W1 + M2 * (1 - (W0 + W1))) Vblend = V * (M0 * W0 + M1 * W1 + M2 * W2 + M3 * (1 - (W0 + W1 + W2))) 如果使用图形混合,则在顶点数据中必须包含相应的混合权重数据,尽管在灵活顶点格式中使用D3DFVF_XYZB1 ~ D3DFVF_XYZB5指定顶点数据中最多可以包含5个混合权重,但目前的Direct3D版本最多只使用前面三个混合权重。在具体渲染图形时,Direct3D使用几个混合矩阵和相应的混合权重对顶点进行混合,由渲染状态D3DVERTEXBLEND控制,该渲染状态的取值为枚举类型D3DVERTEXBLENDFLAGS。 D3DRS_VERTEXBLEND typedef enum D3DVERTEXBLENDFLAGS{ D3DVBF_DISABLE = 0, D3DVBF_1WEIGHTS = 1, D3DVBF_2WEIGHTS = 2, D3DVBF_3WEIGHTS = 3, D3DVBF_TWEENING = 255, D3DVBF_0WEIGHTS = 256,} D3DVERTEXBLENDFLAGS, *LPD3DVERTEXBLENDFLAGS; Constants Geometry blending (multimatrix vertex blending) requires that your application use a vertex format that has blending (beta) weights for each vertex. 在默认情况下,顶点混合是禁用的。要启用图形混合功能,只需调用IDirect3DDevice9::SetRenderState()函数,将渲染状态D3DVERTEXBLEND设置为D3DVERTEXBLENDFLAGS枚举值中除D3DVBF_DISABLE外的其他值。如果启用了图形混合功能,Direct3D就认为在顶点数据中包含了合适数量的混合权重,程序员的工作就是为图形渲染提供包含所需数量混合权重的顶点数据和相应的顶点格式。 |
-- 作者:秋十三 -- 发布时间:1/2/2009 8:16:00 PM -- 好 我全收藏了 |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
6,566.406ms |