动画很简单:全屏显示图片,并在1秒内将图片水平移出屏幕,就像MS PowerPoint中的幻灯片放映切换效果一样。
我正在使用D3D9表面来实现动画,因为我希望该程序与Windows XP兼容,并且可能还需要一些显示图片的3D效果。
当我打开 VSYNC(d3dpp.演示间隔 = D3DPRESENT_INTERVAL_DEFAULT),fps 保持在 60,但我仍然可以看到图片不连续地移动(非常明显)。当我关闭VSYNC时,fps保持在1600左右,画面移动更流畅(但仍然有点滞后)。
这两种情况的奇怪之处在于,我可以看到图片的锯齿形边框和图片中的破裂:
##########
##########
#########
#########
########
我没有DX或2D动画的经验,所以我需要你的帮助。
代码的关键部分如下:
D3DPRESENT_PARAMETERS d3dpp = {0};
d3dpp.BackBufferCount = 3;
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_IMMEDIATE; //VSYNC off
// d3dpp.PresentationInterval = D3DPRESENT_INTERVAL_DEFAULT; //VSYNC
......
if(FAILED(D3DXLoadSurfaceFromFile(g_pSurface, NULL, NULL, PICPATH, NULL, D3DX_FILTER_NONE, 0, NULL)))
return E_FAIL;
......
while(1)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT)
break ;
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
else DxRender();
}
VOID DxRender()
{
LPDIRECT3DSURFACE9 pBackBuffer = NULL;
g_pd3dDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0 );
static int offs = 0;
static float StartTime = timeGetTime() * 0.001f;
float CurTime = timeGetTime() * 0.001f;
offs = CurTime - StartTime * g_cxClient / ANIMATION_TIME_S;
// ANIMATION_TIME_S = 1.0f
if(offs >= g_cxClient)
{
StartTime = CurTime;
offs -= g_cxClient;
}
RECT srcrect1 = {0,0,g_cxClient-1-offs,g_cyClient-1};
POINT dstpt1 = {offs,0};
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
if(FAILED(g_pd3dDevice->GetBackBuffer(0,0, D3DBACKBUFFER_TYPE_MONO, &pBackBuffer)))
{
MessageBox(NULL, TEXT("GetBackBuffer"), TEXT("Error"), MB_OK);
}
g_pd3dDevice->UpdateSurface(g_pSurface, &srcrect1, pBackBuffer, &dstpt1);
ShowFPS();
g_pd3dDevice->EndScene();
}
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
if(pBackBuffer != NULL)
pBackBuffer->Release();
}
动画不流畅可能是由于分辨率不足造成的 timeGetTime()
.这可能会导致这些问题(请参阅 timeGetTime() 的 MSDN),因为在高 FPS 下,帧可能短于一毫秒。您可以尝试QueryPerformanceCounter()
获取频率更高的计时器(MSDN 到 QueryPerformanceCounter())。那么它不应该滞后。我无法想象,为什么在 60 FPS 时会有锯齿形,但有D3DPRESENT_INTERVAL_IMMEDIATE破裂是正常的,因为屏幕无法保持刷新。
我有一个使用世界矩阵来翻译纹理的解决方案,关键步骤如下
- 从文件创建技术
- 显示 texute
- 更新世界矩阵
- 如果经过的时间少于 2 秒,则显示纹理
- 否则将纹理向右平移,直到它离开屏幕,翻译矩阵是基于时间的,你可以只更新matrix._41归档。
完整代码如下,你应该用你的本地文件替换纹理文件才能看到效果,详情请看函数 SetupMatrix 中的代码。
#include <d3dx9.h>
LPDIRECT3D9 g_pD3D = NULL ; // Used to create the D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL ; // Our rendering device
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL ; // Vertex buffer
LPDIRECT3DTEXTURE9 g_pTexture = NULL ; // Texture
D3DXMATRIX g_worldMatrix ;
float g_ShowTimeInterval = 2.0f; // How long will the picture displays.
float g_totalShowTime = 0.0f; // Time elapsed since the picture start to display.
#define SAFE_RELEASE(P) if(P){ P->Release(); P = NULL;}
struct Vertex
{
float x, y, z ; // Vertex position
float u, v ; // Texture coordinates
};
#define VertexFVF D3DFVF_XYZ | D3DFVF_TEX1
HRESULT InitD3D( HWND hWnd )
{
DWORD ScreenW = 0;
DWORD ScreenH = 0;
DEVMODE devMode ;
devMode.dmSize = sizeof(devMode) ;
DWORD iModeNum = 0 ;
DWORD r = 1 ;
while(r != 0)
{
r = EnumDisplaySettings(NULL, iModeNum, &devMode) ;
// Store the maximum resolution currently
if(devMode.dmPelsWidth >= ScreenW && devMode.dmPelsHeight >= ScreenH)
{
ScreenW = devMode.dmPelsWidth ;
ScreenH = devMode.dmPelsHeight ;
}
//OutputModeInfo(iModeNum, devMode) ;
iModeNum++ ;
}
// Create the D3D object.
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Set up the structure used to create the D3DDevice
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = FALSE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferWidth = ScreenW;
d3dpp.BackBufferHeight = ScreenH;
d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
// Create device
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
MessageBoxA(NULL, "Create D3D9 device failed!", "Error", 0) ;
return E_FAIL;
}
// Disable lighting, since we didn't specify color for vertex
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING , FALSE );
D3DXMatrixIdentity(&g_worldMatrix);
// Create Texture
HRESULT hr ;
hr = D3DXCreateTextureFromFile(g_pd3dDevice, "../Common/Media/chessboard.jpg", &g_pTexture) ;
if (FAILED(hr))
{
MessageBoxA(NULL, "Create Texture failed!", "Error", 0) ;
}
return S_OK;
}
// Prepare vertex buffer
void InitVB()
{
Vertex Quad[] =
{
{-5.0f, 5.0f, 0, 0, 0}, // 1
{ 5.0f, 5.0f, 0, 1.0f, 0}, // 2
{-5.0f, -5.0f, 0, 0, 1.0f}, // 4
{ 5.0f, -5.0f, 0, 1.0f, 1.0f}, // 3
} ;
// Create vertex buffer
HRESULT hr ;
hr = g_pd3dDevice->CreateVertexBuffer(6 * sizeof(Vertex), D3DUSAGE_WRITEONLY,
VertexFVF, D3DPOOL_MANAGED, &g_pVB, NULL) ;
if (FAILED(hr))
{
MessageBoxA(NULL, "Create vertex buffer failed!", "Error", 0) ;
}
// Copy data
Vertex* v ;
g_pVB->Lock(0, 0, (void**)&v, 0) ;
memcpy(v, Quad, 6 * sizeof(Vertex)) ;
g_pVB->Unlock() ;
}
VOID Cleanup()
{
SAFE_RELEASE(g_pTexture) ;
SAFE_RELEASE(g_pVB) ;
SAFE_RELEASE(g_pd3dDevice) ;
SAFE_RELEASE(g_pD3D) ;
}
void SetupMatrix(float timeDelta)
{
g_totalShowTime += timeDelta;
if(g_totalShowTime > g_ShowTimeInterval)
{
g_worldMatrix._41 += timeDelta * 10;
g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix) ;
}
else
{
D3DXMatrixTranslation(&g_worldMatrix, 0.0f, 0.0f, 0.0f) ;
g_pd3dDevice->SetTransform(D3DTS_WORLD, &g_worldMatrix) ;
}
// set view
D3DXVECTOR3 eyePt(0.0f, 0.0f, -15.0f) ;
D3DXVECTOR3 upVec(0.0f, 1.0f, 0.0f) ;
D3DXVECTOR3 lookCenter(0.0f, 0.0f, 0.0f) ;
D3DXMATRIX view ;
D3DXMatrixLookAtLH(&view, &eyePt, &lookCenter, &upVec) ;
g_pd3dDevice->SetTransform(D3DTS_VIEW, &view) ;
// set projection
D3DXMATRIX proj ;
D3DXMatrixPerspectiveFovLH(&proj, D3DX_PI / 4, 1.0f, 1.0f, 1000.0f) ;
g_pd3dDevice->SetTransform(D3DTS_PROJECTION, &proj) ;
}
void SetupTexture()
{
// Create Texture
HRESULT hr ;
hr = D3DXCreateTextureFromFile(g_pd3dDevice, "../Common/Media/crate.jpg", &g_pTexture) ;
if (FAILED(hr))
{
MessageBoxA(NULL, "Create Texture failed!", "Error", 0) ;
}
// Setup texture
g_pd3dDevice->SetTexture(0, g_pTexture) ;
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP );
g_pd3dDevice->SetSamplerState( 0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP );
}
void RenderQuad()
{
SetupTexture();
// Set stream source
g_pd3dDevice->SetStreamSource(0, g_pVB, 0, sizeof(Vertex) );
g_pd3dDevice->SetFVF(VertexFVF) ;
g_pd3dDevice->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2) ;
}
VOID Render(float timeDelta)
{
SetupMatrix(timeDelta) ;
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET, 0, 1.0f, 0 );
// Begin the scene
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
RenderQuad() ;
// End the scene
g_pd3dDevice->EndScene();
}
// Present the back-buffer contents to the display
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
switch( msg )
{
case WM_KEYDOWN:
{
switch( wParam )
{
case VK_ESCAPE:
SendMessage( hWnd, WM_CLOSE, 0, 0 );
break ;
default:
break ;
}
}
break ;
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
}
INT WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR szCmdLine, int iCmdShow)
{
WNDCLASSEX winClass ;
winClass.lpszClassName = "ScreenQuad";
winClass.cbSize = sizeof(WNDCLASSEX);
winClass.style = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc = MsgProc;
winClass.hInstance = hInstance;
winClass.hIcon = NULL ;
winClass.hIconSm = NULL ;
winClass.hCursor = LoadCursor(NULL, IDC_ARROW) ; // to avoid busy cursor
winClass.hbrBackground = NULL ;
winClass.lpszMenuName = NULL ;
winClass.cbClsExtra = 0;
winClass.cbWndExtra = 0;
RegisterClassEx (&winClass) ;
HWND hWnd = CreateWindowEx(NULL,
winClass.lpszClassName, // window class name
"ScreenQuad", // window caption
WS_OVERLAPPEDWINDOW, // window style
32, // initial x position
32, // initial y position
600, // initial window width
600, // initial window height
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL) ; // creation parameters
// Create window failed
if(hWnd == NULL)
{
MessageBoxA(hWnd, "Create Window failed!", "Error", 0) ;
return -1 ;
}
// Initialize Direct3D
if( SUCCEEDED(InitD3D(hWnd)))
{
InitVB() ;
// Show the window
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
// Enter the message loop
MSG msg ;
ZeroMemory( &msg, sizeof(msg) );
PeekMessage( &msg, NULL, 0U, 0U, PM_NOREMOVE );
static DWORD lastTime = timeGetTime();
while (msg.message != WM_QUIT)
{
if( PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE) != 0)
{
TranslateMessage (&msg) ;
DispatchMessage (&msg) ;
}
else // Render the game if there is no message to process
{
DWORD currentTime = timeGetTime();
float timeDelta = (currentTime - lastTime) * 0.001f;
Render(timeDelta) ;
lastTime = currentTime;
}
}
}
UnregisterClass(winClass.lpszClassName, hInstance) ;
return 0;
}