录制DirectX和OpenGL渲染动画

分享于 

15分钟阅读

多媒体

 

Sample Image - SimulationRecording.jpg

内容

简介

创建游戏和模拟时,有时需要记录渲染内容以便离线查看。 在实际呈现过程过于复杂且需要按需重复时,这一点尤其难以避免。 还要创建剪切场景电影,需要记录渲染的动画apriori并在游戏中使用它们。

在DirectX中,库函数 D3DXSaveSurfaceToFile() 有助于将 Surface 保存到图像文件中。 对于 OpenGl,我们可以使用 glReadPixels() 读取渲染图像位,然后手动将它们保存到图像文件中。 虽然这些对于单帧记录来说足够,但不存在这样简单的方法来连续记录帧的序列。 换句话说,没有库函数可以记录我们完整的渲染动画结果。

在这里,本文介绍了一些类,这些类有助于从DirectX和OpenGL动画创建电影。 可以从下面的CDxToMovieCGLToMovie 中选择或者连续地创建,和OpenGL渲染框架。 通常,典型的电影创建过程涉及复杂任务,例如读取位图内容,选择帧速率设置,编解码器设置,initalizing流,编写流 等等。 ( 有关如何从正常的HBitmap序列创建影片的详细讨论,请参考文章从HBitmap创建电影)。 这里提供的CDxToMovieCGLToMovie 抽象出所有不必要的复杂性,并提供简单和直接的接口,如下面解释的那样。

录制来自DirectX渲染序列的影片。

CDxToMovie 可以将渲染的序列记录到电影文件中。 这个类使用 LPDIRECT3DSURFACE9 9.0接口作为它的功能,因此你应该使用 DirectX 9.0 SDK或者它的兼容程序来使用这个类。 如下所述,使用这里类是相当的straighforward。

首先,复制文件 DxToMovie.h, RenderTarget.h, AviFile.h 以及从本文的DirectX源代码到项目目录的AviFile.cpp,并将它们添加到项目中。 并将 vfw32.lib 添加到链接器输入库。 将 CDxToMovie 类添加到项目中后,可以通过 #including的头文件" dxtomovie.h"从代码访问类。

CDxToMovie 构造函数接受各种参数,如输出电影文件名。电影帧宽度和高度。每个像素 等等的比特数。 如下所示。

CDxToMovie(LPCTSTR lpszOutputMovieFileName = _T("Output.avi"),
 int nFrameWidth = GetSystemMetrics(SM_CXSCREEN), /*Frame Width*/int nFrameHeight = GetSystemMetrics(SM_CYSCREEN), /*Frame Height*/int nBitsPerPixel = 32, /*Bits per Pixel*/ DWORD dwCodec = mmioFOURCC('M','P','G','4'), /*Video Codec*/ DWORD dwFrameRate = 1) /*Frame Rate (FPS)*/

你可以为这些参数传递自己的值,也可以使用默认值( 这对于大多数情况下都适用)。 但是,应该注意这些是一个时间设置,不能在影片录制时间之后更改。 每个 CDxToMovie 对应不同的电影文件,重新创建具有相同输出文件 NAME的CDxToMovie 对象将不会附加到以前的电影内容,而是覆盖它。

CDxToMovie g_MovieRecorder("Output.Avi", 320, 240);

CDxToMovie 创建对象后,应在为应用程序创建 direct 3d设备时在该对象上调用方法 CDxToMovie::OnCreateDevice()。 当设备丢失。复位和销毁时,应调用 Similarily。CDxToMovie::OnLostDevice()CDxToMovie::OnResetDevice()CDxToMovie::OnDestroyDevice()。 这些函数的Prototype如下所示。

class CDxToMovie
{
 HRESULT OnCreateDevice(LPDIRECT3DDEVICE9 pd3dDevice);
 HRESULT OnDestroyDevice(LPDIRECT3DDEVICE9 pd3dDevice);
 HRESULT OnLostDevice();
 HRESULT OnResetDevice(LPDIRECT3DDEVICE9 pd3dDevice,
 const D3DSURFACE_DESC* pBackBufferSurfaceDesc);
};

