sunLoadingImage
whowedImag
decoration left 1
decoration left 2
transhome
transprojects
transgallery
transarticles
decoration rigth
English
Українська
Show/Hide search bar
black cat logo variable logo
[19 Січ 2013]

Компіляція HLSL шейдерів в DirectX 11

Шейдера це програми для графічних карт, які дозволяють запрограмувати частини конвеєра рендерінгу. До введення шейдерів налаштування рендерінгу було обмежене змінами станів конвеєра рендерінгу. HLSL шейдер заміняє частину графічного конвеєра DirectX. Є наступні етапи графічного конвеєра, які можуть бути замінені : вершинний шейдер, піксельний шейдер, геометричний шейдер, шейдера тесселяції. Основними шейдерами є вершинний шейдер і піксельний шейдер. Інші типи шейдерів є необовязковими.
Розумні COM вказівники для DirectX обєктів
В DirectX використовуються COM обєкти. При необхідності видалити COM обєкт потрібно вручну викликати метод Release(). Про видалення обєкта можна забути і обєкт залишиться у памяті. Для автоматичного видалення простих С++ обєктів можна використати розумні вказівники, наприклад, з стандартної бібліотеки std. Але стандартні розумні вказівники не викликають метода Release. Виправити це можна використанням розумних COM вказівників. Використання таких вказівників є аналогічним до використання стандартних розумних вказівників. Тільки певні методи мають іншу назву, наприклад для отримання вказівника потрібно викликати vertexShader.GetInterfacePtr().
В Visual Studio додайте в project dependencies comsuppw.lib. Включіть файл #include "comdef.h". Також потрібно оголосити типи вказівників, які будуть використовуватися. Далі наведено приклад:
// Включення необхідних файлів
#include "d3d11.h"
#include "D3D11SDKLayers.h"
#include "d3dcompiler.h"
#include "comdef.h"

// Оголошуємо типи розумних вказівників для DirectX Com Objects
_COM_SMARTPTR_TYPEDEF(ID3D11Device, __uuidof(ID3D11Device));
_COM_SMARTPTR_TYPEDEF(ID3D11DeviceContext, __uuidof(ID3D11DeviceContext));
_COM_SMARTPTR_TYPEDEF(ID3D11VertexShader, __uuidof(ID3D11VertexShader));
_COM_SMARTPTR_TYPEDEF(ID3D11PixelShader, __uuidof(ID3D11PixelShader));
_COM_SMARTPTR_TYPEDEF(ID3D11InputLayout, __uuidof(ID3D11InputLayout));
_COM_SMARTPTR_TYPEDEF(ID3DBlob, __uuidof(ID3DBlob));
_COM_SMARTPTR_TYPEDEF(ID3D11ShaderReflection, IID_ID3D11ShaderReflection);
Компіляція DirectX шейдерів в Visual Studio
Задайте для шейдерів розширення .hlsl. Додайте шейдера в проект. Для кожного шейдера через правий клік -> Properties можна задати параметри компіляції. Наприклад, шейдерна модель, яка повинна використовуватися, чи зберігати з прекомпільованим шейдером debug інформацію, який це тип шейдера і тд. Після цього можна зробити Build для проекта, і Visual Studio скомпілює шейдера і запише їх прекомпільовані версії у одну з папок проекта. Прекомпільовані файли шейдерів по замовчуванню будуть мати .cso розширення. Тепер прекомпільовані шейдера можна завантажити в програму, створити з них DirectX шейдера і використовувати для рендерінгу.
Можна скомпілювати шейдера вручну замість компіляції шейдерів у Visual Studio. fxc lighting.vertex /E VertexShaderEntry /Fo lighting_vertex.cso
fxc lighting.pixel /E PixelShaderEntry /Fo lighting_pixel.cso
Не забудьте додати d3d11.lib, d3dcompiler.lib, dxguid.lib в project dependencies.
PS. В наступних прикладах не всюди є перевірки на помилки, що повертаються DirectX функціями.
Завантаження вершинного та піксельного шейдерів:
// Оголошуємо необхідні змінні (наприклад, в класі ShaderProgram)
ID3D11VertexShaderPtr vertexShader(nullptr);
ID3D11PixelShaderPtr pixelShader(nullptr);
ID3D11InputLayoutPtr vertexLayout(nullptr);
std::vector<D3D11_INPUT_ELEMENT_DESC> inputLayoutArray;

