将 DirectX 精灵移出屏幕时如何防止其被擦除

Posted

技术标签:

【中文标题】将 DirectX 精灵移出屏幕时如何防止其被擦除【英文标题】:How do I prevent a DirectX sprite from being erased when moving it outside of the screen 【发布时间】:2013-07-05 08:01:56 【问题描述】:

所以我试图学习如何在 directX 中制作精灵并下载了一个示例文件,但是示例中存在一个错误,我无法克服并且必须知道如何修复,只要附加到窗口的精灵是移出屏幕并返回离开屏幕的精灵部分不会重新绘制,直到我释放鼠标左键。我阅读了 RenderFrame() 函数中的所有内容,希望找到一些设置来更改但没有成功。

Here 是该问题的示例动画(由于某种奇怪的原因,我在我的视频上找不到任何没有 502 错误的文件共享服务。)

这里是源代码:

// main.cpp : Defines the entry point for the application.
//
#include <windows.h>
#include "C:\Program Files\Microsoft DirectX SDK (August 2008)\Include\D3dx9core.h"
#include "C:\Documents and Settings\Death\My Documents\Downloads\DXSprite\DXSprite\resource.h"

//-----------------------------------------------------------------------------
// GLOBALS
//-----------------------------------------------------------------------------
HWND g_hWnd = NULL;
LPDIRECT3D9 g_pD3D = NULL;
LPDIRECT3DDEVICE9 g_pD3DDevice = NULL;
ID3DXSprite * g_pD3DXSprite = NULL;
LPDIRECT3DTEXTURE9 g_pTexture = NULL;
const int SCREEN_WIDTH = 800;
const int SCREEN_HEIGHT = 600;

//-----------------------------------------------------------------------------
// PROTOTYPES
//-----------------------------------------------------------------------------
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
               LPSTR lpCmdLine, int nCmdShow);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HRESULT InitializeD3D       ( );
void RenderFrame            ( );

//-----------------------------------------------------------------------------
//    Name: WinMain()
// Desc: The application's entry point
//-----------------------------------------------------------------------------
int WINAPI WinMain( HINSTANCE hInstance,
                HINSTANCE hPrevInstance,
                LPSTR     lpCmdLine,
                int       nCmdShow )

WNDCLASSEX  winClass;
MSG         uMsg;
HRESULT     hr;

memset(&uMsg,0,sizeof(uMsg));

winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize        = sizeof(WNDCLASSEX);
winClass.style         = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc   = WindowProc;
winClass.hInstance     = hInstance;
winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
winClass.lpszMenuName  = NULL;
winClass.cbClsExtra    = 0;
winClass.cbWndExtra    = 0;

if( !RegisterClassEx(&winClass) )
    return E_FAIL;

g_hWnd = CreateWindowEx( NULL, "MY_WINDOWS_CLASS",
                         "Direct3D 9 - ID3DXSprite Example",
                         WS_OVERLAPPEDWINDOW | WS_VISIBLE,
                         0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, NULL, NULL, hInstance, NULL );

if( g_hWnd == NULL )
    return E_FAIL;

ShowWindow( g_hWnd, nCmdShow );
UpdateWindow( g_hWnd );

//----------------------------------------------------------------
// Create the DirectX device
//----------------------------------------------------------------
if (FAILED( InitializeD3D()))
    return 0;


//----------------------------------------------------------------
// CREATE THE ID3DXSprite
//----------------------------------------------------------------

// Create the ID3DXSprite interface object
hr = D3DXCreateSprite(g_pD3DDevice, &g_pD3DXSprite );
if( FAILED(hr) )
    return hr;


//----------------------------------------------------------------
// LOAD THE TEXTURE FOR THE SPRITE
//----------------------------------------------------------------

// --------------------------------------------------------
// Load the texture.  I decided to use the extended
// version of the texture loading function just to show what
// it would look like.
// The texture was created with Photoshop with a transparent
// background to start with.  Then line cross hairs were added.
//
// Note - If you don't use a texture image that has a power of
// 2 size for the width or height then the image may not load
// properly.  This image is 256x256.
//
D3DXCreateTextureFromFileEx(
    g_pD3DDevice,
    "C:\\Documents and Settings\\Death\\My Documents\\Downloads\\DXSprite\\DXSprite\\sarah.jpg",                // Our texture image!
    D3DX_DEFAULT,               // width
    D3DX_DEFAULT,               // height
    D3DX_DEFAULT,               // MIP levels
    0,                          // usage
    D3DFMT_DXT1,                // texture format
    D3DPOOL_MANAGED,            // mem pool
    D3DX_DEFAULT,               // filter
    D3DX_DEFAULT,               // MIP filter
    0,                          // transparent color key
    NULL,                       // image info struct
    NULL,                       // palette
    &g_pTexture);               // the returned texture, if success

