[Directx 11] [2D] problem positioning a 2D quad , center starting at bottom left and not top left

Started by
7 comments, last by Joyal 10 years, 4 months ago
I'm following some tutorials of directx 11,and i was able to draw a quad, but my problem began when i was applying transforms , for some reason, my quad at position 0,0, start at bottom left of my screen, but i want to it starts at Top Left , i don't figure out why
this happend when i used XMMatrixOrthographicOffCenterLH , but if i use XMMatrixOrthographicLH , it start at the center of the screen, im doing something wrong?
here is some screenshots:
using XMMatrixOrthographicOffCenterLH : http://troll.ws/image/27213361
using XMMatrixOrthographicLH : http://troll.ws/image/ebf0dbc8
SpriteTest.h


    #pragma once
    #ifndef JDX_SPRITE_TEST_H
    #define JDX_SPRITE_TEST_H


    //DX Includes
    #include <DirectXMath.h>
    using namespace DirectX;
   
    class SpriteTest{
    public:
     void Draw();
    private:
     struct ShaderParameters { XMMATRIX worldMatrix; };
     struct VERTEX { XMFLOAT3 Position; XMFLOAT4 Color; };  
    ...
    ...
    };
    #endif
SpriteTest.cpp

    #include "SpriteTest.h"
    using namespace DirectX;
    
    void SpriteTest::Initialize(){
    
     // load and compile the two shaders
     ID3D10Blob *VS = NULL, *PS = NULL;
    
     m_pRenderManager->CompileShader("Shaders/SpriteBatchVS.hlsl","main","vs_5_0",&VS);
     m_pRenderManager->CompileShader("Shaders/SpriteBatchPS.hlsl","main","ps_5_0",&PS);
    
    
     // encapsulate both shaders into shader objects
     m_pDevice->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &m_pVertexShader);
     m_pDevice->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &m_pPixelShader);
    
     // set the shader objects
     m_pDeviceContext->VSSetShader(m_pVertexShader, 0, 0);
     m_pDeviceContext->PSSetShader(m_pPixelShader, 0, 0);
    
     // create the input layout object
     D3D11_INPUT_ELEMENT_DESC ied[] =
     {
     {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
     {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
     };
    
     m_pDevice->CreateInputLayout(ied, 2, VS->GetBufferPointer(), VS->GetBufferSize(), &m_pInputLayout);
     m_pDeviceContext->IASetInputLayout(m_pInputLayout);
    
    
     // __________ Init Vertex Buffer
    
     // create a triangle using the VERTEX struct
     /*
     1__3
     |\ |
     | \|
     0--2
    
     struct VERTEX { XMFLOAT3 Position; XMFLOAT4 Color; };  
     */
     VERTEX QuadVertices[] =
     {
     { XMFLOAT3(-1.0f, -1.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)},
     { XMFLOAT3(-1.0f,  1.0f, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f)},
     { XMFLOAT3( 1.0f, -1.0f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f)},
     { XMFLOAT3 (1.0f,  1.0f, 0.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f)}, 
    
     };
    
     // create the vertex buffer
     D3D11_BUFFER_DESC bd;
     ZeroMemory(&bd, sizeof(bd));
    
     bd.Usage = D3D11_USAGE_DYNAMIC;                // write access access by CPU and GPU
     bd.ByteWidth = sizeof(VERTEX) * 4;             // size is the VERTEX struct * 4
     bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;       // use as a vertex buffer
     bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;    // allow CPU to write in buffer
    
     m_pDevice->CreateBuffer(&bd, NULL, &m_pVertexBuffer);       // create the buffer
    
     // copy the vertices into the buffer
     D3D11_MAPPED_SUBRESOURCE ms;
     m_pDeviceContext->Map(m_pVertexBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);    // map the buffer
     memcpy(ms.pData, QuadVertices, sizeof(QuadVertices));  // copy the data
     m_pDeviceContext->Unmap(m_pVertexBuffer, NULL);                                      // unmap the buffer
     
    
     // __________ Init Index Buffer
    
     WORD indices[] =
     {
     0,1,2,
     2,1,3,
     };
    
    
     D3D11_BUFFER_DESC indexDesc; 
     ZeroMemory( &indexDesc, sizeof( indexDesc ) );
     
     indexDesc.Usage = D3D11_USAGE_DEFAULT;
     indexDesc.ByteWidth = sizeof( WORD ) * 6;
     indexDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
     indexDesc.CPUAccessFlags = 0;
    
     D3D11_SUBRESOURCE_DATA indexData; 
     ZeroMemory( &indexData, sizeof( indexData ) ); 
     indexData.pSysMem = indices;
    
     m_pDevice->CreateBuffer( &indexDesc, &indexData, &m_pIndexBuffer );
    
    
     // __________ Init Constant Buffer
     D3D11_BUFFER_DESC constDesc; 
     ZeroMemory( &constDesc, sizeof( constDesc ) );
     
     constDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER; 
     constDesc.ByteWidth = sizeof( XMMATRIX ); 
     constDesc.Usage = D3D11_USAGE_DEFAULT;
     
     m_pDevice->CreateBuffer( &constDesc, 0, &m_pCostantBuffer );
    
    }
    
    void SpriteTest::Draw(){
     // select which vertex buffer to display
     
     UINT stride = sizeof(VERTEX);
     UINT offset = 0;
     m_pDeviceContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &stride, &offset);
     
     // select which primtive type we are using
     m_pDeviceContext->IASetIndexBuffer( m_pIndexBuffer, DXGI_FORMAT_R16_UINT, 0 );
     m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
    
     XMMATRIX view = XMMatrixIdentity(); 
     //XMMATRIX projection = XMMatrixOrthographicLH( 1024, 768, 0.0f, 100.0f );
     XMMATRIX projection  = XMMatrixOrthographicOffCenterLH( 0.0f,(float)m_pRenderManager->GetWidth(),0.0f,(float) m_pRenderManager->GetHeight(), 0.0f, 100.0f );
     XMMATRIX worldMatrix = XMMatrixMultiply( view, projection );
    
     ShaderParameters shaderParameters;
    
     // __________ Sprite World Coordinates
     float spriteWidth  = 32.0f,
       spriteHeight = 32.0f,
       spritePosX = 0.0f,
       spritePosY = 0.0f;
    
     XMMATRIX l_translation = XMMatrixTranslation(spritePosX, spritePosY, 0.0f ); 
     XMMATRIX l_rotationZ = XMMatrixRotationZ( 0.0f ); 
     XMMATRIX l_scale = XMMatrixScaling( 1.0f * spriteWidth,1.0f * spriteHeight, 1.0f );
     XMMATRIX l_spriteWVP  = l_translation * l_rotationZ * l_scale;
    
     // __________ Prepare World Coordinates to send to the shader
     XMMATRIX l_worldMatrix = XMMatrixMultiply( l_spriteWVP, worldMatrix ); 
     l_worldMatrix = XMMatrixTranspose( l_worldMatrix );
    
     shaderParameters.worldMatrix = l_worldMatrix;
     
     m_pDeviceContext->UpdateSubresource( m_pCostantBuffer, 0, NULL, &shaderParameters, 0, 0 );  
     m_pDeviceContext->VSSetConstantBuffers( 0, 1, &m_pCostantBuffer );
     
     m_pDeviceContext->DrawIndexed( 6, 0, 0 );
     
    }
