1

I'm working on a 2D game using Direct3D 10's ID3DX10Sprite interface. It works pretty well except for that the textures are filtered using a linear algorithm (I think?), which makes them look pretty ugly when you scale them.

Original texture (32 x 32):

Original 32 x 32 texture

What it looks like scaled up in-game:

What it currenty looks like

What I want it to look like:

What I want it to look like


So my question is: Is there a way to use Nearest Neighbour filtering (aka Point filtering) for the sprites, and how do you do that?

This is my code:

Initialization:

float width = 818.0F;
float height = 646.0F;

IDXGISwapChain*             swapChain;
ID3D10Device*               device      = Direct3D_CreateDevice(hWnd, swapChain, (int)width, (int)height);
ID3D10RenderTargetView*     rtv         = Direct3D_CreateRenderTargetView(device, swapChain);
ID3DX10Sprite*              mainSprite  = Direct3D_CreateMainSpriteObject(device);
ID3D10ShaderResourceView*   texture     = Direct3D_CreateTexture(device, "C:\\Users\\Vincent\\Documents\\visual studio 2010\\Projects\\DirectX Test C++\\Debug\\base_grass.png", 32, 32);
D3DX10_SPRITE*              sprite      = Direct3D_CreateSprite(texture, 0.0F, 0.0F, 1.0F, 1.0F); //800.0F / 64.0F, 600.0F / 64.0F);

Direct3D_CreateViewport(device, 0, 0, (UINT)width, (UINT)height);

Rendering:

FLOAT* backColor = new FLOAT[4];
backColor[0] = 0.0F;
backColor[1] = 0.5F;
backColor[2] = 0.0F;
backColor[3] = 1.0F;

device->ClearRenderTargetView(rtv, backColor);
device->Draw(3, 0);

Direct3D_DrawSpritesBuffered(mainSprite, sprite, 1);
swapChain->Present(0, 0);

Direct3D functions:

/////////////////////////////////////////////////
//            Direct3D_CreateDevice            //
/////////////////////////////////////////////////
ID3D10Device * __stdcall Direct3D_CreateDevice(HWND hWnd, IDXGISwapChain* &swapChain, int width, int height)
{
    //Variables.
    ID3D10Device* D3DDevice;
    DXGI_SWAP_CHAIN_DESC swapChainDescription;

    ZeroMemory(&swapChainDescription, sizeof(DXGI_SWAP_CHAIN_DESC));

    //Buffer settings.
    swapChainDescription.BufferCount                            = 1;
    swapChainDescription.BufferDesc.Format                      = DXGI_FORMAT_R8G8B8A8_UNORM;
    swapChainDescription.BufferDesc.Width                       = width;
    swapChainDescription.BufferDesc.Height                      = height;
    swapChainDescription.BufferDesc.RefreshRate.Numerator       = 60;
    swapChainDescription.BufferDesc.RefreshRate.Denominator     = 1;
    swapChainDescription.BufferUsage                            = DXGI_USAGE_RENDER_TARGET_OUTPUT;

    //Misc.
    swapChainDescription.Flags                                  = DXGI_SWAP_CHAIN_FLAG_ALLOW_MODE_SWITCH;
    swapChainDescription.OutputWindow                           = hWnd;
    swapChainDescription.SampleDesc.Count                       = 1;
    swapChainDescription.SampleDesc.Quality                     = 0;
    swapChainDescription.SwapEffect                             = DXGI_SWAP_EFFECT_DISCARD;
    swapChainDescription.Windowed                               = TRUE;

    //Try to create the device and SwapChain.
    if (FAILED(D3D10CreateDeviceAndSwapChain(NULL,
                                             D3D10_DRIVER_TYPE_HARDWARE,
                                             NULL,
                                             D3D10_CREATE_DEVICE_DEBUG,
                                             D3D10_SDK_VERSION,
                                             &swapChainDescription,
                                             &swapChain,
                                             &D3DDevice))) return NULL;
    return D3DDevice;
}

