스키닝 셰이더
jjuiddong
- 스키닝에 관련된 소스는 아래 URL을 통해 가져올 수 있다.
- 셰이더 스키닝 하면서 삽질 한 부분.
- D3DFVF_TEX3 는 텍스쳐 좌표를 3개 쓰겠다는 뜻이다.
- D3DFVF_TEX0 은 텍스쳐를 쓰지 않겠다는 뜻이다.
- D3DFVF_TEXCOORDSIZE2(index) 는 멀티 텍스쳐를 쓸 때, 각 텍스쳐의 좌표 차원을 설정할 때 쓰이는 매크로다.
- 버텍스 구조체는 해골책을 참조해서 만들었다.
struct sVertexNormTexSkin { Vector3 p; Vector3 n; float u,v; float weights[4]; float matrixIndices[4]; enum {FVF = (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_TEX3 | D3DFVF_TEXCOORDSIZE2(0)| // texture D3DFVF_TEXCOORDSIZE4(1)| // blend weight D3DFVF_TEXCOORDSIZE4(2)) }; // blend indices };
- 계속
- VertexDeclaration 은 설정할 필요가 없었다. SetFVF() 만으로 충분하다.
- 매트릭스 팔레트는 flot4x3 으로 선언해서 POSITION 값과 곱할 때 여분의 w 값을 무시하도록 해야 한다.
- application 에서 4x4 형태의 행렬을 저장해도 문제 없다.
- 셰이더 코드는 아래와 같다.
// 팔레트 float4x3 mPalette[ 64]; // ------------------------------------------------------------- // 1패스:정점셰이더 // ------------------------------------------------------------- VS_OUTPUT VS_pass0( float4 Pos : POSITION, // 모델정점 float3 Normal : NORMAL, // 법선벡터 float2 Tex : TEXCOORD0, // 텍스쳐 좌표 float4 Weights : TEXCOORD1, // 버텍스 가중치 float4 BoneIndices : TEXCOORD2 // 본 인덱스 (4개 저장) ) { VS_OUTPUT Out = (VS_OUTPUT)0; // 출력데이터 // 좌표변환 float4x4 mWVP = mul(mWorld, mVP); float3 p = {0,0,0}; float3 n = {0,0,0}; p += mul(Pos, mPalette[ BoneIndices.x]) * Weights.x; p += mul(Pos, mPalette[ BoneIndices.y]) * Weights.y; p += mul(Pos, mPalette[ BoneIndices.z]) * Weights.z; p += mul(Pos, mPalette[ BoneIndices.w]) * Weights.w; n += mul(Normal, mPalette[ BoneIndices.x]) * Weights.x; n += mul(Normal, mPalette[ BoneIndices.y]) * Weights.y; n += mul(Normal, mPalette[ BoneIndices.z]) * Weights.z; n += mul(Normal, mPalette[ BoneIndices.w]) * Weights.w; Out.Pos = mul( float4(p,1), mWVP ); n = normalize(n); // 법선 벡터 계산. float3 N = normalize( mul(n, (float3x3)mWIT) ); // 월드 좌표계에서의 법선. Out.N = N; Out.Eye = vEyePos - Pos.xyz; Out.Tex = Tex; return Out; }
//////////////////////////////////////////////////////////////////////////////////////
- D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR 타입으로 fvf 를 설정하고 스키닝을 구현 할 수 있다.
- 핵심은 매트릭스 팔레트를 float4x3 으로 하는게 핵심 이었다. 나머지는 그 동안 삽질한 코드와 동일 하다.
struct sVertexNormTexSkin2 { Vector3 p; float weights[4]; DWORD matrixIndices; Vector3 n; float u,v; enum {FVF = (D3DFVF_XYZB5 | D3DFVF_LASTBETA_D3DCOLOR | D3DFVF_NORMAL | D3DFVF_TEX1)}; };
- 셰이더 코드는 아래와 같다.
// ------------------------------------------------------------- // 1패스:정점셰이더 // ------------------------------------------------------------- VS_OUTPUT VS_pass0( float4 Pos : POSITION, // 모델정점 float3 Normal : NORMAL, // 법선벡터 float2 Tex : TEXCOORD0, // 텍스쳐 좌표 float4 Weights : BLENDWEIGHT, // 버텍스 가중치 float4 BoneIndices : BLENDINDICES// 본 인덱스 (4개 저장) ) { VS_OUTPUT Out = (VS_OUTPUT)0; // 출력데이터 // 좌표변환 float4x4 mWVP = mul(mWorld, mVP); // BoneIndices 는 0~1 사이 값으로 스케일링된 상태이기 때문에 // rgba * 255 를 해줘야 어플리케이션에서 저장한 BoneIndex를 // 얻어올 수 있다. D3DCOLORtoUBYTE4() 함수가 그 역할을 하게 된다. // // ARGB 형태로 어플리케이션 단에서 저장 한 값이 // D3DCOLORtoUBYTE4() 함수를 거치면서 BGRA 형태로 바뀐다. // 저장한 순서대로 Bone Index 를 접근하려면 wzyx 순으로 접근해야 한다. int4 IndexVector = D3DCOLORtoUBYTE4(BoneIndices); float3 p = {0,0,0}; float3 n = {0,0,0}; p += mul(Pos, mPalette[ IndexVector.w]) * Weights.x; p += mul(Pos, mPalette[ IndexVector.z]) * Weights.y; p += mul(Pos, mPalette[ IndexVector.y]) * Weights.z; p += mul(Pos, mPalette[ IndexVector.x]) * Weights.w; n += mul(Normal, mPalette[ IndexVector.w]) * Weights.x; n += mul(Normal, mPalette[ IndexVector.z]) * Weights.y; n += mul(Normal, mPalette[ IndexVector.y]) * Weights.z; n += mul(Normal, mPalette[ IndexVector.x]) * Weights.w; Out.Pos = mul( float4(p,1), mWVP ); n = normalize(n); // 법선 벡터 계산. float3 N = normalize( mul(n, (float3x3)mWIT) ); // 월드 좌표계에서의 법선. Out.N = N; Out.Eye = vEyePos - Pos.xyz; Out.Tex = Tex; return Out; }
- D3DCOLORtoUBYTE4() 함수 때문에 코드가 좀더 복잡해졌다. 때문에 속도도 텍스쳐좌표로 스키닝할 때보다 느리다.