I am trying to implement Vertex Texture Fetching on SM3.0. My motivation is that I don't want to split up my 120 bone humanoid to get room for skinning matrices in the registers, and not complicating my vertices. I wish to Copy all 120 Bone Matrices into a texture and sample it in the vertex shader.
A DX10 sample uses this method:
// Read a matrix(3 texture reads) from a texture containing animation data
float4x4 loadBoneMatrix(uint3 animationData,float bone)
{
// Calculate a UV for the bone for this vertex
float4x4 rval = g_Identity;
// animationData.x and .y are linear offsets. Combine into a single linear offset.
uint baseIndex = animationData.x + animationData.y;
baseIndex += (4*bone); // 4*bone is since each bone is 4 texels to form a float4x4
// Now turn linear offset into 2D coords
uint baseU = baseIndex%g_InstanceMatricesWidth;
uint baseV = baseIndex/g_InstanceMatricesWidth;
// Note that we assume the width of the texture(and just add texels) is an even multiple of the # of texels per bone,
// otherwise we'd have to recalculate the V component per lookup
float4 mat1 = g_txAnimations.Load( uint3(baseU,baseV,0));
float4 mat2 = g_txAnimations.Load( uint3(baseU+1,baseV,0));
float4 mat3 = g_txAnimations.Load( uint3(baseU+2,baseV,0));
// only load 3 of the 4 values, and deocde the matrix from them.
rval = decodeMatrix(float3x4(mat1,mat2,mat3));
return rval;
}
How would this be implemented in SM3.0?
I have looked around and tex2D or tex2Dlod seems to do what I want, but I am not sure how I get Bone[59] when the UVs are between 0.0f and 1.0f. Would I have to make the UV value = 59/120? Would this sample the correct vector, POINT filtering ofc.
In UV-space, a [font=courier new,courier,monospace]u[/font] value of [font=courier new,courier,monospace]0[/font] is the left hand side of the left-most texel. A [font=courier new,courier,monospace]u[/font] value of [font=courier new,courier,monospace]1[/font] is the right hand side of the right-most texel.
If your texture is 120 pixels wide, and you want to fetch the 60th texel (texel number 59), the [font=courier new,courier,monospace]u[/font] coordinate for the centre of that texel is:
[font=courier new,courier,monospace](59.0 + 0.5) / 120.0.[/font]
If you used [font=courier new,courier,monospace]59.0/120.0[/font], you would be exactly between texel #58 and #59. This is probably defined to round-up to #59 (when using point filtering), but I never trust floating-point math that much, so I always make sure that I specify the centre of each texel as above.
yeah I thought you meant for a 16-float matrix, but wasn't sure
if you need more than a float4, then use multiple texels per data-element. E.g. in your DX10 sample above, they're fetching their matrix from 3 texels.
One option is to use one row of pixels for each row of the matrix, so for a 4x4 matrix, you'd have to sample 4 pixels. float u = (bone+0.5)*oneOverNumBones;
float4 row0 = tex2D( buffer, float2( u, 0.5/4.0 ) );
float4 row1 = tex2D( buffer, float2( u, 1.5/4.0 ) );
float4 row2 = tex2D( buffer, float2( u, 2.5/4.0 ) );
float4 row3 = tex2D( buffer, float2( u, 3.5/4.0 ) );
N.B. usually skinning matrices are reduced to 4x3 (or 3x4) sized matrices though to save space, or to a different representation such as an axis+angle float4 and a position+scale float4, or dual-quaternions, etc...
You have to use tex2Dlod or tex2Dgrad in a vertex shader to sample textures. tex2D, like Sample, automatically determines the mip level by calculating the gradients of the texture coordinate in screenspace using the neighboring pixels in the same 2x2 quad. Since you don't have 2x2 quads in a vertex shader you can't calculate gradients this way, consequently you have to either supply the gradients or the mip level manually.
As far as I know z is unused. It doesn't matter what you pass if there are no mipmaps, but 0 corresponds to the top mip level so you can use that if you'd like.