버텍스셰이더 HLSL 기초

 

드디어 HLSL이다. shader8.0 1, 2장 shader9.0 1, 2장을 보고 오면 도움이 될것이다.
자세한 설명은 책에서 참고하고 어셈블리어 모습의 셰이더와 HLSL에 대해서만 알아보자.
소스를 만든지 오래 되어서 설명하기도 힘들고, 솔직히 말해서 구차니즘 때문이다.

 1) 셰이더 변수 선언

LPDIRECT3DVERTEXSHADER9	      g_hVShader = 0;  //버텍스 셰이더의 핸들
LPDIRECT3DVERTEXDECLARATION9  g_pVertexDeclaration = 0;  //버텍스 셰이더 선언자
LPD3DXCONSTANTTABLE           g_pVertexConstantTable = NULL;  //버텍스 상수테이블 접근 핸들

버텍스 셰이더에서는 디바이스 인터페이스의 멤버 함수를 통해 상수 값을 전달했지만 HLSL에서는 LPD3DXCONSTANTTABLE 인터페이스를 통해 상수를 전달한다.

2) 셰이더 생성

HRESULT CreateShader()
{
	const char szHlslShader[] =
"   float4x4 worldViewProj : WORLDVIEWPROJ;                                         \n"
"                                                                                   \n"
"   struct VS_OUTPUT                                                                \n"
"   {                                                                               \n"
"        float4 pos  : POSITION;                                                    \n"
"        float4 diff : COLOR0;                                                      \n"
"   };                                                                              \n"
"                                                                                   \n"
"   VS_OUTPUT VertexShader_main( float3 pos  : POSITION,  float4 diff : COLOR0)     \n"
"   {                                                                               \n"
"        VS_OUTPUT Out = (VS_OUTPUT)0;                                              \n"
"                                                                                   \n"
"        Out.pos = mul(float4(pos, 1), worldViewProj);                              \n"
"        Out.diff = diff;                                                           \n" 
"                                                                                   \n"
"        return Out;                                                                \n"
"   }                                                                               \n"
"";

	D3DVERTEXELEMENT9 Declaration[] =
	{
		{0, 0,  D3DDECLTYPE_FLOAT3,   D3DDECLMETHOD_DEFAULT,   D3DDECLUSAGE_POSITION, 0},
		{0, 12, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT,   D3DDECLUSAGE_COLOR,    0},
		D3DDECL_END()
	};

    if( FAILED(g_d3d_Device->CreateVertexDeclaration( Declaration, &g_pVertexDeclaration )))
    {
		_RELEASE_(g_pVertexDeclaration);
		return E_FAIL;
    }

	ID3DXBuffer* pShaderBuffer;
	ID3DXBuffer* pShaderErrors;

	if (FAILED(D3DXCompileShader(szHlslShader, sizeof(szHlslShader), 0, NULL, 
					"VertexShader_main", "vs_1_1", D3DXSHADER_DEBUG, 
					&pShaderBuffer, &pShaderErrors, &g_pVertexConstantTable)))
	{
		char* str = (char*)pShaderErrors->GetBufferPointer();
		return E_FAIL;
	}

	if (FAILED(g_d3d_Device->CreateVertexShader((DWORD *)pShaderBuffer->GetBufferPointer(), &g_hVShader)))
		return E_FAIL;
	_RELEASE_(pShaderBuffer);
	return S_OK;
}

HLSL 설명은 마지막에 하겠다.
버텍스 선언자는 버텍스 셰이와 다른점이 없다.

D3DXAssembleShader 대신 D3DXCompileShader를 사용하여 셰이더를 컴파일한다.
소스 보고 잘이해하기 바란다. (... 소스 작성한지 오래 되어서 역시 구찮음....)

