Jump to content

  • Log In with Google      Sign In   
  • Create Account


The Jump to D3D11: Orthographic projection


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
16 replies to this topic

#1 noodleBowl   Members   -  Reputation: 183

Like
0Likes
Like

Posted 17 November 2013 - 05:20 PM

I'm trying to make the switch to DirectX11 and I'm having some trouble with some things. One thing that I need to be solved is how I can set up an orthographic projection?

 

My focus is on 2D and I feel like this is pretty important

 

I know from Direct X 9 you could do this and you have an Orthographic Projection

D3DXMATRIX out;
D3DXMatrixOrthoOffCenterLH(&out, 0.0f, 800.0f, 600.0f, 0.0f, 0.0f, 1.0f);
device->SetTransform(D3DTS_PROJECTION, &out);

But how is this done in DirectX11? I'm having trouble finding resources that can explain this properly



Sponsor:

#2 mhagain   Crossbones+   -  Reputation: 7467

Like
0Likes
Like

Posted 17 November 2013 - 06:55 PM

Can you be clearer on what part of this you're having trouble with?  The old SetTransform calls and D3DTS_* states are gone in D3D11, but otherwise things work much the same (with the exception of cbuffers versus standalone constants) as they did with shaders in D3D9.  Have you experience using shaders in D3D9?  Are you looking for a matrix library to build the ortho matrix?  It's difficult to provide a meaningful answer without an understanding of what concepts you do or do not already know about here.


It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#3 noodleBowl   Members   -  Reputation: 183

Like
0Likes
Like

Posted 17 November 2013 - 07:51 PM

quote

Honestly, when it comes to DirectX 11 there is not a lot I know in terms of the API.
I have never used shaders in DirectX 9, so with them being mandatory in DirectX 11 I'm not sure what to expect.

When it comes to setting up projections and other matrices I'm not even sure where to start. I definitely need a solid explanation or tutorial for setting up projections and matrix manipulation in DirectX 11

#4 ankhd   Members   -  Reputation: 1116

Like
0Likes
Like

Posted 18 November 2013 - 01:11 AM

Hi. Theres one in xna math file

There are a few functions that are part of the XNA Math library that are used to build projection matrices, . XMMatrixOrthographicOffCenterLH

there is I think a directmath header and lib for DX11.



#5 tonemgub   Members   -  Reputation: 642

Like
0Likes
Like

Posted 18 November 2013 - 02:29 AM

I would just write a shader function that does the same thing as D3DXMatrixOrthoOffCenterLH. It would take the parameters directly from a constant buffer. The formula is at the bottom of the page: http://msdn.microsoft.com/en-us/library/windows/desktop/bb205347(v=vs.85).aspx. This way you don't have to bother with matrix row/column ordering.

And for 2D, maybe an identity matrix would suffice?


Edited by tonemgub, 18 November 2013 - 02:30 AM.


#6 Tom KQT   Members   -  Reputation: 1506

Like
0Likes
Like

Posted 18 November 2013 - 02:52 AM

I would just write a shader function that does the same thing as D3DXMatrixOrthoOffCenterLH. It would take the parameters directly from a constant buffer. The formula is at the bottom of the page: http://msdn.microsoft.com/en-us/library/windows/desktop/bb205347(v=vs.85).aspx. This way you don't have to bother with matrix row/column ordering.

And for 2D, maybe an identity matrix would suffice?

What would be the motivation to do this in shaders? Projection matrix is usually the same for all objects in the scene (sometimes you need two, maybe three, but still the number is smaller than the number of objects). Even if your camera moves a lot, you need to build the proj matrix once per frame. If you move the code to a vertex shader, you will be building the matrix once per VERTEX. Once per very single vertex in your scene. Or you can also put it to a pixel shader.... ;)


Edited by Tom KQT, 18 November 2013 - 02:54 AM.


#7 ericrrichards22   Members   -  Reputation: 744

Like
1Likes
Like

Posted 18 November 2013 - 05:17 AM

You can always download the June 2012 DirectX SDK, and continue using the older deprecated D3DX matrix functions.

I haven't played with it myself, but DirectXMath looks like it is a replacement for the D3DX matrix functions, if you want to stick with something that is actively supported.


Eric Richards

SlimDX tutorials - http://www.richardssoftware.net/

Twitter - @EricRichards22


#8 mhagain   Crossbones+   -  Reputation: 7467

Like
1Likes
Like

Posted 18 November 2013 - 05:47 AM