if ( FAILED(hr) )
    return hr;





// ---------
// Main Loop
// ---------
while( uMsg.message != WM_QUIT )

RenderFrame();
    if( PeekMessage( &uMsg, NULL, 0, 0, PM_REMOVE ) )
    
        TranslateMessage( &uMsg );
        DispatchMessage( &uMsg );
    


// -------------------------
// Release directx resources
// -------------------------
if (g_pD3DXSprite)

    g_pD3DXSprite->Release();
    g_pD3DXSprite = NULL;


if (g_pTexture)

    g_pTexture->Release();
    g_pTexture = NULL;


if (g_pD3DDevice)

    g_pD3DDevice->Release();
    g_pD3DDevice = NULL;





UnregisterClass( "MY_WINDOWS_CLASS", winClass.hInstance );

return (int)uMsg.wParam;


//-----------------------------------------------------------------------------
// Name: WindowProc()
// Desc: The window's message handler
//-----------------------------------------------------------------------------
LRESULT CALLBACK WindowProc( HWND   hWnd,
                         UINT   msg,
                         WPARAM wParam,
                         LPARAM lParam )


switch( msg )

    case WM_KEYDOWN:
    
        switch( wParam )
        
            case VK_ESCAPE:
                PostQuitMessage(0);
                break;

        
    
    break;

    case WM_CLOSE:
    
        PostQuitMessage(0);
    

    case WM_DESTROY:
    
        PostQuitMessage(0);
    
    break;

    default:
    
        return DefWindowProc( hWnd, msg, wParam, lParam );
    
    break;


return 0;


//-----------------------------------------------------------------------------
// Name: InitializeD3D()
// Desc: Create DirectX interface objects 
//       Initialize the view matrix.
//       Setup render states that will not need changing throughout
//       the life of the application.
//-----------------------------------------------------------------------------
HRESULT InitializeD3D( ) 

HRESULT hr;

// Create a direct 3D interface object
g_pD3D = Direct3DCreate9( D3D_SDK_VERSION );

if( g_pD3D == NULL )

    // TO DO: Respond to failure of Direct3DCreate9
    return E_FAIL;


D3DDISPLAYMODE d3ddm;

if( FAILED( hr = g_pD3D->GetAdapterDisplayMode( D3DADAPTER_DEFAULT, &d3ddm ) ) )

    // TO DO: Respond to failure of GetAdapterDisplayMode
    return hr;



//
if( FAILED( hr = g_pD3D->CheckDeviceFormat( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL,
                                            d3ddm.Format, D3DUSAGE_DEPTHSTENCIL,
                                            D3DRTYPE_SURFACE, D3DFMT_D16 ) ) )

    if( hr == D3DERR_NOTAVAILABLE )
        // POTENTIAL PROBLEM: We need at least a 16-bit z-buffer!
        return hr;


//
// Do we support hardware vertex processing? If so, use it.
// If not, downgrade to software.
//

D3DCAPS9 d3dCaps;

if( FAILED( hr = g_pD3D->GetDeviceCaps( D3DADAPTER_DEFAULT,
                                   D3DDEVTYPE_HAL, &d3dCaps ) ) )

    // TO DO: Respond to failure of GetDeviceCaps
    return hr;


DWORD dwBehaviorFlags = 0;

if( d3dCaps.VertexProcessingCaps != 0 )
    dwBehaviorFlags |= D3DCREATE_HARDWARE_VERTEXPROCESSING;
else
    dwBehaviorFlags |= D3DCREATE_SOFTWARE_VERTEXPROCESSING;

//
// Everything checks out - create a simple, windowed device.
//

D3DPRESENT_PARAMETERS d3dpp;
memset(&d3dpp, 0, sizeof(d3dpp));

d3dpp.BackBufferFormat       = d3ddm.Format;
d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
d3dpp.Windowed               = TRUE;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_IMMEDIATE;

// Attempt to create a HAL device, end app on failure just to keep things
// simple.  In other words we are not trying to create a REF device if the
// HAL fails.
if( FAILED( hr = g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hWnd,
                                  dwBehaviorFlags, &d3dpp, &g_pD3DDevice ) ) )

    // Should respond to failure of creating the hardware device.
    return hr;



// If we get here everything worked!
return S_OK;



//-----------------------------------------------------------------------------
// Name: RenderFrame()
// Desc: Draw the image to the framebuffer.
//-----------------------------------------------------------------------------
void RenderFrame( )

if (!g_pD3DDevice && !g_pD3DXSprite && !g_pTexture)
    return;


// Clear the frame & depth buffer ready for drawing (Black color)
g_pD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,  0x00000000, 1.0f, 0 );

