Как правильно установить глобальные параметры в шейдере hlsl?

Как правильно установить глобальные параметры в шейдере hlsl? Если у меня есть следующие глобальные параметры:

float4x4 World;
float4x4 View;
float4x4 Projection;

И я использую их в вершинном шейдере:

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;
}

Тогда как мне установить эти глобальные значения из кода С++, например. когда камера перемещается? Должен ли я создать еще один шейдер, который просто устанавливает эти значения, к которым я могу получить доступ как к буферу, подобному этому?

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

Пожалуйста, скажите мне, каков правильный способ добиться этого.


person The amateur programmer    schedule 26.10.2014    source источник
comment
Вы уже искали это в Интернете? Это очень простая проблема, которая должна быть решена во многих учебниках. (даже быстрый поиск привел, например, к gamedev.stackexchange.com/questions/13435/)   -  person Gnietschow    schedule 26.10.2014
comment
@Gnietschow Думаю, я не знал правильной терминологии.   -  person The amateur programmer    schedule 26.10.2014
comment
@Gnietschow Также вопрос, на который вы предоставили ссылку, использует эффекты вместо буферов. Я использую буферы и привязываю их к стадии ввода ассемблера.   -  person The amateur programmer    schedule 26.10.2014
comment
Хотя мой пример здесь не подходит, было бы неплохо увидеть, что вы хотя бы приложили немного усилий, чтобы найти свою проблему в Google. Если вы не знаете терминологию, я предлагаю сначала прочитать несколько руководств. Кроме того, вы можете прочитать справочник по hlsl, который немного сложнее, но в основном с примерами: msdn.microsoft.com/en-us/library/windows/desktop/   -  person Gnietschow    schedule 26.10.2014


Ответы (1)


Во-первых, в вашем шейдере вы захотите поместить свои матрицы в постоянный буфер:

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

Если вы не объявляете постоянный буфер, они создаются за вас, но гораздо лучше объявить их явно и сгруппировать по частоте обновления. Например, сгруппируйте вместе все константы, которые обновляются для каждого кадра, и все константы, которые устанавливаются только один раз. Это позволяет вам обновлять только те константы, которые вам нужно обновить, без отправки дополнительных данных в GPU.

Несмотря на то, что они находятся внутри этой структуры cbuffer, доступ к ним в вашем шейдере остается таким же.

В вашем коде C++ вы захотите объявить аналогичную структуру для хранения ваших матриц:

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

Очень важно позаботиться о правила упаковки для постоянных переменных. У этой структуры не будет проблем, но в некоторых случаях вам может потребоваться добавить дополнительное заполнение к вашим структурам C++, чтобы учесть тот факт, что буферы шейдера упаковывают данные так, чтобы они не пересекали 16-байтовые границы из-за 16-байтовой природы структуры. Регистры графического процессора.

Во время инициализации вам также потребуется создать постоянный буфер. Процесс такой же, как и для буфера вершин, за исключением того, что вам нужно будет использовать следующие флаги, чтобы объявить постоянный буфер, доступный для записи ЦП:

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

Всякий раз, когда вы обновляете свои матрицы, вам необходимо загрузить их в GPU. Для этого вы сопоставляете буфер констант с процессором и копируете свою структуру CameraConstants:

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 );

Теперь просто привяжите буфер констант к вашему вершинному шейдеру:

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

Обратите внимание, что первый параметр сопоставляется с регистром, который вы использовали в объявлении буфера шейдера (в данном случае b0).

Еще одна вещь: матрицы в hlsl по умолчанию являются основными по столбцам. Если ваши матрицы являются основными строками в C++ (вероятно), вам нужно либо транспонировать их перед отправкой на графический процессор, либо объявить ваши матрицы как row_major в шейдере.

Ознакомьтесь с примерами DirectX, чтобы найти исходный код всего этого: https://code.msdn.microsoft.com/windowsdesktop/Direct3D-Tutorial-Win32-829979ef

person megadan    schedule 27.10.2014
comment
Должен ли я вызывать VSSetConstantBuffers() только при инициализации буфера констант или каждый раз, когда я изменяю значения в буфере констант? - person The amateur programmer; 27.10.2014
comment
Вам нужно вызывать его только тогда, когда вы хотите привязать другой постоянный буфер к определенному слоту. Итак, на данный момент, если у вас есть только один постоянный буфер, вам нужно вызвать его только один раз, независимо от того, сколько раз вы обновляете содержимое. - person megadan; 27.10.2014