2018년 1월 16일 화요일

Shader in C/C++ Code (HLSL)

Shader in C/C++ Code (HLSL)

이제 HLSL 에서 쉐이더를 어떻게 만들고 컴파일하여 사용하는지를 살펴보자.

struct VOut
{
   float4 position : SV_POSITION;
};

VOut VShader(float4 position : POSITION)
{
   VOut output;
   output.position = position;
   return output;
}


float4 PShader() : SV_Target
{
   return float4(1.0,1.0,1.0,1.0);
}

HLSL로 작성된 버텍스쉐이더와 픽셀쉐이더이다.
외부 파일로 준비하도록 하자.  그 이유는 OpenGL의 쉐이더 컴파일러는 string 을 입력하여 컴파일 하도록 되어 있다. 그러나 Direct3D의 경우 외부 파일을 컴파일 하도록 되어 있다. 물론 string 메모리를 사용하여 컴파일 할 수 있는 방법은 있으나 복잡하다. 그럴 이유가 없다.

참고적으로 D3DCompiler에 대한 함수는 다음 링크를 잠고하기 바란다.

OpenGL의 예 에서와  마찬가지로 다음과 같은 데이터를 준비한다.

struct VERTEX{FLOAT X, Y, Z;};
VERTEX OurVertices[] =
{
  {0.0f, 0.5f, 0.0f},
  {0.45f, -0.5, 0.0f},
  {-0.45f, -0.5f, 0.0f}
};



다음과 같이 버텍스 버퍼를 생성한 후 데이터를 넣도록 하자.
// create the vertex buffer
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));

bd.Usage = D3D11_USAGE_DYNAMIC;                // write access access by CPU and GPU
bd.ByteWidth = sizeof(VERTEX) * 3;             // size is the VERTEX struct * 3
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;       // use as a vertex buffer
bd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;    // allow CPU to write in buffer
dev->CreateBuffer(&bd, NULL, &pVBuffer);       // create the buffer

우리는 OpenGL에서 버텍스 버퍼의 자료의 구조를 Attribute 라는 것으로 설정해 주었다. 이러한 일을 D3D 에서는  D3D11_BUFFER_DESC 가 수행하게 된다.

즉 buffer descriptor 인 것이다.

위의 코드는 Buffer Desc 를 설정하고 d3d device 에 버텍스 버퍼를 하나 만든 것이다.

이렇게 만든 버퍼에 OurVertices 배열에 있는 정점 정보를 전달하도록 하자.
// copy the vertices into the buffer
D3D11_MAPPED_SUBRESOURCE ms;
devcon->Map(pVBuffer, NULL, D3D11_MAP_WRITE_DISCARD, NULL, &ms);    // map the buffer
memcpy(ms.pData, OurVertices, sizeof(OurVertices));                 // copy the data
devcon->Unmap(pVBuffer, NULL);                                      // unmap the buffer


쉐이더의 컴파일

D3DCompileFromFile() 함수는 전달되는 인수도 많고 복잡하다. 그러니 아래와 같이 간단하게 호출할 수 있는 함수를 만들어 사용하도록 하자.

참고적으로 MSDN 에서는 이 함수에 대하여 다음과 같이 말하고 있다.
The D3dcompiler_44.dll or later version of the file contains the D3DCompileFromFile compiler function.

즉 D3DCompileFromFile 을 사용하기 위해서는 D3dcompiler_44.dll 또는 그 이후 버전의 파일이 필요하다.

이 파일은 Windows SDK 의 Direct3D 관런 내용에서 에서 찾아볼수 있을 것이다.


HRESULT CompileShader(const LPCWSTR srcFile, LPCSTR entryPoint, LPCSTR profile, ID3DBlob** blob )
{
// if ( !entryPoint || !profile || !blob ) return E_INVALIDARG;

*blob = nullptr;

UINT flags = D3DCOMPILE_ENABLE_STRICTNESS;

#if defined( DEBUG ) || defined( _DEBUG )
flags |= D3DCOMPILE_DEBUG;
#endif

const D3D_SHADER_MACRO defines[] =
{
"EXAMPLE_DEFINE", "1",
NULL, NULL
};

ID3DBlob* shaderBlob = nullptr;
ID3DBlob* errorBlob = nullptr;

HRESULT hr = D3DCompileFromFile( srcFile, defines, D3D_COMPILE_STANDARD_FILE_INCLUDE,
entryPoint, profile,
flags, 0, &shaderBlob, &errorBlob );
if ( FAILED(hr) )
{
if ( errorBlob )
{
OutputDebugStringA( (char*)errorBlob->GetBufferPointer() );
errorBlob->Release();
}

if ( shaderBlob )
  shaderBlob->Release();

return hr;
}

*blob = shaderBlob;

return hr;
}


위의 함수를 이용하여 컴파일 쉐이더 코드를 컴파일 해보자.

shader.sfx 파일은 위에서 설명한 쉐이더 프로그램 소스코드 이다.
// load and compile the two shaders
ID3D10Blob *VS, *PS;

CompileShader(L"shader.sfx","VShader","vs_4_0",&VS);
CompileShader(L"shader.sfx","PShader","ps_4_0",&PS);


컴파일된 쉐이터 코드들은 아래와 같이하여 device 에 의하여 shader object로 만들 수 있다.
// encapsulate both shaders into shader objects
dev->CreateVertexShader(VS->GetBufferPointer(), VS->GetBufferSize(), NULL, &pVS);
dev->CreatePixelShader(PS->GetBufferPointer(), PS->GetBufferSize(), NULL, &pPS);

생성된 shader object 를 Device Context에서 사용하도록 설정하는 부분이다.
// set the shader objects
devcon->VSSetShader(pVS, 0, 0);
devcon->PSSetShader(pPS, 0, 0);

다음 코드는 버텍스 버퍼의 자료 구조를 쉐이더에게 알려주는 기능을 수행한다. 즉 레이아웃 을 수행하는 것이다. 이러한 것은 GLSL에서도 수행한 적이 있음을 기억하자.
이론적으로는 GLSL과 같으니  자세한 설명은 하지 않겠다.
// create the input layout object
D3D11_INPUT_ELEMENT_DESC ied[] =
{
 {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0,   D3D11_INPUT_PER_VERTEX_DATA, 0}
};

dev->CreateInputLayout(ied, 1, VS->GetBufferPointer(), VS->GetBufferSize(), &pLayout);
devcon->IASetInputLayout(pLayout);



이상으로 HLSL 쉐이더를 C++ 코드에서 어떻게 사용하는지를 설명하였다.

댓글 없음:

댓글 쓰기

Vertex , 정점 정보의 확장

Vertex , 정점 정보의 확장 그동안의 설명에 있어서 우리는 VERTEX 정보에 대하여 3 차원의 위치 정보만 사용하였다. struct VERTEX {float x,y,z;}; 이제 이를 확장하여 색상을...