Advertisement Jump to content
Sign in to follow this  
Florian22222

Skinning shader problem

This topic is 1779 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hello!

 

I am attempting to write my own game engine and am now at the point of implementing skinned meshes.

For testing purposes the gBones-Array only holds identity-matrices.

I have this shader:

uniform extern float4x4 gWVP;
uniform extern texture gMainTex;

static const int MAX_NUM_BONES = 100;
uniform extern float4x4 gBones[MAX_NUM_BONES];

OutputVS TransformVS(float3 pos : POSITION0, 
                     float3 normal : NORMAL0, 
                     float2 tex0 : TEXCOORD0, 
                     uint4 boneIDs : BLENDINDICES0, 
                     float4 boneWeights : BLENDWEIGHT0) 
{ 
  OutputVS outVS = (OutputVS)0; 

  float4 posResult = float4(0.0f,0.0f,0.0f,0.0f); 

  //problem here; the following line works 
  posResult = mul(float4(pos,1.0f), gBones[0]);  
  //but if i write 
  //posResult = mul(float4(pos,1.0f), gBones[boneIDs[0]]); 
  //and just use the blendindex, i dont see the mesh anymore 

  outVS.pos = mul(posResult, gWVP); outVS.color = float4(0.0f,0.0f,0.0f,1.0f); 
  
  outVS.tex0 = tex0; return outVS; 
}

This problem might be caused due to the uint4 type on boneIDs : BLENDINDICES0 in the vs_input. I already tried int4, uint4, float4...

 

Here is my vertex declaration:

D3DVERTEXELEMENT9 CustomVertexFormats::VERTEX_POSITION_NORMAL_UV_BONE4_DECL_ARRAY []=
{
	{0,0,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_POSITION, 0},
	{0,12,D3DDECLTYPE_FLOAT3,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_NORMAL, 0},
	{0,24,D3DDECLTYPE_FLOAT2,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_TEXCOORD, 0},
	{0,32,D3DDECLTYPE_UBYTE4,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDINDICES, 0},
	{0,36,D3DDECLTYPE_FLOAT4,D3DDECLMETHOD_DEFAULT, D3DDECLUSAGE_BLENDWEIGHT, 0},
	D3DDECL_END()
};

I really hope someone can help me. I have been looking all over the internet but just can't find the solution.

 

Thanks in advance,

Flo

Edited by IceBreaker23

Share this post


Link to post
Share on other sites
Advertisement

Do you have the debug runtimes enabled? If there's a mismatch between your vertex shader and your vertex declaration, the debug runtimes will output an error message to your debugger output window.

 

Honestly it's been a very long time since I worked with D3D9, and I don't remember the rules for input types for vertex shaders. If I recall correctly we always used float4 type in the shader for a UBYTE4 vertex element, since D3D9 shaders don't actually support integer type (the compiler maps them to floats in assembly). Taking a quick look at the SDK samples, this is also what the SkinnedMesh sample does.

Share this post


Link to post
Share on other sites

I have working DX9 shaders which use both int4 and float4 for the bone indices. Your shader code is all but identical to the various shaders I use.

 

The only difference in your code is (obviously) boneID[0]. Have you checked that the bone indices in your vertex array are correct?

 

EDIT: Are you sure you're loading each vertex in accordance with the vertex declaration, that you don't have (for instance) weights and indices swapped around?

 

EDIT2: If you're using ConvertToIndexedBlendedMesh:

 

are all the input parameters correct?

 

do you check the vertex declaration for the mesh returned from that call?

Edited by Buckeye

Share this post


Link to post
Share on other sites

Honestly it's been a very long time since I worked with D3D9, and I don't remember the rules for input types for vertex shaders. If I recall correctly we always used float4 type in the shader for a UBYTE4 vertex element, since D3D9 shaders don't actually support integer type (the compiler maps them to floats in assembly). Taking a quick look at the SDK samples, this is also what the SkinnedMesh sample does.

I was sure about this as well, so I tried changing my skinning code from float4 to uint4.. and it still works. I think newer compilers (mine is D3Dcompiler_47.dll, targeting vs_3_0) take care of the conversion under the hood, because I remember having to ifdef that part when my D3D9 path used _43 and D3D11 used _47.

 

Edit: as Buckeye said, can we see the code that writes to the vertex buffer?

Edited by Mona2000

Share this post


Link to post
Share on other sites

Thanks for the many replies!

 