函数 OnCreateDevice()OnDestroyDevice() 接受单个参数,即指向应用程序设备对象的direct 3d的指针。 OnLostDevice() 没有参数,但是 OnResetDevice() 需要一个指向设备缓冲区 Surface的Surface 描述的指针作为一个 D3DSURFACE_DESC*CDxToMovie 对象使用 D3DSURFACE_DESC 中提供的信息来在内部创建适当的屏幕外呈现目标,可以用于记录应用程序的呈现。

实际记录由 CDxToMovie::StartRecordingMovie()CDxToMovie::PauseRecordingMovie() 函数完成。 必须为 IDirect3DDevice9::BeginScene()IDirect3DDevice9::EndScene() 之间的每个帧调用这两个函数,如下所示。

g_pd3dDevice->BeginScene();// Capture the Rendering onto CDxToMovie's Render Targetg_MovieRecorder.StartRecordingMovie(g_pd3dDevice);
 // Render as usual..... g_pd3dDevice->Clear(0,NULL,D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
 D3DCOLOR_XRGB(0,0,200),1,0);
 g_pd3dDevice->SetStreamSource(0,g_pVB,0,sizeof(CUSTOMVERTEX));
 g_pd3dDevice->SetFVF(D3DFVF_CUSTOMVERTEX);
 g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLELIST,0,1);
g_MovieRecorder.PauseRecordingMovie(g_pd3dDevice);// Copy the CDxToMovie's Render Target content onto BackBuffer's Surfaceg_pd3dDevice->StretchRect(g_MovieRecorder.RecordingSurface(),
 NULL,pBackSurface,
 0,D3DTEXF_NONE);
g_pd3dDevice->EndScene();

在上面的代码Fragment中, g_MovieRecorder.StartRecordingMovie(g_pd3dDevice) 将所有后续呈现重定向到呈现目标的CDxToMovie 内部,直到 g_MovieRecorder.PauseRecordingMovie(g_pd3dDevice) 将被还原为原始 Surface的呈现目标。 由于所有呈现都是在呈现目标的CDxToMovie 内部完成的,因这里应用程序 Surface 背后不会显示任何有效内容。 如果你的应用程序正在直接创建 wihtout,在屏幕( 例如如果要创建要插入到游戏中的渲染剪切场景) 上显示任何动画,这并不重要。 但是,如果你正在从交互式游戏会话录制电影,那么如果屏幕没有被最新的渲染内容( 因为 CDxToMovie 通过重定向渲染目标来窃取内容) 更新。 为了避免这种情况,可以使用方法 IDirect3DDevice9::StretchRect()CDxToMovie 内部内容复制到应用程序 Surface 背面,然后通常的g_pd3dDevice->EndScene()g_pd3dDevice->Present() 调用。

如果你希望有选择地避免将帧记录到影片中,只需不要调用 g_MovieRecorder.StartRecordingMovie()g_MovieRecorder.PauseRecordingMovie() ( 和 correspoding g_pd3dDevice->StretchRect() ) 对于这些帧,动画将直接呈现在屏幕( 不被重定向到呈现目标的CDxToMovie 内部) 上。

在屏幕上提供的演示代码提供一个简单的DirectX应用程序,在屏幕上呈现一个三角形,鼠标移动窗口,鼠标移动到窗口中,并将它的录制到电影文件。 要运行演示程序,请确保机器上安装了MPG4编解码器,并且目录具有创建输出电影文件的写权限。 有关编解码器和FPS设置的详细信息,请参考文章从HBitmap创建电影。

从OpenGL渲染序列中录制电影。

CGLToMovie 可以将OpenGL渲染序列记录到电影文件中。 这是一个非常简单和直接的前进类,。

首先,从本文的OpenGL源代码中将文件 GLToMovie.h AviFile.h 和 AviFile.cpp 复制到项目目录中,并将它们添加到项目中。 并将 vfw32.lib 添加到链接器输入库。 添加到项目之后,可以通过-ing从代码中访问 CGLToMovie 类。

CGLToMovie 构造函数接受各种参数,如输出电影文件名。电影帧宽度和高度。每个像素 等等的比特数。 如下所示。