// шляхи повинні вказувати на файли прекомпільованих шейдерів (.cso)
void buildShaderProgram(ID3D11Device * d3dDevice,
                  const std::string & vertexShaderPath,
                  const std::string & pixelShaderPath)
{
   HRESULT hr = S_OK;

   // зчитати з диску прекомпільований вершинний шейдер
   ID3DBlobPtr vertexBlob;
   hr = D3DReadFileToBlob(vertexShaderPath.c_str(), &vertexBlob);
   if(FAILED(hr))
   {
      // обробка помилок при неможливості завантаження
      return false;
   }

   // створити вершинний шейдер з прекомпільованого шейдера
   hr = d3dDevice->CreateVertexShader(
         vertexBlob->GetBufferPointer(),
         vertexBlob->GetBufferSize(),
         nullptr, &m_impl->vertexShader);
   if(FAILED(hr))
   {
      // помилка при створенні вершинного шейдера
      return false;
   }

   // зчитати з диску прекомпільований піксельний шейдер
   ID3DBlobPtr pixelBlob;
   hr = D3DReadFileToBlob(pixelShaderPath.c_str(), &pixelBlob);
   if(FAILED(hr))
   {
      // обробка помилок при неможливості завантаження
      return false;
   }

   // створити піксельний шейдер з прекомпільованого шейдера
   hr = d3dDevice->CreatePixelShader(
         pixelBlob->GetBufferPointer(),
         pixelBlob->GetBufferSize(),
         nullptr, &m_impl->pixelShader);
   if(FAILED(hr))
   {
      // помилка при створенні піксельного шейдера
      return false;
   }

   // дістати опис вхідних параметрів вершинного шейдера (атрибути в OpenGL)
   hr = enumInputLayout(d3dDevice, vertexBlob.GetInterfacePtr());
   if(FAILED(hr))
   {
      // неможливо створити input layout по вхідних параметрах
      return false;
   }

   // дістати опис константних буферів, текстур і семплерів (uniforms в OpenGL)
   enumConstantBuffers(d3dDevice, vertexBlob.GetInterfacePtr());
   enumConstantBuffers(d3dDevice, pixelBlob.GetInterfacePtr());

   return true;
}
Дістати дані про вхідні дані (атрибути) для вершинного шейдера та створити Input Layout:
HRESULT enumInputLayout(ID3D11Device * d3dDevice, ID3DBlob * VSBlob)
{
   HRESULT hr = S_OK;

   // Дістати опис для прекомпільованого шейдера
   ID3D11ShaderReflectionPtr vertReflect;
   D3DReflect(
         VSBlob->GetBufferPointer(),
         VSBlob->GetBufferSize(),
         IID_ID3D11ShaderReflection,
         (void**) &vertReflect
      );

   D3D11_SHADER_DESC descVertex;
   vertReflect->GetDesc(&descVertex);

   // зберегти опис вхідних даних (атрибутів вершинного шейдера)
   inputLayoutArray.clear();
   uint32 byteOffset = 0;
   D3D11_SIGNATURE_PARAMETER_DESC input_desc;
   for (uint i = 0; i    {
      // дістати опис одного з вхідних параметрів
      vertReflect->GetInputParameterDesc(i, &input_desc);
      
      // заповнити структуру даних для подальшого створення input layout
      D3D11_INPUT_ELEMENT_DESC ie;
      ie.SemanticName = input_desc.SemanticName;
      ie.SemanticIndex = input_desc.SemanticIndex;
      ie.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
      ie.InputSlot = i;
      ie.InputSlotClass = D3D11_INPUT_PER_VERTEX_DATA;
      ie.InstanceDataStepRate = 0;
      ie.AlignedByteOffset = byteOffset;
      
      // вирахувати правильний формат вхідного параметра і зсув
      if (input_desc.Mask == 1)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32_FLOAT;
         }
         byteOffset += 4;
      }
      else if (input_desc.Mask <= 3)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32_FLOAT;
         }
         byteOffset += 8;
      }
      else if (input_desc.Mask <= 7)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32_FLOAT;
         }
         byteOffset += 12;
      }
      else if (input_desc.Mask <= 15)
      {
         if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_UINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_UINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_SINT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_SINT;
         }
         else if (input_desc.ComponentType == D3D_REGISTER_COMPONENT_FLOAT32)
         {
            ie.Format = DXGI_FORMAT_R32G32B32A32_FLOAT;
         }
         byteOffset += 16;
      }
      
      inputLayoutArray.push_back(ie);

      // тут можна зберегти інші параметри input_desc, якщо потрібно
   }
   
   // створити input layout зі збережених даних
   uint numElements = inputLayoutArray.size();
   hr = d3dDevice->CreateInputLayout(
         inputLayoutArray.data(),
         numElements,
         VSBlob->GetBufferPointer(),
         VSBlob->GetBufferSize(),
         &vertexLayout
      );

   if(FAILED(hr))
   {
      // неможливо створити input layout
      return hr;
   }
   
   return S_OK;
}
Дістати дані про константні буфери (uniforms в OpenGL)
HRESULT initializeConstantBuffers(ID3D11Device * d3dDevice, ID3DBlob * blob, bool isVS)
{
   // дістати опис для прекомпільованого шейдера
   ID3D11ShaderReflectionPtr pReflector = NULL;
   D3DReflect(
         blob->GetBufferPointer(),
         blob->GetBufferSize(),
         IID_ID3D11ShaderReflection,
         (void**) &pReflector
      );

   D3D11_SHADER_DESC desc;
   pReflector->GetDesc( &desc );

   // дістати опис кожного константного буфера в шейдері
   for (uint i = 0; i < desc.ConstantBuffers; i++)
   {
      // дістати опис буфера
      D3D11_SHADER_BUFFER_DESC shaderBuffer;
      ID3D11ShaderReflectionConstantBuffer * pConstBuffer =
                  pReflector->GetConstantBufferByIndex(i);
      pConstBuffer->GetDesc(&shaderBuffer);
      
      // тут можна зберегти необхідні поля з shaderBuffer

      // дістати опис кожної змінної в константному буфері
      for (uint j = 0; j < shaderBuffer.Variables; j++)
      {
         // опис змінної
         ID3D11ShaderReflectionVariable * pVariable =
                  pConstBuffer->GetVariableByIndex(j);
         D3D11_SHADER_VARIABLE_DESC varDesc;
         pVariable->GetDesc(&varDesc);

         // тип змінної
         D3D11_SHADER_TYPE_DESC varType;
         ID3D11ShaderReflectionType * pType = pVariable->GetType();
         pType->GetDesc(&varType);

         // тут можна зберегти необхідні поля varType і varDesc
      }

      // дістати опис точки привязки для буфера
      D3D11_SHADER_INPUT_BIND_DESC bindingDesc;
      hr = pReflector->GetResourceBindingDescByName(shaderBuffer.Name, &bindingDesc);

      // PS. Дані можна зберегти в наступній структурі
      //struct ConstantBufferDescription
      //{
      // std::pair variables;
      // D3D11_SHADER_INPUT_BIND_DESC bindingDescription;
      //}

   }

   ///////////////////////////////////////////////////

   // Зберегти дані про текстури та семплери
   for(uint i=0; i    {
      D3D11_SHADER_INPUT_BIND_DESC inputBindDesc;
      pReflector->GetResourceBindingDesc(i, &inputBindDesc);
   
      // тут можна зберегти необхідні дані з inputBindDesc
   }

   return S_OK;
}
Встановити поточні шейдера для використання:
// Встановити вершинний шейдер
d3dContext->VSSetShader(vertexShader.GetInterfacePtr(), nullptr, 0);
// Встановити піксельний шейдер
d3dContext->PSSetShader(pixelShader.GetInterfacePtr(), nullptr, 0);



Sun and Black Cat- Ігор Дихта (igor dykhta email) © 2007-2014