I have checked if the boneIDs are correct. I added this line to my vertex shader:

if(boneIDs[0] < 32 && boneIDs[0] >= 0)
{
	outVS.color = float4(boneIDs[0]/32.0f,0.0f,0.0f,1.0f);
}
else
{
	outVS.color = float4(0.0f,1.0f,0.0f,1.0f);
}

So basically what this does is it makes the vertex some shade of red if the boneIDs[0] is between 0-32. Otherwise its green.

You can see the output as an attached screenshot.

The farther away from the spine the more red it gets -> just logical, since the rootBone is at the spine.

Edited by IceBreaker23

Share this post


Link to post
Share on other sites

And as requested, here the struct I use for the Vertex Buffer:

struct VERTEX_POSITION_NORMAL_UV_BONE4
{
	float x,y,z;
	float nx,ny,nz;
	float uvx,uvy;
	unsigned char boneIDs[4];
	float boneWeights[4];
};

Here the vertex buffer code:

VertexBuffer::VertexBuffer(LPDIRECT3DDEVICE9 d3ddev, void *vertices, int sizeofVertex,int nVertices, IDirect3DVertexDeclaration9 *vertexDeclaration)
{
	d3ddev->CreateVertexBuffer(nVertices*sizeofVertex,0,0,D3DPOOL_MANAGED,&_vertexBuffer,0);

	//lock buffer
	VOID* pVoid;    

	_vertexBuffer->Lock(0, 0, (void**)&pVoid, 0);

	memcpy(pVoid, vertices, nVertices*sizeofVertex);    

	_vertexBuffer->Unlock();

	this->_nVertices = nVertices;

	this->_sizeofVertex = sizeofVertex;

	this->_vertexDeclaration = vertexDeclaration;
}
//assign vertices
	VERTEX_POSITION_NORMAL_UV_BONE4 *vertices = new VERTEX_POSITION_NORMAL_UV_BONE4[mesh->mNumVertices];
	for(unsigned int i = 0;i < mesh->mNumVertices;i++)
	{
		vertices[i].x = mesh->mVertices[i].x;
		vertices[i].y = mesh->mVertices[i].y;
		vertices[i].z = mesh->mVertices[i].z;

		vertices[i].nx = mesh->mNormals[i].x;
		vertices[i].ny = mesh->mNormals[i].y;
		vertices[i].nz = mesh->mNormals[i].z;

		if(hasTextureCoords)
		{
			vertices[i].uvx = mesh->mTextureCoords[0][i].x;
			vertices[i].uvy = mesh->mTextureCoords[0][i].y;
		}else
		{
			vertices[i].uvx = 0.0f;
			vertices[i].uvy = 0.0f;
		}

		for(int j=0;j<4;j++)
		{
			vertices[i].boneWeights[j] = 0.0f;
			vertices[i].boneIDs[j] = 0;
		}
	}

	for(unsigned int i = 0;i<mesh->mNumBones;i++)
	{
		aiBone *bone = mesh->mBones[i];
		const char *boneName = bone->mName.C_Str();
		if(_boneMap.count(boneName) == 0)
		{
			Bone boneStruct;
			boneStruct.offsetMatrix = *(Matrix *)&bone->mOffsetMatrix;
			_boneMap.insert(std::pair<const char *, int>(boneName, _bones.size()));
			_bones.push_back(boneStruct);
		}

		int boneID = _boneMap.find(boneName)->second;
		
		//assign vertex weights
		for(unsigned int j = 0;j<bone->mNumWeights;j++)
		{
			aiVertexWeight weight = bone->mWeights[j];
			unsigned int vertexID = weight.mVertexId;

			bool assignedWeight = false;
			for(unsigned int weightID = 0;weightID<4 && !assignedWeight;weightID++)
			{
				if(vertices[vertexID].boneWeights[weightID] <= 0.0f)
				{
					vertices[vertexID].boneWeights[weightID] = weight.mWeight;
					vertices[vertexID].boneIDs[weightID] = (unsigned short)boneID;
					assignedWeight = true;
				}
			}
			if(!assignedWeight)
			{
				Debug::Log(filename);
				Debug::Log("\nMore bone weights than possible with only 4 weights\n");
			}
		}
	}

	submesh.vertexBuffer = new VertexBuffer(d3ddev,vertices, sizeof(VERTEX_POSITION_NORMAL_UV_BONE4),mesh->mNumVertices,CustomVertexFormats::VERTEX_POSITION_NORMAL_UV_BONE4_DECL);
	