Yes, the old D3DX matrix functions work perfectly fine with D3D11 - they're just a matrix library so there's really no dependencies on which D3D version you use them with (I've even successfully used them with OpenGL in the past).


It appears that the gentleman thought C++ was extremely difficult and he was overjoyed that the machine was absorbing it; he understood that good C++ is difficult but the best C++ is well-nigh unintelligible.


#9 Juliean   GDNet+   -  Reputation: 2247

Like
0Likes
Like

Posted 18 November 2013 - 06:37 AM

Just to add, you do not necessarily need an orthographic projection for 2d in DX11. You can do the transform yourself pretty easily on the CPU:

size_t Sprite::Draw(const Texture& d3dTexture, const math::Vector3& vPosition, const math::Vector3& /*vOrigin*/, const RECT& rSrcRect, const math::Vector2f& vScale, float /*fAngle*/)
{
    RECT rSrc(rSrcRect);

    const math::Vector2& vTextureSize(d3dTexture.GetSize());

    const math::Vector2 vSrcSize(rSrcRect.right - rSrcRect.left, rSrcRect.bottom - rSrcRect.top);
    const math::Vector2 vSize((int)(vSrcSize.x*vScale.x), (int)(vSrcSize.y*vScale.y));

    // calculate the sprites vertices in screen space
    // to speed up calculations, we multiply by 1.0 / screen size, which is calculated offline
    float leftVertex = vPosition.x  * m_vInvHalfScreenSize.x - 1.0f;
    float rightVertex = leftVertex + vSize.x * m_vInvHalfScreenSize.x;
    float topVertex = -vPosition.y * m_vInvHalfScreenSize.y + 1.0f;
    float bottomVertex = topVertex - vSize.y * m_vInvHalfScreenSize.y;

    const float leftCoord = rSrc.left / (float)vTextureSize.x;
    const float rightCoord = rSrc.right / (float)vTextureSize.x;
    const float topCoord = rSrc.top / (float)vTextureSize.y;
    const float bottomCoord = rSrc.bottom / (float)vTextureSize.y;

    SpriteVertex Vertices[] =
    {
        { leftVertex, topVertex, vPosition.z, leftCoord, topCoord, 1.0f, 1.0f, 1.0f, 1.0f },
        { rightVertex, topVertex, vPosition.z, rightCoord, topCoord, 1.0f, 1.0f, 1.0f, 1.0f },
        { rightVertex, bottomVertex, vPosition.z, rightCoord, bottomCoord, 1.0f, 1.0f, 1.0f, 1.0f },
        { leftVertex, bottomVertex, vPosition.z, leftCoord, bottomCoord, 1.0f, 1.0f, 1.0f, 1.0f }
    };

    SpriteVertex* pBuffer = static_cast<SpriteVertex*>(m_mapped.pData);
    memcpy(pBuffer + m_nextFreeId * 4, Vertices, sizeof(SpriteVertex)* 4);

    return m_nextFreeId++;
}

This way, the vertices are already transformed, and you can simply pass them through in your vertex shader. This example works when you draw sprites with pixel coordinates, if you have them already in relative screen space (from 0.0f to 1.0f) the conversion is even simplier. Of course it depends whether this makes sense, but depending on your programs structure, it can make things easier if you don't have to have a orthogonal matric available for every shader that eigther draws a sprite or performs a fullscreen pass.



#10 NightCreature83   Crossbones+   -  Reputation: 2674

Like
0Likes
Like

Posted 18 November 2013 - 06:39 AM

You have to look into cbuffers to be able to set the matrices on the pipeline however unlike in D3D9 there is no longer a Fixed Function pipeline which does transformations for VBs for you.

 

You will have to write a vertex shader to accomplish this in D3D11 and they arent that hard to write, there are enough sample out there to give you a basic tranform vertex shader and pixel shader.

 

The rastertek tutorials are a good way to get into D3D11 shaders and how the whole pipeline works: http://www.rastertek.com/tutindex.html If you want to work of familiar D3D9.c concepts use the http://www.codesampler.com/dx9src.htm D3D9 tutorials as they ease you into shader programming as well, since you know D3D9 skip to the part where they start using shaders.


Edited by NightCreature83, 18 November 2013 - 06:42 AM.

Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#11 tonemgub   Members   -  Reputation: 642

Like
1Likes
Like

Posted 18 November 2013 - 07:11 AM


What would be the motivation to do this in shaders?

1) To reduce the number of constants that need to be sent to the shader (16 floats for the matrix versus only the 6 floats that can be sent to create the matrix).

