380 likes | 587 Views
第 10 章 多媒体应用. 10.1 使用媒体控制接口 (MCI) 10.2 使用 OpenGL 10.3 DirectX 编程. 10.1 使用媒体控制接口 (MCI). 10.1.1 MCI 设备类型 媒体控制接口允许控制两类设备:第一类为简单设备,是指那些不需要文件的设备,如 CD 音频播放设备;第二类为复合设备,是那些需要文件的设备,如数字视频及波形音频设备等。表 10.1 列出了目前已定义的设备的标识符。. 10.1 使用媒体控制接口 (MCI). 10.1.2 MCI 编程步骤 打开设备
E N D
第10章多媒体应用 10.1 使用媒体控制接口(MCI) 10.2 使用OpenGL 10.3 DirectX编程
10.1使用媒体控制接口(MCI) 10.1.1 MCI设备类型 媒体控制接口允许控制两类设备:第一类为简单设备,是指那些不需要文件的设备,如CD音频播放设备;第二类为复合设备,是那些需要文件的设备,如数字视频及波形音频设备等。表10.1列出了目前已定义的设备的标识符。
10.1使用媒体控制接口(MCI) 10.1.2 MCI编程步骤 • 打开设备 MCI为不同的多媒体设备打开提供相应的数据结构类型。打开一个多媒体设备:定义一个多媒体设备结构类型变量,给结构变量中的相应参数赋值,调用mciSendCommand成功调用时,即可获得相应的设备标识符。下面的代码是打开波形音频设备: WORD wDeviceID; // MCI设备ID CString fileName; // 波形文件名 MCI_OPEN_PARMS openParms; // MCI设备打开参数 openParms.lpstrDeviceType = "waveaudio"; // 波形音频设备openParms.lpstrElementName = fileName; if (mciSendCommand (NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_TYPE, (DWORD)(LPVOID) &openParms)) return FALSE; wDeviceID = openParms.wDeviceID; • 设置或获取设备信息 使用MCI_SET和MCI_STATUS命令可以设置和获取设备信息,函数mciSendCommand发送命令时,使用相应的MCI_SET_PARMS结构。将波形音频设备的时间格式设成毫秒: MCI_SET_PARMS setParms; setParms.dwTimeFormat=MCI_FORMAT_MILLISECONDS; if(mciSendCommand(wDeviceID, MCI_SET, MCI_SET_TIME_FORMAT,(DWORD)(LPVOID) &setParms)) return FALSE;
10.1使用媒体控制接口(MCI) • 播放设备 使用MCI_PLAY命令可以使设备播放多媒体文件,并在用函数mciSendCommand发送命令时,使用相应的MCI_PLAY_PARMS结构。例如,下面的代码是播放波形音频设备: MCI_PLAY_PARMS playParms; // 定位到开始位置 mciSendCommand (wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, NULL); // 播放设备 if (mciSendCommand (wDeviceID, MCI_PLAY, NULL, (DWORD)(LPVOID) &playParms)) return FALSE; Else return TRUE; • 关闭设备 用MCI_STOP和MCI_CLOSE用来停止播放和关闭设备。它们不需要设置或返回附加的信息,不必考虑相应的MCI_GENERIC_PARMS结构。下面是停止播放波形音频设备并关闭: if (wDeviceID){ mciSendCommand (wDeviceID, MCI_STOP, MCI_WAIT, NULL); mciSendCommand (wDeviceID, MCI_CLOSE, NULL, NULL); } wDeviceID = 0;
10.1使用媒体控制接口(MCI) • 实例 [例Ex_Wave] 播放WAVE音频。 (1)用MFC AppWizard创建一个默认的基于对话框的应用程序项目Ex_Wave。 (2)单击开发环境标准工具栏中的新建工具按钮(),输入下列代码: class CWave : public CObject { public: // 构造和析构函数 CWave(); virtual ~CWave(); // 操作 public: BOOL Open(CString waveFileName);// 调入一个波形文件并打开设备 void Close(); // 关闭设备 void Stop(); // 停止播放 BOOL Play(CWnd *pWnd); // 播放并向窗口pWnd发送MCI_NOTIFY消息 // 数据成员 protected: WORD m_wDeviceID; // 用于保存设备标识 BOOL m_bOpened; // 设备打开标志 }; (3)按快捷键Ctrl+S,指定文件名Wave.h,保存到Ex_Wave文件夹“..\Ex_Wave”中。
10.1使用媒体控制接口(MCI) (4)按上述方法,再创建一个Wave.cpp文件,其代码如下: #include "stdafx.h" #include "Wave.h" CWave::CWave() // 在构造函数中添加一些初始代码 { m_bOpened = FALSE; // 设备没有打开 m_wDeviceID = 0; // 设备号为0 } CWave::~CWave() // 在析构函数中添加关闭设备代码 { Close(); // 关闭设备} BOOL CWave::Play(CWnd *pWnd) // 在窗口中播放设备 { Stop(); // 停止播放设备,设备复位 MCI_PLAY_PARMS playParms; // 定义一个播放结构变量 playParms.dwCallback=(DWORD)pWnd->m_hWnd; // 设置回调窗口句柄 mciSendCommand (m_wDeviceID, MCI_SEEK, MCI_SEEK_TO_START, NULL); // 定位到设备的开始 if (mciSendCommand (m_wDeviceID, MCI_PLAY, MCI_NOTIFY, (DWORD)(LPVOID)&playParms)) /* 播放设备并向回调窗口发送MCI_NOTIFY消息 */ return FALSE; else return TRUE; }
10.1使用媒体控制接口(MCI) void CWave::Stop() // 停止播放 { mciSendCommand (m_wDeviceID, MCI_STOP, NULL, NULL); } void CWave::Close() // 关闭设备 { m_bOpened = FALSE; if (m_wDeviceID) { mciSendCommand (m_wDeviceID, MCI_STOP, MCI_WAIT, NULL); mciSendCommand (m_wDeviceID, MCI_CLOSE, NULL, NULL); } m_wDeviceID = 0; } BOOL CWave::Open(CString waveFileName) // 打开设备并调入音频波型文件 { if (m_bOpened) Close(); // 若设备打开,关闭它 MCI_OPEN_PARMS openParms; openParms.lpstrDeviceType = "waveaudio"; openParms.lpstrElementName = waveFileName; if (mciSendCommand (NULL, MCI_OPEN, MCI_OPEN_ELEMENT | MCI_OPEN_TYPE|MCI_WAIT, (DWORD)(LPVOID) &openParms)) return FALSE; // MCI_OPEN_ELEMENT指定音频波型文件保存在lpstrElementName中, // MCI_OPEN_TYPE指定设备类型保存在lpstrDeviceType中 m_wDeviceID = openParms.wDeviceID; m_bOpened = TRUE; return TRUE; }
10.1使用媒体控制接口(MCI) (5)选择“工程”“添加工程”“Files”,选中源代码文件Wave.h和Wave.cpp,单击[确定],CWave类就加入到项目Ex_Wave中了。 (6)打开StdAfx.h文件,添加调用MCI函数所需要的头文件和库的包含语句: #ifndef _AFX_NO_AFXCMN_SUPPORT #include <afxcmn.h> // MFC support for Windows Common Controls #endif // _AFX_NO_AFXCMN_SUPPORT #include <mmsystem.h> #pragma comment (lib,"winmm.lib") (7)切换到ResourceView页面,打开IDD_EX_WAVE_DIALOG对话框资源。 (8) 将对话框标题改为“播放波形音频”。 (9)按图10.1所示的布局,添加如表10.1所示的控件。
10.1使用媒体控制接口(MCI) (10)打开MFC ClassWizard对话框,切换到Member Variables页面,为上述控件添加相关联的成员变量如表10.2。 (11)为CEx_WaveDlg类添加一个公有型成员CWave m_wave,并在Ex_WaveDlg.h文件的前面添加CWave类的包含语句: #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #include "Wave.h"
10.1使用媒体控制接口(MCI) (12)用ClassWizard在CEx_WaveDlg类分别为按钮控件IDC_FILE、IDC_PLAY和IDC_STOP添加BN_CLICKED消息映射,并在函数中添加下列代码: void CEx_WaveDlg::OnFile() { CString filter; filter = "波形音频文件(*.wav)|*.wav||"; CFileDialog dlg (TRUE, NULL, NULL, OFN_HIDEREADONLY, filter); if (dlg.DoModal () == IDOK) { CString str; str = dlg.GetPathName(); if (m_wave.Open(str)){ m_btnPlay.EnableWindow(TRUE); m_btnPlay.SetFocus(); m_strFileName = str; UpdateData( FALSE ); }else MessageBox(str+"文件打不开或操作失败!"); } }
10.1使用媒体控制接口(MCI) void CEx_WaveDlg::OnPlay() { if (m_wave.Play(this)){ m_btnPlay.EnableWindow(FALSE); // 禁用[播放]按钮 m_btnFile.EnableWindow(FALSE); // 禁用[波形文件]按钮 m_btnStop.EnableWindow(TRUE); m_btnStop.SetFocus(); // 设置控件输入焦点 } } void CEx_WaveDlg::OnStop() { m_wave.Stop(); m_btnPlay.EnableWindow(TRUE); m_btnFile.EnableWindow(TRUE); m_btnStop.EnableWindow(FALSE); // 禁用[停止]按钮 m_btnPlay.SetFocus(); }
10.1使用媒体控制接口(MCI) (13)编译并运行。从Windows\Media目录中调用一些波形文件,单击[播放]按钮,来欣赏一下音乐,如图。 (14)上述程序的运行结果中,当一个波形音乐播放完之后,若不单击[停止]按钮,则[播放]按钮被禁用。实际上,当波形音频播放完毕后,[播放]就应该可以使用。为此需要处理MM_MCINOTIFY消息来实现,但由于MFC ClassWizard不支持该消息的映射,因而需要手动进行。 (15)打开Ex_WaveDlg.h文件,添加MM_MCINOTIFY消息映射函数声明,如下面所示的代码: protected: … LRESULT OnMCINotify(WPARAM wParam,LPARAM lParam); DECLARE_MESSAGE_MAP() };
10.1使用媒体控制接口(MCI) (16)打开Ex_WaveDlg.cpp文件,在类CEx_WaveDlg的消息入口处,添加消息宏指令: BEGIN_MESSAGE_MAP(CEx_WaveDlg, CDialog) //{{AFX_MSG_MAP(CEx_WaveDlg) ON_WM_SYSCOMMAND() ON_WM_PAINT() ON_WM_QUERYDRAGICON() ON_BN_CLICKED(IDC_FILE, OnFile) ON_BN_CLICKED(IDC_PLAY, OnPlay) ON_BN_CLICKED(IDC_STOP, OnStop) //}}AFX_MSG_MAP ON_MESSAGE(MM_MCINOTIFY,OnMCINotify) END_MESSAGE_MAP() (17)在Ex_WaveDlg.cpp文件的最后,添加CEx_WaveDlg::OnMCINotify函数代码: LRESULT CEx_WaveDlg::OnMCINotify(WPARAM wParam, LPARAM lParam) { OnStop(); return FALSE; } (18)编译并运行。从Windows\Media目录中调用一些波形文件,单击[播放]按钮,看看与前面有什么不同。
10.1使用媒体控制接口(MCI) 10.1.3使用MCIWnd窗口类 • MCIWnd窗口类 MCIWnd是一个控制多媒体设备的窗口类,它提供的函数、消息以及宏与上述的MCI的底层功能具体相似的方法。若在应用程序中使用MCIWnd函数所在的源文件的前面(最好是stdafx.h)添加vfw.h的头文件,以及编译时加入vfw32.lib库或在程序中加入下列语句: #pragma comment (lib,"vfw32.lib") 在MCIWnd窗口类中,虽然它所提供的函数并不多,但是它所提供的宏却非常多,并且基本上与MCI的底层功能相对应。 • 使用MCIWnd窗口类一般步骤 在应用程序中使用MCIWnd窗口类的一般步骤是: (1)在程序中调用MCIWndRegisterClass函数注册MCI窗口类,以便以后用CreateWindow或CreateWindowEx函数创建窗口,或者直接调用函数MCIWndCreate创建窗口。 (2)获得相应的窗口句柄后,就可调用MCIWndOpen宏来打开设备。 (3)MCIWnd窗口提供了相应的媒体控制按钮,因而不需要编写额外的代码。 (4)但作为技巧,用户还应该跟踪MCIWnd窗口的一些消息(如MCIWNDM_ NOTIFYSIZE)来调整MCIWnd窗口。
10.1使用媒体控制接口(MCI) • 示例 [例Ex_MCI] 制作一个多媒体播放器。 (1)用MFC AppWizard(exe)创建一个默认的多文档应用程序项目Ex_MCI。 (2)在StdAfx.h中放入包含文件使得应用程序能使用所有的多媒体代码。由于项目中的每一个文件已经包含了StdAfx.h,所以在其他地方就不必再包含这些多媒体文件。 ... #endif // _AFX_NO_AFXCMN_SUPPORT #include <vfw.h> #pragma comment (lib,"vfw32.lib") ... (3)在CEx_MCIApp::InitInstance函数,使用MCIWndRegisterClass函数注册MCI窗口类。虽然,后面的创建窗口是直接调用函数MCIWndCreate来进行的,但还应该保证应用程序的运行系统拥有并支持MCIWnd窗口类。 BOOL CEx_MCIApp::InitInstance() { if (!MCIWndRegisterClass()) return FALSE; AfxEnableControlContainer(); ... }
10.1使用媒体控制接口(MCI) (4)在Ex_MCIView类中添加一个公共成员变量标识嵌入的MCIWnd窗口句柄。 public: CEx_MCIDoc* GetDocument(); HWND m_hMyMCIWnd; ... (5)为Ex_MCIView类添加OnInitialUpdate消息处理函数,增加代码: void CEx_MCIView::OnInitialUpdate() { CView::OnInitialUpdate(); m_hMyMCIWnd = MCIWndCreate(m_hWnd,AfxGetInstanceHandle(), MCIWNDF_NOTIFYSIZE |MCIWNDF_NOERRORDLG | WS_CHILD|WS_VISIBLE,NULL); if (m_hMyMCIWnd==NULL) return; const CString &filename = GetDocument()->GetPathName(); if (filename.GetLength()>0) MCIWndOpen(m_hMyMCIWnd,(LPCSTR)filename,0); } (6)在CEx_MCIView构造函数中将成员变量m_hMyMCIWnd初始化为NULL,由于以后要对m_hMyMCIWnd进行判断,所以这一步非常重要。 CEx_MCIView::CEx_MCIView() { m_hMyMCIWnd = NULL; }
10.1使用媒体控制接口(MCI) (7)由于MCIWnd会发送一个重要的消息MCIWNDM_NOTIFYSIZE(窗口大小改变),所以还要添加处理该消息的代码来调整窗口的大小以便能及时更新显示。但MFC ClassWizard所封装的消息列表没有此消息,因而需要手动进行。在Ex_MCIView.h文件中的消息声明处添加下列代码: // Generated message map functions protected: //{{AFX_MSG(CEx_MCIView) ... //}}AFX_MSG afx_msg LONG OnNotifySize(UINT wParam, LONG lParam); DECLARE_MESSAGE_MAP() (8)在Ex_MCIView.cpp文件中的消息入口处添加下列代码: BEGIN_MESSAGE_MAP(CEx_MCIView, CView) ... ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview) ON_MESSAGE(MCIWNDM_NOTIFYSIZE,OnNotifySize) END_MESSAGE_MAP()
10.1使用媒体控制接口(MCI) (9)为CEx_MCIView类添加函数OnNotifySize,调整MCIWnd窗口的大小,代码: LONG CEx_MCIView::OnNotifySize(UINT wParam, LONG lParam) { CRect rc; CFrameWnd* pParent = GetParentFrame(); // 获取主框架窗口指针 if (m_hMyMCIWnd) { ::GetWindowRect(m_hMyMCIWnd,rc); pParent->CalcWindowRect(rc,CWnd::adjustBorder); CSize size(rc.Width(),rc.Height()); if (GetExStyle()&WS_EX_CLIENTEDGE) { size.cx+=4; size.cy+=4; } pParent->SetWindowPos(NULL,0,0,size.cx,size.cy, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE); } else { pParent->SetWindowPos(NULL,0,0,200,160, SWP_NOZORDER|SWP_NOACTIVATE|SWP_NOMOVE); } return 1; }
10.1使用媒体控制接口(MCI) (10)编译并运行。调入一个AVI文件(或其他媒体文件)并单击[播放]()按钮,其结果如图所示。
10.2使用OpenGL 10.2.1 OpenGL特点及功能 OpenGL不但具有开放性、独立性和兼容性三大特点,还提供了七大功能。 (1)建模功能 (2)变换功能 (3)颜色模式设置 (4)光照和材质设置 (5)纹理映射 (6)位图显示和图像增强 (7)双缓存动画
10.2使用OpenGL 10.2.2OpenGL图形库 OpenGL图形库主要由核心库、实用库和辅助库三个部分组成。 核心库中有一百多个函数,它们是最基本的函数,其前缀是gl实用库(OpenGL utility library, GLU)的函数功能更高一些,如绘制复杂的曲线曲面等;辅助库(OpenGL auxilary library,GLAUX)的函数是一些特殊的函数,包括简单的窗口管理、输入事件处理、某些复杂三维物体绘制等函数,前缀为aux。 此外,还有不少非常重要的WGL函数,专用于OpenGL 和Windows窗口系统的连接,其前缀为wgl,主要用于创建和选择绘图内容以及在窗口内任一位置显示字符位图。这些功能是对Windows和OpenGL的唯一补充。另外,还有一些个Win32函数用来处理像素格式( pixel formats)和双缓存,它们是对Win32系统的扩展。 在MFC中使用这些图形库的函数时,还必须在源文件的开头处加入相应的包含文件,以及编译时加入相应的库或在程序中加入,如下列语句: #include "gl\gl.h" #include "gl\glu.h" #include "gl\glaux.h" #pragma comment (lib,"Opengl32.lib") #pragma comment (lib,"Glu32.lib") #pragma comment (lib,"Glaux.lib")
10.2使用OpenGL 10.2.3用MFC编写OpenGL程序 • 编写OpenGL程序一般步骤 (1)先设置设备环境DC的位图格式(PIXELFORMAT)属性,通过填写一个PIXELFORMATDESCRIPTOR的结构来完成,该结构决定了OpenGL作图的物理设备的属性,比如该结构中的数据项dwFlags中PFD_DOUBLEBUFFER位,如果没有设置,通过该设备的DC作图的OpenGL命令就不可能使用双缓冲来做动画。由于有一些位图格式(PIXELFORMAT)DC不支持,因而程序必须先用ChoosePixelFormat来选择与DC所支持的指定位图格式最接近的位图格式,然后用SetPixelFormat设置DC的位图格式。 (2)根据刚才的设备环境DC用wglCreateContext建立一个渲染环境RC(Rendering Context,RC),并调用wglMakeCurrent使得RC与DC建立联系。 (3)调用OpenGL函数作图。 (4)作图完毕以后,先通过设置当前RC为NULL,断开和该渲染环境的联系,然后再根据RC句柄的有效性进行释放或者删除。
10.2使用OpenGL • OpenGL程序示例 [例Ex_OpenGL] 使用OpenGL。 (1)用MFC AppWizard创建一个单文档应用程序项目Ex_OpenGL,在向导第4步中将默认的“打印和打印预览(Printing and print Preview)”选项去掉(因为这里不需要),单击[完成]按钮。 (2)向CEx_OpenGLView类中添加下列成员变量: public: HGLRC m_hGLRC; (3)向CEx_OpenGLView类中添加下列成员函数: public: void DrawScene(); (4)编写DrawScene函数代码: { glClearColor(0.0,0.0,0.0,0.0); // 设置清屏所需要的颜色 glClear(GL_COLOR_BUFFER_BIT); // 清屏 glPushMatrix(); // 把当前操作矩阵压入矩阵堆栈 glColor3f(1.0,0.0,1.0); // 设置绘图颜色 auxWireTeapot(0.4); // 绘制茶壶的线框模型 glPopMatrix(); // 恢复当前操作矩阵 glFinish(); // 完成绘制 }
10.2使用OpenGL (5)在CEx_OpenGLView类的头文件Ex_OpenGLView.h的开始处添加OpenGL库的包含文件 #include "gl\gl.h" #include "gl\glu.h" #include "gl\glaux.h" #pragma comment (lib,"Opengl32.lib") #pragma comment (lib,"Glu32.lib") #pragma comment (lib,"Glaux.lib") (6)修改CEx_OpenGLView类中的PreCreateWindow函数来重新设置文档窗口的风格,OpenGL窗口必须具有WS_CLIPSIBLINGS和WS_CLIPCHILDREN风格,如下面的代码: BOOL CEx_OpenGLView::PreCreateWindow(CREATESTRUCT& cs) { cs.style |= WS_CLIPSIBLINGS|WS_CLIPCHILDREN; return CView::PreCreateWindow(cs); }
10.2使用OpenGL (7)为CEx_OpenGLView类添加WM_CREATE消息处理,增加下列初始化代码: int CEx_OpenGLView::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CView::OnCreate(lpCreateStruct) == -1) return -1; PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), // 该结构的大小 1, // 该结构的版本号,这里必须为1 PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, // 像素颜色模式为RGBA 24, // 颜色的位数为24位 0, 0, 0, 0, 0, 0, // 忽略各颜色分量在RGBA的位数 0, // 在RGBA中没有alpha成分 0, // 忽略alpha的偏移量 0, // 没有累加缓冲区 0, 0, 0, 0, // 忽略累加缓冲区中各颜色的位数 32, // z缓冲区的深度为32位 0, // 没有模板缓冲区 0, // 没有辅助缓冲区 PFD_MAIN_PLANE, // 设为主平面类型 0, // 保留,这里必须是0 0, 0, 0 // 忽略层、颜色等的屏蔽 };
10.2使用OpenGL CClientDC dc(this); int pixelformat = ChoosePixelFormat(dc.GetSafeHdc(), &pfd); if (SetPixelFormat(dc.GetSafeHdc(), pixelformat, &pfd) == FALSE) { MessageBox("SetPixelFormat failed"); return -1; } m_hGLRC = wglCreateContext(dc.GetSafeHdc()); return 0; }
10.2使用OpenGL (8)为CEx_OpenGLView类添加WM_DESTROY消息处理,并增加代码来释放RC句柄: void CEx_OpenGLView::OnDestroy() { if (wglGetCurrentContext()!=NULL) wglMakeCurrent(NULL,NULL); if (m_hGLRC!=NULL) { wglDeleteContext(m_hGLRC); m_hGLRC=NULL; } CView::OnDestroy(); } (9)为CEx_OpenGLView类添加WM_SIZE消息处理,增加代码。作用是窗口大小发生改变时,相应的改变视口大小和投影变换方式,场景中的物体正确地显示于窗口中。 void CEx_OpenGLView::OnSize(UINT nType, int cx, int cy) { CView::OnSize(nType, cx, cy); if(cy > 0) { CClientDC dc(this); wglMakeCurrent(dc.GetSafeHdc(),m_hGLRC); glViewport(0, 0, cx, cy); wglMakeCurrent(NULL,NULL); } }
10.2使用OpenGL (10)在CEx_OpenGLView::OnDraw中添加下列代码: void CEx_OpenGLView::OnDraw(CDC* pDC) { CEx_OpenGLDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); HWND hWnd=GetSafeHwnd(); // 获得当前窗口句柄 HDC hDC=::GetDC(hWnd); // 获得与窗口句柄相关联的设备环境 wglMakeCurrent(hDC,m_hGLRC);// 设置当前的渲染环境 DrawScene(); // 绘制场景 wglMakeCurrent(NULL,NULL); // 取消当前的渲染环境 SwapBuffers(hDC); // 将图形显示在窗口的设备环境中 } (11)编译并运行。注意运行时应保证屏幕的颜色在16位以上,如图。
10.3DirectX编程 10.3.1 DirextX概述 • Direct3D、DirectDraw和DirectGraphics DirectDraw为高速的2D渲染提供了良好的支持,由于它具备直接显存访问和位快传送的能力,使得2D图形的绘制速度相对GDI有了一个质的飞跃,其渲染速度甚至有上百倍的差距。 DirectX的版本到了8.0的时候结构发生了巨大的变化,3D图形处理技术逐渐统一在Vertex Shader和Pixel Shader。Vertex Shader被用来描述和修饰3D物体的几何形状,同时也用来控制光亮和阴影;Pixel Shader则用来操纵物体表面的色彩和外观。 Direct3DX实用库是一个位于Direct3D之上的辅助层,用于简化3D图形开发者的常规工作。它包括了一个蒙皮库,支持对网格的操作,还有组装顶点与像素着色器的功能。除此以外,DirectX 9还提供位移贴图以及改进的设备模拟等特性的支持。 • DirectSound、DirectMusic和DirectX Audio 在DirectX中DirectSound和DirectMusic可以统称DirectX Audio。DirectX Audio不是简单地对声音的回放,而且提供了一个完整的系统,能够利用硬件加速的功能动态地操纵控制音轨和声道(soundtrack)。 在DirectX 9中加入了DirectMP3和DirectCD两个功能模块,它们使得主流的音频播放也能够通过DirectX与其调用程序更好地结合在一起。
10.3DirectX编程 • DirectInput 用来管理用户输入。与Winodows事件响应相比,DirectInput可以直接访问硬件,直接从输入缓冲区内检索数据,从而获得比响应Windows消息更快的速度。DirectInput也对力反馈游戏杆(Force Feedback)提供了良好的支持。 • DirectPlay DirectPlay兼顾速度与易用性两个方面,在网络游戏简化多人游戏的开发。 • DirectShow 提供了Windows平台下高质量的媒体捕获和回放能力,支持格式:ASF、MPG、AVI、MP3和WAV。DirectShow支持使用WDM设备或老式视频设备进行视频捕获。当有硬件支持的环境下,它会在使用视频或音频时自动选择是否使用硬件加速。
10.3DirectX编程 10.3.2Direct3D编程 [例Ex_D3D] 用Visual C++进行Direct3D编程。 (1)用MFC AppWizard(EXE)创建一个默认的单文档应用程序Ex_D3D。 (2)为CEx_D3DView类添加两个保护型对象,代码: protected: LPDIRECT3D9 m_pD3D; // Direct3D对象的接口指针 LPDIRECT3DDEVICE9 m_pDevice; // 设备对象的接口指针 (3)为CEx_D3DView类添加OnInitialUpdate函数的重载,添加Direct3D初始化代码: void CEx_D3DView::OnInitialUpdate() { CView::OnInitialUpdate(); m_pD3D = ::Direct3DCreate9(D3D_SDK_VERSION); D3DPRESENT_PARAMETERS d3dpp; ::ZeroMemory(&d3dpp, sizeof(d3dpp)); d3dpp.Windowed = TRUE; // 创建窗口模式的Direct3D程序 d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; d3dpp.BackBufferFormat = D3DFMT_UNKNOWN; m_pD3D->CreateDevice( D3DADAPTER_DEFAULT, // 使用默认的显卡 D3DDEVTYPE_HAL, // 指定设备类型为HAL m_hWnd, // Direct3D窗口的句柄 D3DCREATE_SOFTWARE_VERTEXPROCESSING,//软件顶点处理 &d3dpp, &m_pDevice); }
10.3DirectX编程 (4)为CEx_D3DView类添加WM_DESTROY消息映射,添加Direct3D对象指针释放代码: void CEx_D3DView::OnDestroy() { CView::OnDestroy(); m_pDevice->Release(); // 释放设备对象 m_pD3D->Release(); // 释放Direct3D对象 } (5)打开stdafx.h文件,添加Direct3D的头文件和库的包含语句: #include <d3d9.h> #pragma comment (lib,"d3d9.lib") (6)在CEx_D3DView::OnDraw函数中添加下列绘制代码: void CEx_D3DView::OnDraw(CDC* pDC) { CEx_D3DDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); m_pDevice->Clear( 0, NULL, D3DCLEAR_TARGET,D3DCOLOR_XRGB(0,0,255), 1.0f, 0); // Direct3D规定在渲染前必须调用方法IDirect3DDevice9::BeginScene, m_pDevice->BeginScene(); // 这里添加其他3D绘制代码 m_pDevice->EndScene(); m_pDevice->Present(NULL, NULL, NULL, NULL); }
10.3DirectX编程 (7)编译并运行,如图。 (8)下面将要绘制一个三角形。Direct3D中,所有的三维实体都是由三角形构成。对于三角形的顶点格式,在class CEx_D3DView前面添加代码: // 定义FVF的顶点结构 struct CUSTOMVERTEX { float x, y, z; //顶点坐标 DWORD color; //顶点颜色 }; //定义FVF用到的数据项:坐标颜色 #define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ | D3DFVF_DIFFUSE) class CEx_D3DView : public CView { …};
10.3DirectX编程 (9)在CEx_D3DView类定义一个用于顶点缓存区的的接口指针对象及构造三角形的函数InitGeometry,代码: protected: LPDIRECT3D9 m_pD3D; // Direct3D对象的接口指针 LPDIRECT3DDEVICE9 m_pDevice; // 设备对象的接口指针 LPDIRECT3DVERTEXBUFFER9 m_pVB; // 顶点缓存区的的接口指针 void InitGeometry(); (10)在CEx_D3DView::InitGeometry函数中添加代码: void CEx_D3DView::InitGeometry() { // 构造三角形实体 CUSTOMVERTEX vertices[] = {{ -0.6f, -0.6f, 0.0f, D3DCOLOR_XRGB(255,0,0) }, { 0.0f, 0.6f, 0.0f, D3DCOLOR_XRGB(0,255,0) }, { 0.6f, -0.6f, 0.0f, D3DCOLOR_XRGB(255, 255 ,0) }}; // 创建顶点缓存区,并获取接口IDirect3DVertexBuffer9的指针 m_pDevice->CreateVertexBuffer(sizeof(vertices), //缓存区尺寸 0, D3DFVF_CUSTOMVERTEX,D3DPOOL_DEFAULT, &m_pVB, NULL ); void* pVertices; m_pVB->Lock( 0, sizeof(vertices), (void**)&pVertices, 0 ); memcpy( pVertices, vertices, sizeof(vertices) ); m_pVB->Unlock(); }
10.3DirectX编程 (11)在CEx_D3DView::OnDestroy函数中添加下列代码: void CEx_D3DView::OnDestroy() { CView::OnDestroy(); m_pVB->Release(); // 释放顶点缓存区 m_pDevice->Release(); // 释放设备对象 m_pD3D->Release(); // 释放Direct3D对象 } (12)在CEx_D3DView::OnDraw函数中添加下列代码: void CEx_D3DView::OnDraw(CDC* pDC) { … m_pDevice->BeginScene(); InitGeometry(); // 进行建模 // 设置自定义的FVF m_pDevice->SetFVF( D3DFVF_CUSTOMVERTEX ); // 绑定顶点缓存区至设备数据源 m_pDevice->SetStreamSource( 0, m_pVB, 0, sizeof(CUSTOMVERTEX) ); // 绘制图元,其中参数1为图元格式,参数3为三角形数目 m_pDevice->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1); m_pDevice->EndScene(); … }
10.3DirectX编程 (13) 编译并运行,如图。 (14)显示出其颜色,要在CEx_D3DView:: OnInitialUpdate函数中添加代码: void CEx_D3DView::OnInitialUpdate() { … // 因为使用顶点颜色渲染,所以要禁用光照处理 m_pDevice->SetRenderState( D3DRS_LIGHTING, FALSE ); // 关闭“挑选”功能,允许渲染背面 m_pDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE ); } (15)再次编译并运行,如图。
10.3DirectX编程 10.3.3使用DirectX向导 [例Ex_DirectX] 使用DirectX 9应用程序向导来创建。 (1)选择“文件”“新建”菜单命令。单击“工程”标签,从列表框中选中项。 (2)在工程名称框中键入项目名称Ex_DirectX。 (3)单击[确定],如图。可以选择创建的窗口是单文档窗口还是MFC对话框窗口,以及对DirectX组件的支持等内容。 (4)去除DirectInput和DirectMusic组件支持,单击[下一个],如图。
10.3DirectX编程 (5)选中“Triangle(三角形)”,去除“Direct3D fonts”和“3D meshes”支持,单击[完成]。单击[确定]系统将自动创建此应用程序框架代码。 (6)编译并运行,如图。 (7)将项目工作区窗口切换到ClassView页面。需要说明的是,文件Ex_DirectX.cpp中的WinMain函数是程序的入口函数。