# Help with Creating 2D Texture Renderer

This topic is 2048 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

Hello everyone! I'm trying to create a 2D texture renderer for my engine and cannot get anything working or to show up.  Here's how it's currently setup.  I have a class called NXSpriteBatch.  When NXSpriteBatch::Initialize() is called, it sets up the vertex buffer.  After the vertex buffer is setup, I render textures using the method below:

bool NXSpriteBatch::Render(ID3D11ShaderResourceView* view, XMFLOAT4 color, XMFLOAT2 position, XMFLOAT2 scale)


In this bool,  I start by updating the vertices based on the texture size:

bool NXSpriteBatch::UpdateVertices(XMFLOAT2 size)
{
float sizeX = (float) size.x / 2;
float sizeY = (float) size.y / 2;

Vertices[0].Position = XMFLOAT3(sizeX, sizeY, 1.0f);
Vertices[1].Position = XMFLOAT3(sizeX, -sizeY, 1.0f);
Vertices[2].Position = XMFLOAT3(-sizeX, -sizeY, 1.0f);
Vertices[3].Position = XMFLOAT3(-sizeX, -sizeY, 1.0f);
Vertices[4].Position = XMFLOAT3(-sizeX, sizeY, 1.0f);
Vertices[5].Position = XMFLOAT3(sizeX, sizeY, 1.0f);

Vertices[0].TextureCoordinate = XMFLOAT2(1.0f, 0.0f);
Vertices[1].TextureCoordinate = XMFLOAT2(1.0f, 1.0f);
Vertices[2].TextureCoordinate = XMFLOAT2(0.0f, 1.0f);

Vertices[3].TextureCoordinate = XMFLOAT2(0.0f, 1.0f);
Vertices[4].TextureCoordinate = XMFLOAT2(0.0f, 0.0f);
Vertices[5].TextureCoordinate = XMFLOAT2(1.0f, 0.0f);

return true;
}


Next, I update the vertex buffer:

bool NXSpriteBatch::UpdateVertexBuffer()
{
if (OriginalVertices[0] == Vertices[0] || OriginalVertices[1] == Vertices[1] || OriginalVertices[2] == Vertices[2]
|| OriginalVertices[3] == Vertices[3] || OriginalVertices[4] == Vertices[4] || OriginalVertices[5] == Vertices[5])
return true;

VertexBuffer.Update(&Vertices);

OriginalVertices = Vertices;

return true;
}


You can check out my NXVertexBuffer code using the links below.  After updating my vertex buffer (hopefully I did it right),I finish off the method by rendering.  Here's the full Render method:

bool NXSpriteBatch::Render(ID3D11ShaderResourceView* view, XMFLOAT4 color, XMFLOAT2 position, XMFLOAT2 scale)
{
//Update Vertices
if (!UpdateVertices(position, NXGetSRVSize(view)))
return false;

//Update Buffer
if (!UpdateVertexBuffer())
return false;

XMMATRIX viewMatrix = XMMatrixIdentity();
XMMATRIX projMatrix; NXGPU::Context->GetOrthoMatrix(projMatrix);
XMMATRIX vp = XMMatrixMultiply(viewMatrix, projMatrix);
XMMATRIX mvp = XMMatrixMultiply(CalculateWorldMatrix(position, scale, 0), vp);
mvp = XMMatrixTranspose(mvp);

NXGPU::DisableZBuffer();

NXGPU::Draw(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST, 6, 0);

return true;
}


I have my ortho matrix setup like this:

XMStoreFloat4x4(&m_orthoMatrix, XMMatrixOrthographicOffCenterLH(0.0f, (float) width, 0.0f, (float) height, snear, sdepth));


Here's all of the code:

NXSpriteBatch(.h | .cpp)

NXVertexBuffer(.h | .cpp)

I know the texture is being loaded correctly because I used it for 3D objects and I also know the texture shader works correctly as well.  I find it funny that I could get deferred renderer working great, but can't get a simple texture to render. LOL. Anyways, any help? Thanks!

Edited by KoldGames

*bump*

##### Share on other sites
This:

 bool NXVertexBuffer::Update(vector<NXVertex>* vertices, vector<unsigned long>* indices)
{
if (vertices)
{
if (FAILED(Core::NXGPU::Map(VertexBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &vMS)))
{
return false;
}

Vertices = (vector<NXVertex>*)vMS.pData;
Vertices = vertices;

Core::NXGPU::Unmap(VertexBuffer, 0);
}

return true;
}