/////////////////////////////////////////////////
//       Direct3D_CreateRenderTargetView       //
/////////////////////////////////////////////////
ID3D10RenderTargetView * __stdcall Direct3D_CreateRenderTargetView(ID3D10Device* device, IDXGISwapChain* swapChain)
{
    //Variables.
    HRESULT hRes = 0;
    ID3D10Texture2D* backBuffer;
    ID3D10RenderTargetView* renderTargetView;

    //Get the back buffer.
    hRes = swapChain->GetBuffer(0, __uuidof(ID3D10Texture2D), (LPVOID*)&backBuffer);
    if(FAILED(hRes)) { return NULL; }

    //Try to create the RenderTargetView.
    hRes = device->CreateRenderTargetView(backBuffer, NULL, &renderTargetView);
    if(FAILED(hRes)) { return NULL; }

    //Release the back buffer
    backBuffer->Release();

    //Set the render target
    device->OMSetRenderTargets(1, &renderTargetView, NULL);

    return renderTargetView;
}

/////////////////////////////////////////////////
//           Direct3D_CreateViewport           //
/////////////////////////////////////////////////
void __stdcall Direct3D_CreateViewport(ID3D10Device* device, int x, int y, UINT width, UINT height)
{
    D3D10_VIEWPORT* viewport = new D3D10_VIEWPORT();

    viewport->TopLeftX = x;
    viewport->TopLeftY = y;
    viewport->Width = width;
    viewport->Height = height;
    viewport->MinDepth = 0.0F;
    viewport->MaxDepth = 1.0F;

    device->RSSetViewports(1, viewport);
}

/////////////////////////////////////////////////
//       Direct3D_CreateMainSpriteObject       //
/////////////////////////////////////////////////
ID3DX10Sprite * __stdcall Direct3D_CreateMainSpriteObject(ID3D10Device* device)
{
    //Create the sprite object.
    ID3DX10Sprite* s;
    HRESULT hRes = D3DX10CreateSprite(device, 4096, &s);

    if(FAILED(hRes)) { return NULL; }

    //Construct the Projection- and ViewTransform matrix.
    D3DXMATRIX matview;
    matview._12 = 0.0F;
    matview._13 = 0.0F;
    matview._14 = 0.0F;
    matview._21 = 0.0F;
    matview._23 = 0.0F;
    matview._24 = 0.0F;
    matview._31 = 0.0F;
    matview._32 = 0.0F;
    matview._34 = 0.0F;
    matview._41 = 0.0F;
    matview._42 = 0.0F;
    matview._43 = 0.0F;

    matview._11 = 1.0F;
    matview._22 = 1.0F;
    matview._33 = 1.0F;
    matview._44 = 1.0F;

    //Set the Projection- and ViewTransforms.
    s->SetProjectionTransform(&matview);
    s->SetViewTransform(&matview);

    return s;
}

/////////////////////////////////////////////////
//        Direct3D_DrawSpritesBuffered         //
/////////////////////////////////////////////////
void __stdcall Direct3D_DrawSpritesBuffered(ID3DX10Sprite* spriteObject, D3DX10_SPRITE* sprites, int count)
{
    spriteObject->Begin(0);
    spriteObject->DrawSpritesBuffered(sprites, count);
    spriteObject->Flush();
    spriteObject->End();
}

/////////////////////////////////////////////////
//           Direct3D_CreateTexture            //
/////////////////////////////////////////////////
ID3D10ShaderResourceView * __stdcall Direct3D_CreateTexture(ID3D10Device* device, LPCSTR file, int width, int height)
{
    //Variables.
    D3DX10_IMAGE_LOAD_INFO imgLoadInfo;
    ID3D10ShaderResourceView * shaderResourceView;

    ZeroMemory(&imgLoadInfo, sizeof(imgLoadInfo));

    //Image load settings.
    imgLoadInfo.BindFlags = D3D10_BIND_SHADER_RESOURCE;
    imgLoadInfo.CpuAccessFlags = 0;
    imgLoadInfo.Filter = D3DX10_FILTER_NONE;
    imgLoadInfo.FirstMipLevel = 0;
    imgLoadInfo.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
    imgLoadInfo.MipFilter = D3DX10_FILTER_NONE;
    imgLoadInfo.MipLevels = 1;
    imgLoadInfo.MiscFlags = 0;
    imgLoadInfo.Usage = D3D10_USAGE_DEFAULT;

    //Get the source image's info.
    imgLoadInfo.pSrcInfo = new D3DX10_IMAGE_INFO();
    D3DX10GetImageInfoFromFileA(file, NULL, imgLoadInfo.pSrcInfo, NULL);

    //Set the texture dimensions.
    imgLoadInfo.Width = width;
    imgLoadInfo.Height = height;

    HRESULT hRes;

    //Attempt to create the ShaderResourceView.
    if(FAILED(D3DX10CreateShaderResourceViewFromFile(device, file, &imgLoadInfo, NULL, &shaderResourceView, &hRes)))
    {
        return NULL;
    }

    return shaderResourceView;
}