2) To reduce the number of calculations in the shader - most of the values (10 of them) in the matrix are constant 1s and 0s, and the HLSL compiler will not generate code for calculating (mostly multiplying) vector or other matrices' components with those values, or it will mix them together with other constant values that are part of the same calculations, into a single constant that is only applied to the calculation once... this and other optimisations that the compiler may do, that I cannot possibly imagine. smile.png

 

 


Even if your camera moves a lot, you need to build the proj matrix once per frame. If you move the code to a vertex shader, you will be building the matrix once per VERTEX. Once per very single vertex in your scene. Or you can also put it to a pixel shader.... ;)

Hmmm. I didn't think of it this way. Good point. Please ignore my last comment. smile.png

 

(Though, in my defense, I was thinking of building the projection matrix in a Geometry shader, so only once per primitive...).


Edited by tonemgub, 18 November 2013 - 07:36 AM.


#12 NightCreature83   Crossbones+   -  Reputation: 2674

Like
1Likes
Like

Posted 18 November 2013 - 09:34 AM

 


What would be the motivation to do this in shaders?

1) To reduce the number of constants that need to be sent to the shader (16 floats for the matrix versus only the 6 floats that can be sent to create the matrix).

2) To reduce the number of calculations in the shader - most of the values (10 of them) in the matrix are constant 1s and 0s, and the HLSL compiler will not generate code for calculating (mostly multiplying) vector or other matrices' components with those values, or it will mix them together with other constant values that are part of the same calculations, into a single constant that is only applied to the calculation once... this and other optimisations that the compiler may do, that I cannot possibly imagine. smile.png

 

 


Even if your camera moves a lot, you need to build the proj matrix once per frame. If you move the code to a vertex shader, you will be building the matrix once per VERTEX. Once per very single vertex in your scene. Or you can also put it to a pixel shader.... ;)

Hmmm. I didn't think of it this way. Good point. Please ignore my last comment. smile.png

 

(Though, in my defense, I was thinking of building the projection matrix in a Geometry shader, so only once per primitive...).

 

You dont need to rebuild a projection matrix at all, just create one and pass it along to the shader you need to set this the per frame cb once a frame that is all. If you start recreating your projection matrix you are just wasting CPU time each frame.

 

Generally you build all projection matrices you need in the code once at application initialise or level initialise and then set it when needed, you can have to update the projection matrix multiple times per frame like for example when you have a cubemap renderer or other post effects in your render pipeline. Generally you use a different projection for this then for the normal game camera.


Worked on titles: CMR:DiRT2, DiRT 3, DiRT: Showdown, GRID 2, Mad Max

#13 Tom KQT   Members   -  Reputation: 1506

Like
0Likes
Like

Posted 18 November 2013 - 11:45 AM



You dont need to rebuild a projection matrix at all, just create one and pass it along to the shader you need to set this the per frame cb once a frame that is all. If you start recreating your projection matrix you are just wasting CPU time each frame.

 

Generally you build all projection matrices you need in the code once at application initialise or level initialise and then set it when needed, you can have to update the projection matrix multiple times per frame like for example when you have a cubemap renderer or other post effects in your render pipeline. Generally you use a different projection for this then for the normal game camera.

 

Yep, you don't. Unless you use it to simulate zoom for example.

I wrote originaly my post about the view matrix (I forgot the thread was about projection) and when I noticed the mistake, I only edited "view" to "projection" everywhere in my post, without thinking about it more in depth smile.png But my point still applies and the fact that it's about projection and not view only makes it stronger - because you don't need to rebuild this matrix at all (usually) smile.png

 


Edited by Tom KQT, 18 November 2013 - 11:45 AM.


#14 noodleBowl   Members   -  Reputation: 183

Like
0Likes
Like

Posted 18 November 2013 - 08:50 PM

Feel free to correct me, but I think this what I need to do

 

I need to build my projection matrix once, so for this I can use the XNA Matrix Lib or D3D Matrix Lib using the OrthographicOffCenterLH functions. Then I need to, I'm assuming, store this information in Constant Buffer (cbuffer). Which will then some how go into my Vertex Shader. Right?



#15 tonemgub   Members   -  Reputation: 642

Like
0Likes
Like

Posted 19 November 2013 - 03:27 PM

@Tom KQT, I'd like to mention that I was actually correct:

