2

What is the correct way of setting global params in hlsl shader? If I have the following global params:

float4x4 World;
float4x4 View;
float4x4 Projection;

And I use them within a vertex shader:

void VertexShaderFunction( in float4 inputPosition : POSITION, in float4 colorIn : COLOR, out float4 posOut : SV_POSITION, out float4 colorOut : COLOUR)
{   
    //Set values for output
    float4 worldPosition = mul(inputPosition, World);
    float4 viewPosition = mul(worldPosition, View);
    float4 position = mul(viewPosition, Projection);


    posOut = position;
    colorOut = colorIn;
}

Then how do I set these global values from c++ code f.e. when camera is moved? Should I create another shader, which just sets these values that I can access as buffer like this?

void SetProjectionMatrix(float4x4 inputMatrix : MATRIX){
    Projection = inputMatrix;
}

Please tell me what is the proper way to achieve this.

The amateur programmer
  • 1,238
  • 3
  • 18
  • 38
  • Did you already searched the internet for this? It is a very basic problem, which should be solved by many tutorials. (even a quick search resulted in e.g. http://gamedev.stackexchange.com/questions/13435/loading-and-using-an-hlsl-shader) – Gnietschow Oct 26 '14 at 12:11
  • @Gnietschow I didn't know right terminology I guess. – The amateur programmer Oct 26 '14 at 13:02
  • @Gnietschow Also the question you provided link to uses effects instead of buffers. I'm using buffers and I bind them to input assembler stage. – The amateur programmer Oct 26 '14 at 13:07
  • Although my example doesn't fit here, it would be nice to see, that you even put in a little effort to google for your problem. If you don't know the terminology, I suggest to read some tutorials first. Moreover you can read the hlsl reference, which is a little more heavy, but mostly with examples: http://msdn.microsoft.com/en-us/library/windows/desktop/ff476896%28v=vs.85%29.aspx – Gnietschow Oct 26 '14 at 18:40

1 Answers1

7

First, in your shader you will want to put your matrices into a constant buffer:

cbuffer CameraBuffer : register( b0 ) {
    float4x4 World;
    float4x4 View;
    float4x4 Projection;
}

If you don't declare a constant buffer, they are created for you, but it is much better to declare them explicitly and group them by frequency of update. For example, group all constants that are updated per frame together and all constants that are only set once together. This allows you to only update the constants that you need to update without sending extra data to the GPU.

Even though they are inside this cbuffer structure, they are still accessed the same way within your shader.

In your C++ code you will want to declare a similar structure to store your matrices:

struct CameraConstants {
    XMFLOAT4X4 world;
    XMFLOAT4X4 view;
    XMFLOAT4X4 projection;
};

It's very important to take care with the packing rules for constant variables. This structure will have no problems, but in some cases you may need to add extra padding to your C++ structures to account for the fact that the shader cbuffers pack data so that it does not cross 16 byte boundaries due to the 16 byte nature of the GPU registers.

During initialization, you'll also need to create a constant buffer. The process is the same as a vertex buffer except you'll need to use the following flags to declare a constant buffer that is writable by the CPU:

cbDesc.Usage = D3D11_USAGE_DYNAMIC;
cbDesc.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbDesc.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

Whenever you update your matrices, you need to upload them to the GPU. To do this, you map the constant buffer to the CPU, and copy over your CameraConstants structure:

D3D11_MAPPED_SUBRESOURCE resource;
m_deviceContext->Map( cameraCbuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &resource );
memcpy( resource.pData, cameraConstants, sizeof( CameraConstants ) );
m_deviceContext->Unmap( cameraCbuffer, 0 );

Now just bind the constant buffer to your vertex shader:

m_deviceContext->VSSetConstantBuffers( 0, 1, &cameraCbuffer );

Note that the first parameter maps to the register you used in your shader cbuffer declaration (b0 in this case).

One more thing, matrices in hlsl are column major by default. If your matrices are row major in C++ (probably), then you need to either transpose them before sending to the GPU or declare your matrices as row_major in the shader.

Check out the DirectX samples for some source code for all of this: https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Tutorial-Win32-829979ef

megadan
  • 1,823
  • 12
  • 18
  • Do I have to call `VSSetConstantBuffers()` just when initializing the constant buffer or every time I change values in the constant buffer? – The amateur programmer Oct 27 '14 at 15:21
  • 1
    You only need to call it when you want to bind a different constant buffer to a particular slot. So for now, if you only have one constant buffer, then you only need to call it once no matter how many times you update the contents. – megadan Oct 27 '14 at 16:59