Анимация довольно проста: вывести изображение на весь экран и переместить изображение за горизонтальное положение за 1 секунду, как эффект переключения слайд-шоу в MS PowerPoint.
Я использую поверхность D3D9 для реализации анимации, потому что я хочу, чтобы программа была совместима с Windows XP, и мне также могут понадобиться некоторые 3D-эффекты отображения изображений.
Когда я включаю VSYNC (d3dpp.PresentationInterval = 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()
, Это может вызвать эти проблемы (см. MSDN timeGetTime ()), потому что при высоком FPS кадр может быть короче миллисекунды. Ты можешь попробовать QueryPerformanceCounter()
чтобы получить таймер с более высокой частотой (MSDN для QueryPerformanceCounter ()). Тогда это не должно отставать. Я не могу себе представить, почему зигзаги на скорости 60 кадров в секунду, но с D3DPRESENT_INTERVAL_IMMEDIATE разрывы нормальны, потому что экран не может постоянно обновляться.
У меня есть решение, которое использует мировую матрицу для перевода текстуры, ключевые шаги, как показано ниже
полный код, как показано ниже, вы должны заменить файл текстуры на ваш локальный файл, чтобы увидеть эффект, см. код в функции 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;
}