1) To reduce the number of constants that need to be sent to the shader (16 floats for the matrix versus only the 6 floats that can be sent to create the matrix).
2) To reduce the number of calculations in the shader - most of the values (10 of them) in the matrix are constant 1s and 0s, and the HLSL compiler will not generate code for calculating (mostly multiplying) vector or other matrices' components with those values, or it will mix them together with other constant values that are part of the same calculations, into a single constant that is only applied to the calculation once... this and other optimisations that the compiler may do, that I cannot possibly imagine. smile.png

 

Explanation:

The shader will contain fewer instructions if you just pass the 6 float parameters into the shader and "build" the matrix there... Take a simple mul(matrix, vector) calculation for example... In the case of an orthographic matrix, only 7 values from the matrix will be != 0, and one of them is always 1, so there are actually only 6 variables. In this case, the compiler will optimize the mul(matrix, vector) intrinsic function down to just 9 operations: 6 multiplies, 3 additions. The value from the matrix that is 1 has the effect that the vertex's w value is preserved, so no operation is required for this. To this, you have to add the number of instructions that the compiler will generate for building the 6 variable matrix values from the 6 passed-in values: D3DXMatrixOrthoOffCenterLH does this with 2 additions, 6 subtractions and 6 divisions. Add all these up, and you get a total of 23 operations, though I'm pretty sure there's still room for the HLSL compiler to optimize the D3DXMatrixOrthoOffCenterLH formula.

 

Now, if you pass the ortho matrix as a whole matrix of 4*4=16 floats to the shader, then the mul(matrix, vector) calculation has to treat all of those 16 values as variables, and so the compiler translates it to: 16 multiplies and 12 additions - a total of 28 operations, which is more than the 23 operations requried for the method I proposed. smile.png

 

Keep in mind that you need to do a mul(matrix, vector) at least once per vertex in your vertex shader.

 

 


I need to build my projection matrix once, so for this I can use the XNA Matrix Lib or D3D Matrix Lib using the OrthographicOffCenterLH functions. Then I need to, I'm assuming, store this information in Constant Buffer (cbuffer). Which will then some how go into my Vertex Shader. Right?

 

Yes, this is what you need to do, but instead of D3DXMatrixOrthoOffCenterLH, you can also use this HLSL function directly in your vertex shader, and instead of passing the whole matrix to the shader, you can just pass the values for the function's parameters:

float4x4 ortho_mat(float l, float r, float b, float t, float zn, float zf) {return float4x4(2.0 / (r - l), 0.0, 0.0, 0.0, 0.0, 2.0 / (t - b), 0.0, 0.0, 0.0, 0.0, 1.0 / (zf - zn), 0.0, (l + r) / (l - r), (t + b) / (b - t), zn / (zn - zf), 1.0);}


Edited by tonemgub, 19 November 2013 - 03:48 PM.


#16 L. Spiro   Crossbones+   -  Reputation: 12334

Like
4Likes
Like

Posted 19 November 2013 - 04:03 PM

The shader will contain fewer instructions if you just pass the 6 float parameters into the shader and "build" the matrix there...

The shader will generate even fewer instructions if you don’t send any matrices at all nor do any matrix math.
 
The whole purpose of the projection matrix is to normalize vertices between -1 and 1 on both axes.
If you simply construct a vertex buffer with vertices already normalized in this way you don’t need to send any matrices at all nor perform any math on them inside the vertex shader.
 
This works exceptionally well for static 2D objects such as the HUD, and it is independent of resolution (meaning your HUD items consume the same amount of screen space as you increase or decrease the screen resolution).  You can regenerate the vertex buffer if you want objects not to physically scale up or down with various resolutions upon resizing, which is reasonable performance-wise (updating a few vertex buffers once only when resizing).
 
For sprites that move, rotate, and scale, you need only send normalized screen offset positions, a rotation in radians, and a single scale value.  This lets you fit the entire transform of the sprite into a single float4 value, decreasing demands on bandwidth substantially.  XY = Normalized Translation, Z = Rotation, W = Scale.
 
 
Vertex shader:
 
float2 fXY = IN.xy * IN_POS_ROT_SCALE.w;
float fCos = cos( IN_POS_ROT_SCALE.z );
float fSin = sin( IN_POS_ROT_SCALE.z );
OUT.x = fXY.x * fCos - fXY.y * fSin;
OUT.y = fXY.y * fCos + fXY.x * fSin;
OUT.xy += IN_POS_ROT_SCALE.xy;
OUT.zw = IN.zw;
The sin/cos calls could be done on the CPU and sent instead of scale (if scale is 1.0) or as a second float4, but since it will only be applied to 4 vertices it will likely be much faster done on the GPU.