g_pD3DDevice->BeginScene();

    //-------------------------
    // Render the sprite
    //

    D3DXVECTOR3 vecPos = D3DXVECTOR3(0,0,0);

    if (g_pD3DXSprite && g_pTexture)
    
        g_pD3DXSprite->Begin( D3DXSPRITE_ALPHABLEND );
        g_pD3DXSprite->Draw(g_pTexture, NULL, NULL, &vecPos, 0xffffffff);
        g_pD3DXSprite->End();
    



g_pD3DDevice->EndScene();


// Frame buffer to Front buffer
g_pD3DDevice->Present( NULL, NULL, NULL, NULL );


【问题讨论】:

示例链接给出了 502 Bad Gateway 【参考方案1】:

This answer 也是您问题的答案。您可以通过以下代码 (#include &lt;ctime&gt;) 输出循环中的当前时间来验证消息循环是否在拖动窗口时运行:

// ---------
// Main Loop
// ---------
while( uMsg.message != WM_QUIT )

    std::time_t time = std::time(0);
    OutputDebugStringA(ctime(&time));
    // ... 

答案如下:

在窗口上发生了许多模态操作。 Win32 模态操作是指通过启动自己的事件处理循环直到模式完成将应用程序置于“模式”的功能。常见的应用模式包括拖放操作、移动/调整大小操作、任何时候弹出需要输入才能继续应用的对话框。

所以发生的事情是:您的消息循环没有运行。您的窗口收到了您传递给 DefWindowProc 的 WM_LBUTTONDOWN 消息。 DefWindowProc 确定用户正在尝试以交互方式调整窗口大小或移动窗口,并输入了调整大小/移动模式函数。此函数在消息处理循环中监视鼠标消息,以便它可以拦截它们以提供交互式大小调整体验,并且仅在调整大小操作完成时退出 - 通常通过用户释放按住的按钮或按 Escape。

您会收到通知 - DefWindowProc 在进入和退出模式事件处理循环时会发送 WM_ENTERSIZEMOVE 和 WM_EXITSIZEMOVE 消息。

要继续生成“空闲”消息,通常在调用模态函数之前创建一个计时器 (SetTimer) - 或者当收到 DefWindowProc 正在进入模态函数的消息时 - 模态循环将继续调度 WM_TIMER 消息...并从计时器消息处理程序调用空闲过程。模态函数返回时销毁定时器。

原答案:

我看到一些 DX 应用程序显示了这种行为,而另一些则没有。不仅仅是精灵,从屏幕外拖出的整个矩形都是黑色的。这意味着这是一个窗口重绘问题。

尝试在 WindowProc 中拦截 WM_MOVING 并调用 RedrawWindow

【讨论】:

@user2462027 不,这不是我的想法,我没有可以尝试的 DX 开发环境:-\ @user2462027 如果你还没有解决这个问题,我终于有时间在家里尝试代码并弄明白了。查看我的更新答案【参考方案2】:

当您使用 DirectX / OpenGL / GDI+ 覆盖 Win32 窗口时,您将忽略 GDI 绘制。将一个窗口拖离屏幕会向离开屏幕的窗口区域发送 WM_PAINT 和 WM_ERASEBKGND 消息。 WM_ERASEBKGND 覆盖 ID3DXSprite,清除窗口类的 hbrBackground 成员的区域。

解决方法: 将 WNDCLASS(EX) 中的 hbrBackground 设置为 GetStockObject(NULL_BRUSH)。可以这么说,空画笔是“空”画笔。它会导致 WM_ERASEBKGND 消息被忽略,从而防止精灵被擦除。

代码:

winClass.lpszClassName = "MY_WINDOWS_CLASS";
winClass.cbSize        = sizeof(WNDCLASSEX);
winClass.style         = CS_HREDRAW | CS_VREDRAW;
winClass.lpfnWndProc   = WindowProc;
winClass.hInstance     = hInstance;
winClass.hIcon         = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hIconSm       = LoadIcon(hInstance, (LPCTSTR)IDC_DXSPRITE);
winClass.hCursor       = LoadCursor(NULL, IDC_ARROW);
winClass.hbrBackground = (HBRUSH)GetStockObject(NULL_BRUSH);
winClass.lpszMenuName  = NULL;
winClass.cbClsExtra    = 0;
winClass.cbWndExtra    = 0;

其他一切都保持不变。 :)

【讨论】:

以上是关于将 DirectX 精灵移出屏幕时如何防止其被擦除的主要内容,如果未能解决你的问题,请参考以下文章

SharedPrefs 似乎已被擦除,即使我使用它们来自动登录

即使在手机被擦除后也能跟踪 iOS 设备

ajax请求后php会话值被擦除

Java泛型之类型未被擦除

将自定义类分配给控制器后,Xcode Table View 被擦除

通过 SetPixel 设置的 C++ 像素在调整控制台大小或将其移出屏幕时消失