doesn't do what you hope for.
The D3D11_MAPPED_SUBRESOURCE.pData pointer you're given points to where you should writo your data to. Currently you're just copying some pointers around. Use memcpy instead.
Since you're using std::vector instead of arrays - and my C++ is rather rusty - here's a SO entry how to get to the raw data of the elements: How to get std::vector pointer to the raw data?.

Also: Be careful with those pointers. I fear there are dangling pointers and/or memory leaks waiting to happen.

##### Share on other sites
Hey! Using memcopy fixed it but now I am having another problem. Positioning of the textute doesn't seem to work right. Placing it at zero, zero doesn't put it at the top left corner of the screen. It just doesn't show. Using [1, 1] shows it at the top left corner of the screen but increasing either number changes it's position drastically. I'm not able to position it based on pixels. Using [2, 1] sends it over a lot. I am using XMMatrixTransformation2D now. I'll update dropbox when I get home with the new files. Any help? Thanks!

##### Share on other sites
Ok, missed that earlier.

XMStoreFloat4x4(&m_orthoMatrix, XMMatrixOrthographicOffCenterLH(0.0f, (float) width, 0.0f, (float) height, snear, sdepth));


This is actually good if you want to use pixel coordinates. But if you want your top to be at 0 and your bottom at height you need to swap those parameters. Look up the docs, ViewBottom comes before ViewTop.

Not sure if something else is still amiss. Sometimes one forgets to transpose the final matrix before sending to the shader (also depends on your shader and how you compiled it: Check column vs row-major layout of your matrices). Looks like you're doing it right though, but neither do we know what's happening in shader->SetVSConstants(mvp) nor how your shader looks.

By the way: If you're doing this for learning, keep going
Otherwise, use, or at least have a look at DirectX Tool Kit Library which has a sprite renderer.

##### Share on other sites

This is actually good if you want to use pixel coordinates. But if you want your top to be at 0 and your bottom at height you need to swap those parameters. Look up the docs, ViewBottom comes before ViewTop.

Hey! Haha, I didn't notice that.  Although, when I switch them around, my texture is inverted.  I also saw on the docs that ViewTop is the maximum y-value of the view volume.  Is swapping still correct and my vertices just need to be flipped?  Also I updated the files in Dropbox.  I also checked out my rendering code and I am transposing the matrix.  Here is how NXTextureShader is currently setup:

.H

class NXTextureShader : public Graphics::NXShader
{
public:
const string Name()
{
return "Texture";
}

bool SetVSConstants(XMMATRIX& worldMatrix, XMMATRIX& viewMatrix, XMMATRIX& projectionMatrix);
bool SetVSConstants(XMMATRIX& wvp);
bool SetPSConstants(XMVECTOR color, Graphics::NXTexture2D* texture);

private:
D3D11_MAPPED_SUBRESOURCE mappedResource;
ID3D11Buffer* colorBuffer = nullptr;
bool CreateBuffers();
};


.CPP

NXTextureShader::NXTextureShader() : NXShader("../Debug/TextureVS.cso", "../Debug/TexturePS.cso") { if (!CreateBuffers()){ return; } }
bool NXTextureShader::SetVSConstants(XMMATRIX& worldMatrix, XMMATRIX& viewMatrix, XMMATRIX& projectionMatrix)
{
HRESULT result;
Graphics::MatrixConstantBuffer* dataPtr = nullptr;

worldMatrix = XMMatrixTranspose(worldMatrix);
viewMatrix = XMMatrixTranspose(viewMatrix);
projectionMatrix = XMMatrixTranspose(projectionMatrix);

result = NXGPU::GetDeviceContext()->Map(ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}

dataPtr = (Graphics::MatrixConstantBuffer*) mappedResource.pData;

dataPtr->WVP = projectionMatrix * viewMatrix * worldMatrix;

NXGPU::GetDeviceContext()->Unmap(ConstantBuffer, 0);

NXGPU::GetDeviceContext()->VSSetConstantBuffers(0, 1, &ConstantBuffer);

return true;
}
{
HRESULT result;
Graphics::MatrixConstantBuffer* dataPtr = nullptr;

result = NXGPU::GetDeviceContext()->Map(ConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);
if (FAILED(result))
{
return false;
}

dataPtr = (Graphics::MatrixConstantBuffer*) mappedResource.pData;

dataPtr->WVP = wvp;

NXGPU::GetDeviceContext()->Unmap(ConstantBuffer, 0);

NXGPU::GetDeviceContext()->VSSetConstantBuffers(0, 1, &ConstantBuffer);

return true;
}
{
Graphics::VectorConstantBuffer* dataPtr = nullptr;

if (FAILED(NXGPU::Map(colorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
return false;

dataPtr = ( Graphics::VectorConstantBuffer*) mappedResource.pData;

dataPtr->Vector = color;

NXGPU::Unmap(colorBuffer, 0);

NXGPU::SetPSConstantBuffers(0, 1, &colorBuffer);

return true;
}

{
Graphics::VectorConstantBuffer* dataPtr = nullptr;

if (FAILED(NXGPU::Map(colorBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource)))
return false;

dataPtr = ( Graphics::VectorConstantBuffer*) mappedResource.pData;

dataPtr->Vector = color;

NXGPU::Unmap(colorBuffer, 0);

NXGPU::SetPSConstantBuffers(0, 1, &colorBuffer);

return true;
}

{
D3D11_BUFFER_DESC matrixBufferDesc;
matrixBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
matrixBufferDesc.ByteWidth = sizeof(Graphics::MatrixConstantBuffer);
matrixBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
matrixBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
matrixBufferDesc.MiscFlags = 0;
matrixBufferDesc.StructureByteStride = 0;

if (FAILED(NXGPU::GetDevice()->CreateBuffer(&matrixBufferDesc, NULL, &ConstantBuffer)))
return false;

D3D11_BUFFER_DESC colorBufferDesc;
colorBufferDesc.Usage = D3D11_USAGE_DYNAMIC;
colorBufferDesc.ByteWidth = sizeof( Graphics::VectorConstantBuffer);
colorBufferDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
colorBufferDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;
colorBufferDesc.MiscFlags = 0;
colorBufferDesc.StructureByteStride = 0;

if (FAILED(NXGPU::GetDevice()->CreateBuffer(&colorBufferDesc, NULL, &colorBuffer)))
return false;

return true;
}


It works great for rendering everything else so far.  Does everything look ok?  I also suspected that the problem may be here:

XMMATRIX NXSpriteBatch::CalculateWorldMatrix(XMFLOAT2 position, XMFLOAT2 size, XMFLOAT2 scale, float rotation)
{

XMMATRIX finalMatrix = XMMatrixTransformation2D(pos, 0, scaling, pos, rotation, pos);
finalMatrix = XMMatrixTranspose(finalMatrix);

return finalMatrix;
}


Before I was doing something else, but today I found this function and tried it out.  Anyways, I would really like to thank you for your time, I really appreciate it.

By the way: If you're doing this for learning, keep going
Otherwise, use, or at least have a look at DirectX Tool Kit Library which has a sprite renderer.

Also, thanks for the support!

Edited by KoldGames

##### Share on other sites

I'm getting something totally different, and I don't even remember what I did now lol O_O. Here's what I am currently getting.  When in full size (I changed the scale to 1), it is aligned with the top left corner.

When I render @ [1360, 0], it is completely off the screen, but rendering @ [500, 0] puts it here:

The width of my current screen is 1360.  Putting it @ [1340, 0] makes it go off-screen.  Here is how I am updating my vertices now.

float left, right, top, bottom;

left = position.x;
right = left + (float) size.x;
top = NXGPU::GetViewportHeight() + (float) position.y;
bottom = top - (float) size.y;

Vertices[0].Position = XMFLOAT3(left, top, 1);
Vertices[1].Position = XMFLOAT3(left, bottom, 1);
Vertices[2].Position = XMFLOAT3(right, top, 1);
Vertices[3].Position = XMFLOAT3(right, bottom, 1);


UPDATE: Pretty weird.  Scaling it down half way (0.5f) and putting it @ [0, 0] gives me this as a result:

EDIT: I also got away from using pointers in NXVertexBuffer  Besides NXCreateVertexBuffer (I would like to be able to use nullptr for Indices)

Edited by KoldGames

##### Share on other sites
Slow down, you might spoil the progress. Use version control or backup. Currently you have output you can play with, be glad about that.

Not sure where to begin. I'm too confused to point at the exact problem - or what you actually want. You're combining hardcoded positioning with a world placement transformation. The documentation of the latter (XMMatrixTransformation2D) is sparse. Never used myself. If it works like D3DXMatrixTransformation2D (mind the complexity) you're parameters don't look right (three times pos instead of just for translation, if you ask me).
Also, you're doing a transpose there too/already ? Why ? (The other transpose - the real final - for mvp looks good though).

Though I could go on for ever now, here rather some general hints:
• Get (more ) familiar with transformations in general. 2D without rotation is "easy" enough to do everything by hand (try pen and paper), then understand the matrix version and the helper functions. And of course how to combine them.
• Get (more) familiar with the terminology, the different spaces in the pipeline and the common graphics spaces (world, view, projection).
• In the end points "land" in normalized device coordinates, left to right goes from -1 to 1, bottom to top from -1 to 1. Ever hardcoded a full screen quad ?
• Use a PIX or the graphics debugger (if you can). You can inspect both graphically and numerically what happens with your vertices, or even debug shaders.
• Most of all: Keep it simple first and add features/options only you can handle (both from understanding and programming).

##### Share on other sites

I'm too confused to point at the exact problem - or what you actually want. You're combining hardcoded positioning with a world placement transformation. The documentation of the latter (XMMatrixTransformation2D) is sparse. Never used myself. If it works like D3DXMatrixTransformation2D (mind the complexity) you're parameters don't look right (three times pos instead of just for translation, if you ask me).

Yeah... I wasn't sure about it as I'm still learning DirectX (coming from XNA) lol.  Now that I have output to play with,  I would like to get everything positioned correctly even when scaled. The idea to combine the two actually came from a tutorial I found on the internet because my original code (when I tried on my own) didn't render correctly (looked ok, but had some weird issues).

Also, you're doing a transpose there too/already ? Why ? (The other transpose - the real final - for mvp looks good though).

I was just experimenting.  CalculateWorldMatrix actually doesn't exist any more in the updated code as I found that multiplying the orthographic matrix by scale seems to work like I thought it would with "scale" set to one (0, 0 being the top left corner) and now it seems to just be a matter of handling the positioning when scale is increased or reduced.  Any idea on how I would do this correctly?

Get more familiar with transformations in general. 2D without rotation is "easy" enough to do everything by hand (try pen and paper), then understand the matrix version and the helper functions. And of course how to combine them.
Get (more ) familiar with the terminology, the different spaces in the pipeline and the common graphics spaces (world, view, projection).

I will definitely read up more about those.  Thanks for the suggestion.

In the end points "land" in normalized device coordinates, left to right goes from -1 to 1, bottom to top from -1 to 1. Ever hardcoded a full screen quad ?

Yep!  I currently have it working and setup.  The NXSpriteBatch code I actually used before I came here was kind of working like one, then I realized that I had setup my orthographic matrix wrong which led to using XMMatrixOrthographicOffCenterLH which gave me better results.

Use a PIX or the graphics debugger (if you can). You can inspect both graphically and numerically what happens with your vertices, or even debug shaders.

Yep! I've been using Visual Studio 2013's Graphics Debugger to debug some shaders and figure out what's causing a problem.  It works quite well in my opinion.

Most of all: Keep it simple first and add features/options only you can handle (both from understanding and programming).

I will remember that!   Thanks!

Edited by KoldGames

##### Share on other sites

I was just experimenting.  CalculateWorldMatrix actually doesn't exist any more in the updated code as I found that multiplying the orthographic matrix by scale seems to work like I thought it would with "scale" set to one (0, 0 being the top left corner) and now it seems to just be a matter of handling the positioning when scale is increased or reduced.  Any idea on how I would do this correctly?

I just got it working correctly.  I realized that instead of multiplying scale by the orthographic matrix, I should handle scale in UpdateVertices();

float left, right, top, bottom;

left = position.x;
right = left + ((float) size.x * scale.x);
top = NXGPU::GetViewportHeight() - (float) position.y;
bottom = top - ((float) size.y * scale.y);

Vertices[0].Position = XMFLOAT3(left, top, 1);
Vertices[1].Position = XMFLOAT3(left, bottom, 1);
Vertices[2].Position = XMFLOAT3(right, top, 1);
Vertices[3].Position = XMFLOAT3(right, bottom, 1);


I feel so stupid now. Lol Anyways, it works correctly.  Everything is correctly positioned.  Thanks for the help Unbird, I really appreciate it. And I will definitely read up some more about transformations.   I may be back later as I will probably have just a couple questions. :)

Edited by KoldGames

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 11
• 15
• 21
• 26
• 11