D3D9 2D动画中的奇怪之处 - 高FPS但仍然不流畅



动画很简单:全屏显示图片,并在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;
}

相关内容

  • 没有找到相关文章

最新更新