The above shows the worst-case scenario but you can make different shaders or shader branches when rotation is 0 or scale is 1 and avoid the sin/cos, an extra multiply, or both.
 
 
 
Not only is this the fastest way to handle 2D (for any platform), it has the advantage that vertex coordinates of X = -1 is always the left of the screen and X = 1 is always the far right, so keeping things proportional to various resolutions is only a matter of a simple normalization multiplier.  If stretching is not desired (and why would it be?) then it only needs to account for the aspect ratio, but it still boils down to a simple X/Y normalization multiplier that can be precomputed once when the resolution changes.


And again, for static HUD images, the vertex shader is this:
OUT = IN;
 
 
L. Spiro

Edited by L. Spiro, 19 November 2013 - 04:07 PM.

It is amazing how often people try to be unique, and yet they are always trying to make others be like them. - L. Spiro 2011
I spent most of my life learning the courage it takes to go out and get what I want. Now that I have it, I am not sure exactly what it is that I want. - L. Spiro 2013
I went to my local Subway once to find some guy yelling at the staff. When someone finally came to take my order and asked, “May I help you?”, I replied, “Yeah, I’ll have one asshole to go.”
L. Spiro Engine: http://lspiroengine.com
L. Spiro Engine Forums: http://lspiroengine.com/forums

#17 noodleBowl   Members   -  Reputation: 183

Like
0Likes
Like

Posted 24 November 2013 - 09:56 AM

 

The shader will contain fewer instructions if you just pass the 6 float parameters into the shader and "build" the matrix there...

The shader will generate even fewer instructions if you don’t send any matrices at all nor do any matrix math.
 
The whole purpose of the projection matrix is to normalize vertices between -1 and 1 on both axes.
If you simply construct a vertex buffer with vertices already normalized in this way you don’t need to send any matrices at all nor perform any math on them inside the vertex shader.
 
This works exceptionally well for static 2D objects such as the HUD, and it is independent of resolution (meaning your HUD items consume the same amount of screen space as you increase or decrease the screen resolution).  You can regenerate the vertex buffer if you want objects not to physically scale up or down with various resolutions upon resizing, which is reasonable performance-wise (updating a few vertex buffers once only when resizing).
 
For sprites that move, rotate, and scale, you need only send normalized screen offset positions, a rotation in radians, and a single scale value.  This lets you fit the entire transform of the sprite into a single float4 value, decreasing demands on bandwidth substantially.  XY = Normalized Translation, Z = Rotation, W = Scale.
 
 
Vertex shader:
 
float2 fXY = IN.xy * IN_POS_ROT_SCALE.w;
float fCos = cos( IN_POS_ROT_SCALE.z );
float fSin = sin( IN_POS_ROT_SCALE.z );
OUT.x = fXY.x * fCos - fXY.y * fSin;
OUT.y = fXY.y * fCos + fXY.x * fSin;
OUT.xy += IN_POS_ROT_SCALE.xy;
OUT.zw = IN.zw;
The sin/cos calls could be done on the CPU and sent instead of scale (if scale is 1.0) or as a second float4, but since it will only be applied to 4 vertices it will likely be much faster done on the GPU.

The above shows the worst-case scenario but you can make different shaders or shader branches when rotation is 0 or scale is 1 and avoid the sin/cos, an extra multiply, or both.
 
 
 
Not only is this the fastest way to handle 2D (for any platform), it has the advantage that vertex coordinates of X = -1 is always the left of the screen and X = 1 is always the far right, so keeping things proportional to various resolutions is only a matter of a simple normalization multiplier.  If stretching is not desired (and why would it be?) then it only needs to account for the aspect ratio, but it still boils down to a simple X/Y normalization multiplier that can be precomputed once when the resolution changes.


And again, for static HUD images, the vertex shader is this:
OUT = IN;
 
 
L. Spiro

 

 

If my end goal is to create a sprite batcher, since I'm only focusing on 2D, is this the best approach? I would have to update my vertex/index buffer every frame. I am worried that this will take a up lot of CPU time. Where, I believe, sending all the data to the Vertex Shader will do all the calculations on the GPU.

 

I'm not 100% sure how variables get passed into the Vertex Shader as well. I think this is done through the Input Layout right? So for this I would have to set up a Input Layout that looks

D3D11_INPUT_ELEMENT_DESC inputDesc[] =
{

//Pos 0: XY
//Pos 1: Z
//Pos 2: W
{"POSITION0", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"POSITION1", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"POSITION2", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0},
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0}

};

Edited by noodleBowl, 24 November 2013 - 11:25 AM.





Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS