読者です 読者をやめる 読者になる 読者になる

hayateasdf's blog

Unity,C#, javascript,C++,python,batなど

スキニング Cube DirectX9 シェーダ版

// -----------------------------------------------------
// main.cpp
// -----------------------------------------------------
#pragma comment(lib, "d3d9.lib")
#pragma comment(lib, "d3dx9.lib")

#include <windows.h>
#include <tchar.h>
#include <d3d9.h>
#include <d3dx9.h>
#include "SkinCube.h"


TCHAR gName[100] = _T("スキニングテスト");
LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam){
	if(mes == WM_DESTROY || mes == WM_CLOSE) { PostQuitMessage(0); return 0; }
	return DefWindowProc(hWnd, mes, wParam, lParam);
}

int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
	MSG msg; HWND hWnd;
	WNDCLASSEX wcex = {sizeof(WNDCLASSEX), CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, NULL, NULL,
		(HBRUSH)(COLOR_WINDOW + 1), NULL, (TCHAR*)gName, NULL};

	if(!RegisterClassEx(&wcex))
		return 0;
	int w = 640, h = 480;
	RECT clientRect = { 0, 0, w, h };
	::AdjustWindowRect(&clientRect, WS_OVERLAPPEDWINDOW, FALSE);

	if(!(hWnd = CreateWindow(gName, gName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, clientRect.right - clientRect.left, clientRect.bottom - clientRect.top, NULL, NULL, hInstance, NULL)))
		return 0;


	//D3D初期化
	D3DPRESENT_PARAMETERS param = {w, h, D3DFMT_UNKNOWN, 0, D3DMULTISAMPLE_NONE, 0, D3DSWAPEFFECT_DISCARD, NULL, TRUE, TRUE, D3DFMT_D24FS8, 0, 0};
	LPDIRECT3D9 d3d;
	IDirect3DDevice9* device;
	if(!(d3d = Direct3DCreate9(D3D_SDK_VERSION))) return 0;

	if(FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &param, &device)))
	if(FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &param, &device)))
	if(FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_HARDWARE_VERTEXPROCESSING, &param, &device)))
	if(FAILED(d3d->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_REF, hWnd, D3DCREATE_SOFTWARE_VERTEXPROCESSING, &param, &device)))
	{
		d3d->Release();
		return 0;
	}


	SkinCube cube_;
	cube_.Initialize(device);

	ShowWindow(hWnd, nCmdShow);

	do
	{
		if(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)){ DispatchMessage(&msg); }
		
		cube_.Update(device);

		
		device->BeginScene();
		device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(128, 128, 128), 1.0f, 0);

		cube_.Render(device);

		device->EndScene();
		device->Present(NULL, NULL, NULL, NULL);

	}while(msg.message != WM_QUIT);


	d3d->Release();
	device->Release();

	return 0;
}
// -----------------------------------------------------
// SkinCube.h
// -----------------------------------------------------
#pragma once

#define SAFE_RELEASE(a) if(a){ a->Release(); a = NULL; }
#define ROT(a) sinf(a) * 20 * 2 * 3.141592f / 180.0f

template<typename T>
class VertexBuffer
{
private:
	IDirect3DVertexBuffer9* vBuffer_;
	DWORD size_;
public:
	VertexBuffer() : vBuffer_(NULL), size_(-1){}
	~VertexBuffer(){ SAFE_RELEASE(vBuffer_); }

	void CreateVertexBuffer(IDirect3DDevice9* device, T* data, DWORD size, DWORD FVF)
	{
		size_ = sizeof(T) * size;
		if(FAILED(device->CreateVertexBuffer(size_, NULL, FVF, D3DPOOL_MANAGED, &vBuffer_, NULL))){
			throw;
		}
		void* p;
		if(FAILED(vBuffer_->Lock(0, size_, (void**)&p, NULL))){
			throw;
		}
		memcpy_s(p, size_, data, size_);
		vBuffer_->Unlock();
		
	}
	void SetStreamSource(IDirect3DDevice9* device, UINT StreamNumber, UINT OffsetInBytes)
	{
		device->SetStreamSource(StreamNumber, vBuffer_, OffsetInBytes, sizeof(T));
	}
};

template <typename T>
class IndexBuffer
{
private:
	IDirect3DIndexBuffer9* iBuffer_;
	DWORD size_;
public:
	IndexBuffer() : iBuffer_(NULL), size_(-1){}
	~IndexBuffer(){ SAFE_RELEASE(iBuffer_); }

	void CreateIndexBuffer(IDirect3DDevice9* device, T* data, DWORD size)
	{
		size_ = sizeof(T) * size;
		if(FAILED(device->CreateIndexBuffer(size_, D3DUSAGE_WRITEONLY, D3DFMT_INDEX16, D3DPOOL_MANAGED, &iBuffer_, NULL))){
			throw;
		}
		void* p;
		if(FAILED(iBuffer_->Lock(0, size_, (void**)&p, NULL))){
			throw;
		}
		memcpy_s(p, size_, data, size_);
		iBuffer_->Unlock();
	}
	void SetIndices(IDirect3DDevice9* device)
	{
		device->SetIndices(iBuffer_);
	}
};