vertex shader:

    cbuffer cbParameters : register( b0 ) { 
     float4x4 worldMatrix; 
    };
    
    struct VOut
    {
     float4 position : SV_POSITION;
     float4 color : COLOR;
    };
    
    VOut main(float4 position : POSITION, float4 color : COLOR)
    {
     VOut output;
    
     output.position = mul(position,worldMatrix);
     output.color = color;
    
     return output;
    }
pixel shader:

    float4 main(float4 position : SV_POSITION, float4 color : COLOR) : SV_TARGET
    {
     return color;
    }

Advertisement

Swap the viewBottom and viewTop values for the orthographic matrix:

XMMatrixOrthographicOffCenterLH( 0.0f,(float)m_pRenderManager->GetWidth(),(float) m_pRenderManager->GetHeight(), 0.0f, 0.0f, 100.0f );

That should get 0,0 at the top-left of the viewport.

Swap the viewBottom and viewTop values for the orthographic matrix:

XMMatrixOrthographicOffCenterLH( 0.0f,(float)m_pRenderManager->GetWidth(),(float) m_pRenderManager->GetHeight(), 0.0f, 0.0f, 100.0f );

That should get 0,0 at the top-left of the viewport.

just tried but nothing showing on the window, all black, i change the position of the sprite, but nothing

Lucky guess: If don't set a rasterizer state explicitly the default is used, which culls backfacing triangles (with the new projection they got flipped). Use one with D3D11_CULL_FRONT (or D3D11_CULL_NONE) or flip your triangles.

Your l_spriteWVP matrix is wrong. The multiplication order should be scale * rotation * translation, not the other way around.

Also, I'm a bit confused by your matrix names and usages. You are including sprite position (translation) in your l_spriteWVP matrix, then multiplying that by another matrix (worldMatrix (which is really view * projection)) and setting that result on your shader as the world matrix. Then in your shader, you are multiplying the sprite position by the world matrix again. I'm pretty sure that's going to give strange results.