CGLToMovie(LPCTSTR lpszOutputMovieFileName = _T("Output.avi"),
 int nFrameWidth = GetSystemMetrics(SM_CXSCREEN), /*Frame Width*/int nFrameHeight = GetSystemMetrics(SM_CYSCREEN), /*Frame Height*/int nBitsPerPixel = 24, /*Bits per Pixel*/ DWORD dwCodec = mmioFOURCC('M','P','G','4'), /*Video Codec */ DWORD dwFrameRate = 1) /*Frames Per Second (FPS)*/

你可以为这些参数传递自己的值,也可以使用默认值( 这对于大多数情况下都适用)。 但是,应该注意这些是一个时间设置,不能在影片录制时间之后更改。 每个 CGLToMovie 对应不同的电影文件,重新创建具有相同输出文件 NAME的CGLToMovie 对象将不会附加到以前的电影内容,而是覆盖它。

CGLToMovie g_MovieRecorder("Output.Avi", VIEWPORTWIDTH, VIEWPORTHEIGHT);

在创建 CGLToMovie 对象之后,需要做的是在调用 SwapBuffers() 之前为每个帧调用 CGLToMovie::RecordFrame() 方法。 这段代码的代码Fragment如下所示。

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Render as usualfor (int i = 0; i < NUM_SHARKS; i++)
{
 glPushMatrix();
 FishTransform(&sharks[i]);
 DrawShark(&sharks[i]);
 glPopMatrix();
}// Capture the Rendering into CGLToMovie's movie fileg_MovieRecorder.RecordFrame();
SwapBuffers(wglGetCurrentDC());

函数 CGLToMovie::RecordFrame() 内部使用 glReadPixels() 方法读取帧缓冲的内容,并将帧附加到输出电影文件中。 如果你想避免在电影中录制帧,只要不要对这些帧调用方法,它们将被跳入电影中。

本文提供的演示代码提供了一个简单的OpenGL应用程序,它将屏幕上的几个鲨鱼动画化,将simulatenously渲染并记录到电影文件( 命名 output.avi )。 要运行演示程序,请确保机器上安装了Cinepak编解码器,并且目录具有创建输出电影文件的写权限。 有关编解码器和FPS设置的详细信息,请参考文章从HBitmap创建电影。

几点注意,避免出错。

  • 在渲染应用程序中,通常可以更改渲染 Surface 格式( 或者宽度和高度设置),并使用新设置继续动画。 但是,一旦一个电影的第一帧的宽度。高度和位设置为一个特定的帧,就不可以能在之间更改这些设置。 影片的所有帧应具有相同的格式和大小。 因这里,需要将应用程序呈现窗口限制为在录制影片时调整它的设备和 Surface的格式。 影片录制以particual设置和格式开始后,整个电影应该用相同的设置和格式录制。
  • 就像在创建电影 from explain Movie电影需要编码解码器来压缩它的帧。 本文提供的代码中,电影创建功能是在 AviFile.h 和 AviFile.cpp 文件中处理的。 使用的默认编解码器是 MPG4,它必须安装在计算机上才能成功创建电影文件。 你可以修改 AviFile.cpp 中的代码,以使用你自己选择的编解码器来创建电影。 但是,如果系统中不存在编解码器或者帧大小设置不符合编解码器格式要求,则可能会遇到错误。 但是,由于影片创建库的容错性质,呈现应用程序仍然会继续执行。
  • 电影创建库为它的功能使用函数指针。 然而,由于 Visual Studio 2005中的中断更改,Visual Studio.Net 编译器与最新 Visual Studio 2005编译器之间的函数指针赋值不同。 使用 Visual Studio 2005,pointer-to-members现在需要合格的NAME 和 &。 如果在编译源代码时遇到错误,请参考: Visual C++ 2005编译器中的中断更改。

结论

本文介绍了一些类,这些类有助于从DirectX和OpenGL渲染动画中录制电影。 所生成的影片质量可以能不同于各种设置,从视频帧速率设置到正在使用的编解码器。 编解码器的选择对输出影片的质量有很大的影响。 比如,对于某些屏幕捕获应用程序,如果在质量和大小都不相同的情况下,选择一个称为 Windows 媒体Video的特定编解码器,但在高比特率动画应用程序中可能会产生最佳效果。 上面描述的类使用avi作为电影类型。 但是,它们可以被用来创建其他类型的电影,如wmv和 mov,同样容易。


相关文章