class SkinCube
{
private:
	struct Coord{
		float x, y, z;
	};
	struct Weight{
		float weight1;
	};
	struct Color{
		DWORD color;
	};
private:
	IDirect3DVertexDeclaration9 *decl_;
	VertexBuffer<Coord> coordBuf_;
	VertexBuffer<Weight> weightBuf_;
	VertexBuffer<Color> colorBuf_;
	IndexBuffer<WORD> iBuffer_;
	DWORD num_vertex_;
	DWORD num_poly_;
	const DWORD FVF_;
	D3DXMATRIX mat_[2];
	D3DXVECTOR3 rot_;

	ID3DXEffect* effect_;
public:
	SkinCube() 
		: FVF_(D3DFVF_XYZB1 | D3DFVF_DIFFUSE)
		, decl_(NULL)
		, num_vertex_(-1)
		, num_poly_(-1)
		, effect_(NULL)
		{ rot_ = D3DXVECTOR3(0, 0, 0); }

	~SkinCube(){ SAFE_RELEASE(decl_); SAFE_RELEASE(effect_); }

	void Initialize(IDirect3DDevice9* device)
	{
		D3DVERTEXELEMENT9 VertexElements[] ={
			{0, 0, D3DDECLTYPE_FLOAT3, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0}, //座標
			{1, 0, D3DDECLTYPE_FLOAT1, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0}, //ウェイト
			{2, 0, D3DDECLTYPE_D3DCOLOR, D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_COLOR, 0}, //カラー
			D3DDECL_END()
		};
		device->CreateVertexDeclaration( VertexElements, &decl_);

		Coord coord[] = {
			//box1
			-0.5f,  0.5f, 0.0f,
			 0.5f,  0.5f, 0.0f,
			 0.5f, -0.5f, 0.0f,
			-0.5f, -0.5f, 0.0f,
			
			-0.5f,  0.5f, 1.0f,
			 0.5f,  0.5f, 1.0f,
			 0.5f, -0.5f, 1.0f,
			-0.5f, -0.5f, 1.0f,
			
			//box2
			 1.5f,  0.5f, 0.0f,
			 1.5f,  0.5f, 1.0f,
			 1.5f, -0.5f, 1.0f,
			 1.5f, -0.5f, 0.0f,

			//box3
			 //2.0f,  0.5f, 0.0f,
			 //2.0f,  0.5f, 1.0f,
			 //2.0f, -0.5f, 1.0f,
			 //2.0f, -0.5f, 0.0f,
		};
		coordBuf_.CreateVertexBuffer(device, coord, sizeof(coord) / sizeof(coord[0]), D3DFVF_XYZ);
		num_vertex_ = sizeof(coord) / sizeof(coord[0]);
		Weight weight[] = 
		{
			//box1
			 0.0f,
			 0.0f,
			 0.0f,
			 0.0f,
			
			 0.0f,
			 0.0f,
			 0.0f,
			 0.0f,
			
			//box2
			 1.0f,
			 1.0f,
			 1.0f,
			 1.0f,

			//box3
			 //1.0f,
			 //1.0f,
			 //1.0f,
			 //1.0f,
		};
		weightBuf_.CreateVertexBuffer(device, weight, sizeof(weight) / sizeof(weight[0]), D3DFVF_DIFFUSE);

		Color color[] = 
		{
			//box1
			 0xffff0000,   // top 左上
			 0xffff0000,	// top 右上
			 0xffff0000,	// top 右下
			 0xffff0000,	// top 左下

			 0xffff0000,	// bot 左上
			 0xffff0000,	// bot 右上
			 0xffff0000,	// bot 右下
			 0xffff0000,	// bot 左下

			//box2
			 ~0,	// right 右上前
			 ~0,	// right 右上奥
			 ~0,	// right 右下奥
			 ~0, 	// right 右下前

			//box3
			//~0,	// right2 右上前
			//~0,	// right2 右上奥
			//~0,	// right2 右下奥
			//~0,	// right2 右下前
		};

		colorBuf_.CreateVertexBuffer(device, color, sizeof(color) / sizeof(color[0]), D3DFVF_DIFFUSE);

		WORD index[] = 
		{
			//box1
			0, 1, 3, 1, 2, 3,
			4, 5, 7, 5, 6, 7,	
			4, 0, 7, 0, 3, 7,
			1, 5, 2, 5, 6, 2,
			4, 5, 0, 5, 1, 0,
			7, 6, 3, 6, 2, 3,

			//box2
			1, 8, 2, 8, 11, 2,
			5, 9, 6, 9, 10, 6,
			5, 9, 1, 9, 8, 1,
			6, 10, 2, 10, 11, 2,
			8, 9, 11, 9, 10, 11,

			//box3
			//8, 12, 11, 12, 15, 11,
			//9, 13, 10, 13, 14, 10,
			//9, 13, 8, 13, 12, 8,
			//10, 14, 11, 14, 15, 11,
			//12, 13, 15, 13, 14, 15,
		};
		
		num_poly_ = (sizeof(index) / sizeof(index[0])) / 3;
		iBuffer_.CreateIndexBuffer(device, &index[0], sizeof(index) / sizeof(index[0]));

		ID3DXBuffer *error = NULL;
		if(FAILED(D3DXCreateEffectFromFile(
		      device,
		      _T("test.fx"),
		      NULL,
		      NULL,
		      D3DXSHADER_DEBUG,
		      NULL,
		      &effect_,
		      &error)))
		 {
			 const char* s = (const char*)error->GetBufferPointer();
			 SAFE_RELEASE(error);
			 throw s;
		 }
	}
	void Transform_(IDirect3DDevice9* device)
	{		
		//ビュー行列作成
		D3DXVECTOR3 vEyePt( 3.0f, 2.0f, -1.0f );
		D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
		D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
		D3DXMATRIXA16 matView;
		D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
		//射影行列作成
		D3DXMATRIXA16 matProj;
		D3DXMatrixPerspectiveFovLH( &matProj, D3DXToRadian(90.0f), (float)640/480, 1.0f, 100.0f );

		effect_->SetMatrix("proj", &matProj);
		effect_->SetMatrix("view", &matView);
	}
	void Update(IDirect3DDevice9* device)
	{
		Transform_(device);
		for(int i = 0; i < 2; ++i)
			D3DXMatrixIdentity(&mat_[i]);
		D3DXMATRIX matRot; 
		D3DXMatrixIdentity(&matRot);

		rot_.x += 0.02f;
		rot_.y += 0.03f;
		rot_.z += 0.05f;
		D3DXMatrixRotationYawPitchRoll(&mat_[1], ROT(rot_.y), ROT(rot_.x), ROT(rot_.z));
		
		for(DWORD i = 0; i < 2; ++i)
			D3DXMatrixInverse(&mat_[i], 0, &mat_[i]);
		effect_->SetMatrixArray("BoneMat", mat_, 2);
	}
	void Render(IDirect3DDevice9* device)
	{
		effect_->SetTechnique("Test");
		UINT numPass;
		effect_->Begin(&numPass, 0);
		effect_->BeginPass(0);
		D3DXMATRIX matWorld;
		D3DXMatrixIdentity(&matWorld);
		effect_->SetMatrix("world", &matWorld);

		coordBuf_.SetStreamSource(device, 0, 0);
		weightBuf_.SetStreamSource(device, 1, 0);
		colorBuf_.SetStreamSource(device, 2, 0);
		device->SetVertexDeclaration(decl_);
		iBuffer_.SetIndices(device);

		device->DrawIndexedPrimitive(D3DPT_TRIANGLELIST, 0, 0, num_vertex_, 0, num_poly_);	

		//effect_->CommitChange();
		effect_->EndPass();
		effect_->End();
	}
};
//---------------------------
//test.fx
//---------------------------
// ワールドビュー射影変換行列宣言
float4x4 world;
float4x4 view;
float4x4 proj;
float4x4 BoneMat[2];