I would strongly recommend using the DirectX Tool Kit for sprite drawing. It's an official MS tool kit and takes care of all this for you. If you want to learn how to do it yourself, all the source code for the tool kit is available for your perusal.

Lucky guess: If don't set a rasterizer state explicitly the default is used, which culls backfacing triangles (with the new projection they got flipped). Use one with D3D11_CULL_FRONT (or D3D11_CULL_NONE) or flip your triangles.

@unbird , indeep change the culling to D3D11_CULL_NONE, is showing the QUAD but now i have this error http://screencast.com/t/S2ZsYcqQdkAG , i create the rasterizer after i create the Render Target


    D3D11_RASTERIZER_DESC rasterizerState;
    ZeroMemory(&rasterizerState, sizeof(D3D11_RASTERIZER_DESC));

    rasterizerState.AntialiasedLineEnable = false;
    rasterizerState.CullMode = D3D11_CULL_NONE; // D3D11_CULL_FRONT or D3D11_CULL_NONE D3D11_CULL_BACK
    rasterizerState.FillMode = D3D11_FILL_SOLID; // D3D11_FILL_SOLID  D3D11_FILL_WIREFRAME
    rasterizerState.DepthBias = 0;
    rasterizerState.DepthBiasClamp = 0.0f;
    rasterizerState.DepthClipEnable = true;
    rasterizerState.FrontCounterClockwise = false;
    rasterizerState.MultisampleEnable = false;
    rasterizerState.ScissorEnable = false;
    rasterizerState.SlopeScaledDepthBias = 0.0f;

    result = m_pDevice->CreateRasterizerState( &rasterizerState, &m_pRasterState );
    m_pDeviceContext->RSSetState(m_pRasterState);

Swap the viewBottom and viewTop values for the orthographic matrix:

XMMatrixOrthographicOffCenterLH( 0.0f,(float)m_pRenderManager->GetWidth(),(float) m_pRenderManager->GetHeight(), 0.0f, 0.0f, 100.0f );

That should get 0,0 at the top-left of the viewport.

just tried but nothing showing on the window, all black, i change the position of the sprite, but nothing

@davehunt after i change the culling settings, is showing at the TOP LEFT using the XMMatrixOrthographicOffCenterLH you gave me

Your l_spriteWVP matrix is wrong. The multiplication order should be scale * rotation * translation, not the other way around.

Also, I'm a bit confused by your matrix names and usages. You are including sprite position (translation) in your l_spriteWVP matrix, then multiplying that by another matrix (worldMatrix (which is really view * projection)) and setting that result on your shader as the world matrix. Then in your shader, you are multiplying the sprite position by the world matrix again. I'm pretty sure that's going to give strange results.

I would strongly recommend using the DirectX Tool Kit for sprite drawing. It's an official MS tool kit and takes care of all this for you. If you want to learn how to do it yourself, all the source code for the tool kit is available for your perusal.

spriteWVP multiplication was in deep wrong, i change to scale * rotation * translation, and is also positioning the sprite where they should

i create a worldMatrix that is the global matrix of the world, in this case is the screen coords , viewMatrix is XMMatrixIdentity because i don't new a Look Up view that is normaly used for 3d,

then i need the actual matrix from the sprite, that needs to be multiply by the Screen coords, that is why is multiply with the "worldmatrix"

the last one you mention is the shader , that position is not the sprite position, is the position of each vertex , that need to be multiply with the entire worldmatrix to be converted to world coordinates, in this case, screen coords

the last one you mention is the shader , that position is not the sprite position, is the position of each vertex , that need to be multiply with the entire worldmatrix to be converted to world coordinates, in this case, screen coords

Doh! I knew that, but I still had sprite positions on the brain. ;-)

You are correct.

@unbird , indeep change the culling to D3D11_CULL_NONE, is showing the QUAD but now i have this error http://screencast.com/t/S2ZsYcqQdkAG , i create the rasterizer after i create the Render Target

i rebuilt the entire solution and somehow it fixed that error

i also realized the quad was mirror-like (all inverse), after i applied those changes, but i was able to fix it changing the vertex position when i create the vertex buffer

    VERTEX QuadVertices[] =
    {
        { XMFLOAT3( 0.0f,  1.0f, 0.0f), XMFLOAT4(1.0f, 0.0f, 0.0f, 1.0f)},
        { XMFLOAT3( 0.0f,  0.0f, 0.0f), XMFLOAT4(0.0f, 1.0f, 0.0f, 1.0f)},
        { XMFLOAT3( 1.0f,  1.0f, 0.0f), XMFLOAT4(0.0f, 0.0f, 1.0f, 1.0f)},
        { XMFLOAT3 (1.0f,  0.0f, 0.0f), XMFLOAT4(1.0f, 1.0f, 1.0f, 1.0f)},  


    };

This topic is closed to new replies.

Advertisement