0
\$\begingroup\$

I am quite confused with how the game engine handles camera view in DirectX. I know all the matrix stuffs, but where the projection matrix goes finally seems rarely mentioned.

I looked up in the sample project by microsoft https://msdn.microsoft.com/en-us/windows/uwp/gaming/tutorial--create-your-first-metro-style-directx-game

in the GameRenderer::CreateWindowSizeDependentResources() method, the projection process is implemented

XMFLOAT4X4 orientation = m_deviceResources->GetOrientationTransform3D();

        ConstantBufferChangeOnResize changesOnResize;
        XMStoreFloat4x4(
            &changesOnResize.projection,
            XMMatrixMultiply(
                XMMatrixTranspose(m_game->GameCamera()->Projection()),
                XMMatrixTranspose(XMLoadFloat4x4(&orientation))
                )
            );

        d3dContext->UpdateSubresource(
            m_constantBufferChangeOnResize.Get(),
            0,
            nullptr,
            &changesOnResize,
            0,
            0
            );

However I can't figure out what the UpdateSubresources method has anything to do with projection (yet such projection in the UpdateSubresources method exists in some other samples I found). But wouldn't it be absurd to change the entire buffer every frame for projection? Isn't the data in the buffer in world view coordinates?

The vertex shader seems also take the task of projection

PixelShaderInput output = (PixelShaderInput)0;

    output.position = mul(mul(mul(input.position, world), view), projection);

I do think this one is more plausible for the task.

So the questions are:

  1. Which one exactly is the process of projection, and how does it come along in two places?

  2. If it happens in UpdateSubresources, what should I do with dynamic buffer?

  3. If it happens in the shader, where does the world, view, projection matrics come from?

  4. How should I use it with SharpDX

\$\endgroup\$
8
  • 1
    \$\begingroup\$ msdn.microsoft.com/en-us/windows/uwp/gaming/… - "m_constantBufferChangeOnResize. This constant buffer contains the projection matrix. The projection matrix is dependent on the size and aspect ratio of the window. It's updated only when the window size changes" - that cbuffer doesn't change every frame. \$\endgroup\$ Nov 7, 2016 at 10:35
  • \$\begingroup\$ You mean this buffer is not a vertex buffer, but only contains the matrix? It makes sense in this case, although I don't understand why to store the matrix in the buffer. While in another case, // Update Constant Buffer context.UpdateSubresource(ref worldViewProj, this.constantBuffer, 0); the matrix is in a parameter list with the constantBuffer which holds vertices. Does this implies a transformation of such matrix on the vertices? \$\endgroup\$ Nov 7, 2016 at 10:58
  • 1
    \$\begingroup\$ You might want to read the difference between vertex buffers and constant buffers. I sense some confusion about how they differ from your understanding. msdn.microsoft.com/en-us/library/windows/desktop/… \$\endgroup\$
    – Syntac_
    Nov 7, 2016 at 11:10
  • \$\begingroup\$ Yes, I do. I thought constant buffers were constant vertex buffers... \$\endgroup\$ Nov 7, 2016 at 11:20
  • \$\begingroup\$ So it is the shader which takes care of view transforms and projections, right? \$\endgroup\$ Nov 7, 2016 at 11:23

2 Answers 2

1
\$\begingroup\$

Direct3D version 9, and even more 10, 11, and even even more, Direct3D 12, work at a very different level of abstraction compared to a game engine.

Put simply, Direct3D is simply a set of APIs that allow you to do stuff with the video card, but that's all there is to it. The concept of projection matrices and cameras, does not even exist inside Direct3D, so it's meaningless to talk about them.

Overly generalizing, what Direct3D allows you to do is to feed it a set of abstract "vertices", which you can then transform into screen coordinates, which are then rasterized as triangles. The process of transforming vertices into screen coordinates is done by a piece of code called the "Vertex shader", which is independently run for each input vertex, and each outputs a screen coordinate.

If it happens that the vertices you are feeding to Direct3D correspond to coordinates in a 3D world, you can have the vertex shader multiply the input vertex with a world-view-projection matrix, which will result in the output corresponding to the screen coordinate of the input 3D coordinate, looked at from a camera defined by the world-view-projection matrix.

It seems overly complicated, but what I'm trying to say is that the concept of cameras doesn't really exist in Direct3D. Instead, Direct3D gives you the tools to build a lot of things, one of them you can choose to be a camera.

In practical terms, since the projection is commonly (but not mandatorily) performed in the vertex shader, you need a way to tell the vertex shader which matrix to use. One way of doing this is with the UpdateSubresource API. If you have everything connected correctly, the matrix you set with UpdateSubresource will be seen by the vertex shader. Therefore, to implement the common concept of "cameras" in a low level API such as Direct3D, you need (in no particular order):

  • Your vertices to include a 3D position in a virtual space
  • Your code to create a world-view-projection matrix
  • Your code to upload the matrix to the GPU via UpdateSubresource
  • Your vertex shader to multiply the matrix with the vertex
  • Your pipeline to wire everything
  • The rest of the program to work

As you're reading the sample code, keep a look at which parts of the code do each one of these steps, so you can see how it all fits together.

\$\endgroup\$
0
\$\begingroup\$

In the older Direct3D 9 era 'fixed function pipeline' the lighting & transformation was done by a specific set of hardware that made use of the transformation matrices. In the modern 'programmable pipeline' model, lighting & transformation is typically done in the vertex shader.

In the older Direct3D 9 version of the 'programmable pipeline', variables could be communicated to the shader via a global array of shader constants. In Direct3D 10 and 11 this was made into the concept of a 'Constant Buffer' which is usually updated much more often than a Vertex Buffer or Index Buffer. These can be updated either using the "Map DISCARD" pattern (such as is used for dynamic Vertex Buffers) or by copying from memory using UpdateSubresource.

With Direct3D 12, there's now even more programmatic control over how data is shared between shaders and the CPU using the concept of root signatures. Data can be placed into the root signature itself, copied into the command-list, or referenced indirectly in heap allocated memory which results in much the same thing as a Direct3D 10/11 constant buffer. With Direct3D 12, you have to ensure you don't change the memory until after the GPU has finished drawing with the references, which means in many cases explicitly allocating many small chunks of memory in a frame to serve as a constant buffer, then not reclaiming that memory until appropriate GPU fences are crossed. In other words, it's all up to the application to handle what the Direct3D Runtime used to do for you.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .