c++3ddirectx-11spritefont

DirectX::SpriteFont/SpriteBatch prevents 3D scene from drawing


I have a problem using DirectX::SpriteFont/DirectX::SpriteBatch (from DirectXTK; exactly the same problem as discussed here: Problems when drawing text using the SpriteFont class).

void DrawScene(void)
{
    HRESULT hr;
    
    float bgColor_a[4] = { 0.0f, 0.4f, 0.8f, 0.0f };

    g_pDeviceContext->ClearRenderTargetView(g_pRenderTargetView, bgColor_a);
    g_pDeviceContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);

    XMMATRIX cameraProj = XMLoadFloat4x4(&g_camera._cameraProjection);
    XMVECTOR pos = XMLoadFloat3(&g_camera._pos);
    XMVECTOR target = XMLoadFloat3(&g_camera._target);
    XMVECTOR up = XMLoadFloat3(&g_camera._up);
    XMMATRIX cameraView = XMMatrixLookAtLH(pos, target, up);
    XMMATRIX worldBox2 = XMMatrixIdentity() * (XMMatrixTranslation(2.0f, 0.0f, 0.0f) * XMMatrixRotationY(XMConvertToRadians(g_rotBox2)));
    XMMATRIX wvp = worldBox2 * cameraView * cameraProj;
    XMMATRIX transposeWvp = XMMatrixTranspose(wvp);
    XMStoreFloat4x4(&g_constantBufferPerObject._wvp, transposeWvp);
    g_pDeviceContext->UpdateSubresource(g_pConstantBufferPerObject, 0, NULL, &g_constantBufferPerObject, 0, 0);
    g_pDeviceContext->VSSetConstantBuffers(0, 1, &g_pConstantBufferPerObject);

    g_pDeviceContext->PSSetShaderResources(0, 1, &g_pCageTexture);
    g_pDeviceContext->PSSetSamplers(0, 1, &g_pCubeTextureSamplerState);

    // box
    g_pDeviceContext->DrawIndexed(36, 0, 0);
        
    wchar_t buffer[32];
    swprintf_s(buffer, 32, L"%.2f", g_fps._fps);

    //g_pSpriteBatch->Begin();
    //g_pSpriteFont->DrawString(g_pSpriteBatch, buffer, XMFLOAT2(30, 30));
    //g_pSpriteBatch->End();
    
    // Present the backbuffer to the screen
    hr = g_pSwapChain->Present(0, 0);
    if (FAILED(hr))
    {
        ErrorBoxW(L"Cannot present without error.");
    }
}

Without the calls to SpriteBatch::Begin(), SpriteFont::DrawString and SpriteBatch::End() you will see a textured cube rotating through space (a cage). With the calls to the described functions, you will only see the frames per second in the upper left corner, but no rotating cube.

I followed the tutorials from Chuck Walbourn on github DirectXTK and combined the 2 tutorials Draw String using SpriteFont and rendering a primitive 3D object (a simple triangle). In this example I will see the test string drawing over the triangle. But I do not understand why the rotating cube from my example is not visible while drawing a text string using SpriteFont class of DirectXTK and I cannot even find the root cause.

Pixel shader file (PixelShader.hlsl):

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
    float2 TexCoord : TEXCOORD;
};

Texture2D ObjTexture;
SamplerState ObjSamplerState;

float4 PS( VS_OUTPUT input ) : SV_TARGET
{
    float4 diffuse = ObjTexture.Sample(ObjSamplerState, input.TexCoord);
    clip(diffuse.a - 0.25);
    return diffuse;
}

Vertex shader file (VertexShader.hlsl):

cbuffer constantBufferPerObject
{
    float4x4 WVP;
    float ColorAdjust;
    int Mode;
    int Pad[2];
};

struct VS_OUTPUT
{
    float4 Pos : SV_POSITION;
    float4 Color : COLOR;
    float2 TexCoord : TEXCOORD;
};

VS_OUTPUT VS( float4 pos : POSITION, float4 color : COLOR_ZERO, float4 color2 : COLOR_ONE, float2 texCoord : TEXCOORD )
{
    VS_OUTPUT output;
    float4 temp;
    output.Pos = mul(pos, WVP);  
    temp = color;
    output.Color.r = temp.r * color2.r * (1.0f - ColorAdjust);
    output.Color.g = temp.g * color2.g * (1.0f - ColorAdjust);
    output.Color.b = temp.b * color2.b * (1.0f - ColorAdjust);
    output.Color.a = color.a * color2.a;
    output.TexCoord = texCoord;
    return output;
}

To reproduce the issue: I am working with Visual Studio 2015 Community Edition. I added DirectXTK (.lib) and DirectXTex (WICTextureLoader .cpp/.h, DDSTextureLoader .cpp/.h, .lib) to my project. Image for cube is .png with alpha.


Solution

  • First, as you are using DirectXTK, you don't need DirectXTex. DirectXTex is intended for texture processing tools, while DirectXTK texture loaders are more suited to use in applications. See this blog post.

    Second, don't use the ancient timeGetTime function. You can use GetTickCount, although GetTickCount64 is better as it avoids overflow problems. For frame timing, however, these are not really precise enough. You should take a look at the StepTimer.h used in the DirectX Tool Kit tutorials.

    Third, you should look at using Microsoft::WRL::ComPtr rather than all the SAFE_RELEASE macro stuff. See this page for details.

    Your code was including xinput.h as well. You should take a look at using DirectX Tool Kit's GamePad instead which has a number of benefits. If nothing else, it uses XInput more robustly than you likely would.

    With all that said, your problem is quite simple: You set the render state up for your scene in InitScene, but drawing anything else changes the state which is exactly what SpriteBatch does. I document which render states each object in DirectX Toolkit manipulates on the wiki.

    You need to set all the state your Draw requires each frame, not assume it is set forever, if you do more than a single draw.

    void DrawScene(void)
    {
        HRESULT hr;
    
        float bgColor_a[4] = { 0.0f, 0.4f, 0.8f, 0.0f };
    
        g_pDeviceContext->ClearRenderTargetView(g_pRenderTargetView, bgColor_a);
        g_pDeviceContext->ClearDepthStencilView(g_pDepthStencilView, D3D11_CLEAR_DEPTH | D3D11_CLEAR_STENCIL, 1.0f, 0);
    
    //>>>> THESE WERE MOVED FROM INITSCENE ABOVE!
        // Set Vertex and Pixel Shaders
        g_pDeviceContext->VSSetShader(g_pVertexShader, 0, 0);
        g_pDeviceContext->PSSetShader(g_pPixelShader, 0, 0);
    
        // Set the vertex buffer
        UINT stride = sizeof(SimpleVertex);
        UINT offset = 0;
        g_pDeviceContext->IASetVertexBuffers(0, 1, &g_pTriangleVertexBuffer, &stride, &offset);
    
        g_pDeviceContext->IASetIndexBuffer(g_pSquareIndexBuffer, DXGI_FORMAT_R32_UINT, 0);
    
        // Set the Input Layout
        g_pDeviceContext->IASetInputLayout(g_pVertexLayout);
    
        // Set Primitive Topology
        g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 3 vertices per triangle
                                                                                         //g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_POINTLIST); // 1 vertex per point
                                                                                         //g_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_LINELIST); // 2 vertices per line
    
                                                                                         // Create the Viewport
    
        g_pDeviceContext->RSSetState(g_pNoCullSolid);
    //THESE WERE MOVED FROM INITSCENE ABOVE! <<<<
    
        XMMATRIX cameraProj = XMLoadFloat4x4(&g_camera._cameraProjection);
        XMVECTOR pos = XMLoadFloat3(&g_camera._pos);
        XMVECTOR target = XMLoadFloat3(&g_camera._target);
        XMVECTOR up = XMLoadFloat3(&g_camera._up);
        XMMATRIX cameraView = XMMatrixLookAtLH(pos, target, up);
        XMMATRIX worldBox2 = XMMatrixIdentity() * (XMMatrixTranslation(2.0f, 0.0f, 0.0f) * XMMatrixRotationY(XMConvertToRadians(g_rotBox2)));
        XMMATRIX wvp = worldBox2 * cameraView * cameraProj;
        XMMATRIX transposeWvp = XMMatrixTranspose(wvp);
        XMStoreFloat4x4(&g_constantBufferPerObject._wvp, transposeWvp);
        g_pDeviceContext->UpdateSubresource(g_pConstantBufferPerObject, 0, NULL, &g_constantBufferPerObject, 0, 0);
        g_pDeviceContext->VSSetConstantBuffers(0, 1, &g_pConstantBufferPerObject);
    
        g_pDeviceContext->PSSetShaderResources(0, 1, &g_pCageTexture);
        g_pDeviceContext->PSSetSamplers(0, 1, &g_pCubeTextureSamplerState);
    
        // box
        g_pDeviceContext->DrawIndexed(36, 0, 0);
    
        wchar_t buffer[32];
        swprintf_s(buffer, 32, L"%.2f", g_fps._fps);
    
        g_pSpriteBatch->Begin();
        g_pSpriteFont->DrawString(g_pSpriteBatch, buffer, XMFLOAT2(30, 30));
        g_pSpriteBatch->End();
    
        // Present the backbuffer to the screen
        hr = g_pSwapChain->Present(0, 0);
    //>>>> This behavior means the app crashes if you hit a DEVICE_REMOVED or DEVICE_RESET.
        if (FAILED(hr))
        {
            ErrorBoxW(L"Cannot present without error.");
        }
    }
    

    You can generally 'set it and forget it' for the viewport state RSSetViewports which also implicitly sets the scissors state, but a better usage is to set it at the start of each frame when you do your initial Clear. This is the pattern I use in the Direct3D Game Templates.