/////////////////////////////////////////////////
//            Direct3D_CreateSprite            //
/////////////////////////////////////////////////
D3DX10_SPRITE * __stdcall Direct3D_CreateSprite(ID3D10ShaderResourceView* texture, float textureX, float textureY, float textureWidth, float textureHeight)
{
    //Variables.
    D3DX10_SPRITE* sprite = new D3DX10_SPRITE();

    //Color settings.
    sprite->ColorModulate.r = 1.0f;
    sprite->ColorModulate.g = 1.0f;
    sprite->ColorModulate.b = 1.0f;
    sprite->ColorModulate.a = 1.0f;

    //Texture settings.
    sprite->pTexture = texture;
    sprite->TextureIndex = 0;

    sprite->TexCoord.x = textureX;
    sprite->TexCoord.y = textureY;
    sprite->TexSize.x = textureWidth;
    sprite->TexSize.y = textureHeight;

    //Dimension and location matrix.
    sprite->matWorld._12 = 0.0F;
    sprite->matWorld._13 = 0.0F;
    sprite->matWorld._14 = 0.0F;
    sprite->matWorld._21 = 0.0F;
    sprite->matWorld._23 = 0.0F;
    sprite->matWorld._24 = 0.0F;
    sprite->matWorld._31 = 0.0F;
    sprite->matWorld._32 = 0.0F;
    sprite->matWorld._34 = 0.0F;
    sprite->matWorld._41 = 0.0F;
    sprite->matWorld._42 = 0.0F;
    sprite->matWorld._43 = 0.0F;

    sprite->matWorld._11 = 1.0F;
    sprite->matWorld._22 = 1.0F;
    sprite->matWorld._33 = 1.0F;
    sprite->matWorld._44 = 1.0F;

    return sprite;
}
Visual Vincent
  • 18,045
  • 5
  • 28
  • 75
  • 1
    There's no reason to use Direct3D 10. It's been fully replaced by DirectX 11 on all the same platforms, has more better hardware support, and a significant selection of open source support libraries. D3DX, D3DX10, and D3DX11 are all deprecated, as is the legacy DirectX SDK itself. If you were using DirectX 11, then ``SpriteBatch`` in the [DirectX Tool Kit](https://github.com/Microsoft/DirectXTK/wiki) is a much better choice than using legacy D3DX10Sprite and provides the ability to set the sampler type. See [MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/ee663275.aspx). – Chuck Walbourn Jun 07 '17 at 05:37
  • 1
    The good news is that it's easy to port from Direct3D 10 to Direct3D 11. See [this article](https://msdn.microsoft.com/en-us/library/windows/desktop/ff476190(v=vs.85).aspx#direct3d_10_to_direct3d_11). Converting from legacy D3DXMath to DirectXMath (a.k.a XNAMath) is covered [here](https://msdn.microsoft.com/en-us/library/windows/desktop/ff729728(v=vs.85).aspx). By dropping D3DX/D3DX10/D3DX11, you also greatly simplify your game deployment. See [Not So Direct Setup](https://blogs.msdn.microsoft.com/chuckw/2010/09/08/not-so-direct-setup/) – Chuck Walbourn Jun 07 '17 at 05:38

1 Answers1

2

Legacy D3DX10_SPRITE only supports using a single sampler:

D3D10_SAMPLER_DESC splDesc;
ZeroMemory(&splDesc, sizeof(D3D10_SAMPLER_DESC));
splDesc.Filter = D3D10_FILTER_MIN_MAG_MIP_LINEAR;
splDesc.AddressU = D3D10_TEXTURE_ADDRESS_CLAMP;
splDesc.AddressV = D3D10_TEXTURE_ADDRESS_CLAMP;
splDesc.AddressW = D3D10_TEXTURE_ADDRESS_CLAMP;
splDesc.ComparisonFunc = D3D10_COMPARISON_NEVER;
splDesc.MaxLOD = FLT_MAX;
VH( m_pDevice->CreateSamplerState(&splDesc, &m_pSampler) );

It also does not provide any overloading/custom state mechanism.

SpriteBatch in the DirectX Tool Kit for DirectX 11 does provide the ability to set which sampler state to use and provides hooks for custom state call-backs:

void Begin(SpriteSortMode sortMode = SpriteSortMode_Deferred,
    ID3D11BlendState* blendState = nullptr,
    ID3D11SamplerState* samplerState = nullptr,
    ID3D11DepthStencilState* depthStencilState = nullptr,
    ID3D11RasterizerState* rasterizerState = nullptr,
    std::function<void __cdecl()> setCustomShaders = nullptr,
    XMMATRIX transformMatrix = MatrixIdentity);

The most sensible solution is to port from Direct3D 10 to Direct3D 11 and stop using legacy Direct3D 10.

If there's some particularly compelling reason why you have to stay on Direct3D 10, then you can take a look at SpriteBatch.h / SpriteBatch.cpp which you could copy out and back-port to Direct3D 10.

See MSDN, Where is the DirectX SDK (2015 Edition?), and Living without D3DX

Chuck Walbourn
  • 38,259
  • 2
  • 58
  • 81
  • Thank you for the answer! I had played around a little with the sampler before but I didn't get anything to work. Sadly though your code doesn't seem to work for me, so I'm taking your advice and porting to DX11 instead! Thanks! – Visual Vincent Jun 07 '17 at 19:09
  • I'm using Visual Studio 2010 (I know it's old too), can I still use DirectXTK or at least its `SpriteBatch` class (by just copying the files) in that IDE? Also, when I import `d3d11.h` it says it's located in `Microsoft SDKs\Windows\v7.0A`, is that okay or is that version too old? (I have the Windows 8 `v8.0A` SDK as well but my OS is Windows 7) -- Sorry for the many questions, I just don't see those minor ones fit for a whole new SO question. :) – Visual Vincent Jun 07 '17 at 19:42
  • Firstly, you can just move to VS 2017 Community instead of sticking with the old version of Visual Studio. If you can't do that, you could use an older [DirectX Tool Kit](https://github.com/Microsoft/DirectXTK/releases/tag/jul2015) which will build with VS 2010 SP1. It uses the [Windows 8.1 SDK](http://go.microsoft.com/fwlink/?LinkID=323507) via an included ``.props`` file. If you plan to mix the legacy DirectX SDK with the Windows 8.1 SDK, you can do so but need to review your include/lib path order. See [MSDN](https://msdn.microsoft.com/en-us/library/windows/desktop/ee663275(v=vs.85).aspx). – Chuck Walbourn Jun 07 '17 at 22:52
  • I prefer VS2010 because I don't like the new flat design of the newer versions (which is the same reason I still use Windows 7, although it isn't out of date _yet_). And no, I'm not planning to mix the SDKs, but thanks for the info! -- One last thing: Is the use of `ComPtr` necessary? I'm asking because this is a multilanguage project. I'm coding the DX code in a C++ DLL which I then P/Invoke in VB.NET. I don't know if it's possible to map a `ComPtr` to a VB.NET type, but if I can use regular pointers then I can map them to `IntPtr`. – Visual Vincent Jun 07 '17 at 23:09
  • I make extensive use of ``Microsoft::WRL::ComPtr`` as a C++ smart pointer which greatly simplifies the life-time management of COM objects. It should not have any impact at all on the ability to expose the underlying COM interface. – Chuck Walbourn Jun 08 '17 at 00:09
  • I have now successfully ported my project to DX 11 and DXTK, and it works like a charm: [(screenshot)](https://i.stack.imgur.com/9pR9L.png). Thank you once again for your great help! – Visual Vincent Jun 11 '17 at 18:03