float4x4 testMat;
struct VS_IN
{
   float4 pos : POSITION0;
   float blendWeight : BLENDWEIGHT0;
   float4 color : COLOR0;
};
struct VS_OUT
{
   float4 pos : POSITION0;
   float4 color : COLOR0;
};

VS_OUT SkinCube_vs(VS_IN In)
{
   VS_OUT Out = (VS_OUT)0;
   Out.color = In.color;
   Out.pos = mul(In.pos, BoneMat[In.blendWeight]);
   Out.pos = mul(Out.pos, world);
   Out.pos = mul(Out.pos, view);
   Out.pos = mul(Out.pos, proj);

   return Out;
}
// ピクセルシェーダ
float4 SkinCube_ps( VS_OUT Out) : COLOR
{
   return Out.color;
}

technique Test
{
   pass P0
   {
      VertexShader = compile vs_2_0 SkinCube_vs();
      PixelShader = compile ps_2_0 SkinCube_ps();
   }
}


blendWeightの部分はblendIndexだね。(めんどくさいので修正よろ)

書いてて思ったけどほとんど前のコピペです。ボーンっていうより頂点を動かしてスキンメッシュしているので、ボーンスキンメッシュアニメーションの役に立たないです。
”test.fxのBoneMat[2]”の部分が”effect_->SetMatrixArray("BoneMat", mat_, 2);”で渡している行列になります。何もしない行列と動き行列を渡して、(int)blendWeightの値(インデックス値)のマトリックスを使用します。で、blendWeightが1の場合、頂点が動きます。
まじでWebGLhttp://tyfkda.blogspot.jp/2011/12/webgl.htmlをパクっただけですw

※追記
CreateVertexDeclarationで頂点フォーマットを設定しているので、
CreateVertexBufferのFVFをセットする部分は必要ないので、0を入れてください。