Btw I am using ASSIMP to load data.

 

I just found another weird error.

If I change the input parameters of the shader(so they dont match my vertex declaration anymore) it just doesnt give me an error in d3d9 debug mode.

Share this post


Link to post
Share on other sites

IF all you have posted is correct, the culprit for the problem comes down to gBones[boneIDs[0]]. IF the bone indices are correct but they are being accessed incorrectly, then it must be accessing a matrix other than an identity matrix.

 

That is, if gBones[ 0 ] works but gBones[ some-other-number ] does not work, then gBones[ some-other-number ] is an incorrect matrix. Try:
 

posResult = mul(float4(pos,1.0f), gBones[x]);

where x is any value from 0 to 31.

 

That's quick and easy and, if it works, may indicate there is, indeed, a problem accessing the boneID correctly. Otherwise, your matrices are not correct. It's trivial but we all need more clues.

 

Otherwise, the problem must be somewhere else.

 

EDIT: Although your red-shaded output indicates the bone indices are likely correct, you still haven't checked the actual values of boneIDs[ 0 ]. In general, if you have visually examined code and it appears to be correct, it's a bad trouble-shooting technique to write more code* to check for an error in existing code. That is, you shouldn't back off from boneIDs[ 0 ] to see if your red-green code is correct. If your response is: "What's wrong with the red-green code?" then you've proven the point.

 

*Particularly branching code in shaders. Sometimes shaders don't like unrolling code.

 

The trouble-shooting approach here is:

 

You know where the problem manifests itself: it doesn't appear that boneIDs[ 0 ] = (0 through 31) in your shader. Start there: VERIFY (not guess or say "it makes it red") what number boneIDs[ 0 ] actually returns. You should be able to use PIX.

 

It boneIDs[ 0 ] is not correct, then EXAMINE the values going to the shader.

 

Work your way backwards, step by step, until you find the problem.

Edited by Buckeye

Share this post


Link to post
Share on other sites

Right now I am checking the gBones[] again.

I just set 100 identity matrices to the gBones-array, but it only works(i only see the mesh) from 0-61. If I go 62 or higher I dont see the mesh anymore.

unsigned int nBoneTransforms = 100;
D3DXMATRIX *matrices = new D3DXMATRIX[nBoneTransforms];
for(unsigned int i = 0;i < nBoneTransforms;i++)
{
	D3DXMatrixIdentity(&matrices[i]);
}
if(FAILED(_shader->SetMatrixArray(_gBonesHandle,matrices,nBoneTransforms)))
{
	Debug::Log("Failed setting SetMatrixArray()");
}

Is there anyway that this code would not work?(the SetMatrixArray does not fail)

Share this post


Link to post
Share on other sites

Why are you setting up 100 transforms? Have you examined the values for boneIDs[ ] yet? Please take a look at my previous post. Writing more code and asking why that doesn't work doesn't help anyone, least of all you.

 

EDIT: It appears you're just hacking, hoping to stumble on something that works. Just a suggestion, but why don't you try to solve the problem? cool.png

 

EDIT2: I apologize. That was a bit harsh.

 

If you're saying that gBones[x], for x=0 to 61, works, it appears you've established that you can access (most of) the transforms correctly. However, as mentioned in my previous post, keep to the problem at hand. Don't get sidetracked. It's very likely that the "over 61" problem has nothing to do with your algorithm, possibly shader capabilities.

 

What you're trying to determine is: does boneIDs[ 0 ] access data correctly?

 

As suggested, examine the actual values of boneIDs[ 0 ] in the shader.

Edited by Buckeye

Share this post


Link to post
Share on other sites

Well I wrote that code a while ago to make sure that all the transforms are identity matrices for debugging purposes.

And trying to solve the problem is easier said than done :D Writing more code to check what does not work usually worked for me in CPU programming. Guess that doesnt apply here...

 

 

I checked the boneIDs in PIX and they only encountered values from 0-31 while debugging a few vertices.

 

To recap:

-gBones are all set to identity-matrix(checked by trying out values from 0-31 statically)

-boneIDs are all set to values between 0-31(checked by debugging vertices in PIX)

 

posResult = mul(float4(pos,1.0f),gBones[0]); works

posResult = mul(float4(pos,1.0f),gBones[boneIDs[0]]); does not work

 

Do you have any suggestions for what could be the problem here?

I dont know where to continue debugging. This just seems like a very weird error.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!