3) 렌드링
void MoveShip(float fx, float fy)
{
	D3DXMATRIX	mScale1, mRot1, mTran1, mResult1;
	D3DXMATRIX	mScale2, mRot2, mTran2, mResult2;
	D3DXMATRIX	mScale3, mRot3, mTran3, mResult3;

	//본체
	//SRT
	D3DXMatrixScaling( &mScale1, 1.1f, 1.1f, 1.1f );
	D3DXMatrixRotationZ(&mRot1, D3DXToRadian(g_fDegree));
	D3DXMatrixTranslation( &mTran1, fx, fy, 0.0f);

	mResult1 = mScale1 * mRot1 * mTran1;
	if(g_pVertexConstantTable)
	{
		D3DXMATRIX ShaderMatrix = mResult1 * g_matCameraView * g_matProj;

		//Get the transpose
		g_pVertexConstantTable->SetMatrix(g_d3d_Device, "worldViewProj", &ShaderMatrix);

		//Pass the transposed matrix to the shader
		g_d3d_Device->SetVertexDeclaration( g_pVertexDeclaration);
		g_d3d_Device->SetVertexShader( g_hVShader);
		g_d3d_Device->DrawPrimitive( D3DPT_TRIANGLESTRIP, 3, 2 );

		//고정 파이프라인 복구
		g_d3d_Device->SetVertexShader(NULL);
		g_d3d_Device->SetFVF(D3DFVF_CUSTOMVERTEX);
	}
	else
	{
		g_d3d_Device->SetTransform( D3DTS_WORLD, &mResult1);
		g_d3d_Device->DrawPrimitive( D3DPT_TRIANGLESTRIP, 3, 2 );
	}


	//포탑
	D3DXMatrixScaling( &mScale2, 1.0f, 1.0f, 1.0f );
	D3DXMatrixRotationZ(&mRot2, D3DXToRadian(g_fTurretDegree));
	D3DXMatrixTranslation( &mTran2, 0.0f, -0.7f, 0.0f );
	
    mResult2 = mScale2 * mRot2 * mTran2 * mResult1;
	g_d3d_Device->SetTransform( D3DTS_WORLD, &mResult2 );
	g_d3d_Device->DrawPrimitive( D3DPT_TRIANGLELIST, 0, 1 );

	//포
	D3DXMatrixScaling( &mScale3, 0.1f, 0.3f, 1.0f );
	D3DXMatrixRotationZ(&mRot3, 0.0f);
	D3DXMatrixTranslation( &mTran3, 0.0f, -0.1f, -0.5f);

	mResult3 = mScale3 * mRot3 * mTran3 * mResult2;
	g_d3d_Device->SetTransform( D3DTS_WORLD, &mResult3 );
	g_d3d_Device->DrawPrimitive( D3DPT_TRIANGLESTRIP, 3, 2 );
}

상수 테이블 변수가 선언되어 있을 때만 HLSL을 통한 렌드링을 할 것이다.

g_pVertexConstantTable->SetMatrix()를 통해 셰이더로 상수를 전달하고 있다. 전치로 변환하는 부분이 없다.
왜 전치로 변환 하지 않을까?

열우선 팩킹은 명령 슬롯의 절약뿐만 아니라, 일반적으로 상수 레지스터 공간을 절약한다.
그리고
행 우선이나 열 우선 방식으로 패킹하는 것은 행렬의 생성자에 아무런 변화가 없다.(즉 언제나 행 우선순위 방식으로 인지하고 코드를 작성하면 된다).

4) 소멸

	    
        _RELEASE_(g_hVShader);
        _RELEASE_(g_pVertexDeclaration);
        _RELEASE_(g_pVertexConstantTable);

g_pVertexConstantTable도 소멸 시킨다.
 

5) HLSL 설명

float4x4 worldViewProj : WORLDVIEWPROJ;

struct VS_OUTPUT
{
      float4 pos  : POSITION;
      float4 diff : COLOR0;
}

VS_OUTPUT VertexShader_main( float3 pos  : POSITION,  float4 diff : COLOR0)
{
       VS_OUTPUT Out = (VS_OUTPUT)0;

       Out.pos = mul(float4(pos, 1), worldViewProj);
       Out.diff = diff;  

       return Out;
}

SetMatrix()에서 넘긴 상수를 전달받는다. 전치로 바꿀 필요가 없다.
입력 출력 레지스터에 대해 신경쓰는게 많이 준 것을 알수있다.
입력으로 위치와 디퓨즈 칼라값을 들어와서  VS_OUTPUT의 값을 리턴한다.

[펌]
Microsoft DirectX 9 Programmable Graphics Pipeline - 정보문화사
DirectX 실시간 렌더링 실전 테크닉 - 정보 문화사
http://www.t-pot.com