Parameters = { StructBuffer boneMatrices : auto("BoneMatrices"); }; Technique #ifdef USE_BLEND_SHAPES : base("SkinnedMorphVertexInput") = #else : base("SkinnedVertexInput") = #endif { Language = "HLSL11"; Pass = { Common = { struct VStoFS { float4 position : SV_Position; float2 uv0 : TEXCOORD0; float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used }; }; Vertex = { StructuredBuffer boneMatrices; struct VertexInput { float3 position : POSITION; float3 normal : NORMAL; // Note: Half-precision could be used float4 tangent : TANGENT; // Note: Half-precision could be used float2 uv0 : TEXCOORD0; uint4 blendIndices : BLENDINDICES; float4 blendWeights : BLENDWEIGHT; #ifdef USE_BLEND_SHAPES float3 deltaPosition : POSITION1; float4 deltaNormal : NORMAL1; #endif }; struct VertexIntermediate { float3x4 blendMatrix; float3 worldNormal; // Note: Half-precision could be used float4 worldTangent; // Note: Half-precision could be used }; float3x4 getBoneMatrix(uint idx) { float4 row0 = boneMatrices[idx * 3 + 0]; float4 row1 = boneMatrices[idx * 3 + 1]; float4 row2 = boneMatrices[idx * 3 + 2]; return float3x4(row0, row1, row2); } float3x4 getBlendMatrix(VertexInput input) { float3x4 result = input.blendWeights.x * getBoneMatrix(input.blendIndices.x); result += input.blendWeights.y * getBoneMatrix(input.blendIndices.y); result += input.blendWeights.z * getBoneMatrix(input.blendIndices.z); result += input.blendWeights.w * getBoneMatrix(input.blendIndices.w); return result; } float3x3 getSkinnedTangentToLocal(VertexInput input, float3x4 blendMatrix, out float tangentSign) { tangentSign = input.tangent.w * 2.0f - 1.0f; float3 normal = input.normal * 2.0f - 1.0f; float3 tangent = input.tangent.xyz * 2.0f - 1.0f; #ifdef USE_BLEND_SHAPES float3 deltaNormal = (input.deltaNormal.xyz * 2.0f - 1.0f) * 2.0f; normal = normalize(normal + deltaNormal * input.deltaNormal.w); tangent = normalize(tangent - dot(tangent, normal) * normal); #endif normal = mul(blendMatrix, float4(normal, 0.0f)).xyz; tangent = mul(blendMatrix, float4(tangent, 0.0f)).xyz; float3 bitangent = cross(normal, tangent) * tangentSign; tangentSign *= gWorldDeterminantSign; float3x3 result = float3x3(tangent, bitangent, normal); result = transpose(result); return result; } VertexIntermediate getVertexIntermediate(VertexInput input) { VertexIntermediate result; result.blendMatrix = getBlendMatrix(input); float tangentSign; float3x3 tangentToLocal = getSkinnedTangentToLocal(input, result.blendMatrix, tangentSign); float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal); result.worldNormal = float3(tangentToWorld._m02_m12_m22); // Normal basis vector result.worldTangent = float4(tangentToWorld._m00_m10_m20, tangentSign); // Tangent basis vector return result; } float4 getVertexWorldPosition(VertexInput input, VertexIntermediate intermediate) { #ifdef USE_BLEND_SHAPES float4 position = float4(input.position + input.deltaPosition, 1.0f); #else float4 position = float4(input.position, 1.0f); #endif position = float4(mul(intermediate.blendMatrix, position), 1.0f); return mul(gMatWorld, position); } void populateVertexOutput(VertexInput input, VertexIntermediate intermediate, inout VStoFS result) { result.uv0 = input.uv0; result.tangentToWorldZ = intermediate.worldNormal; result.tangentToWorldX = intermediate.worldTangent; } }; }; }; Technique #ifdef USE_BLEND_SHAPES : base("SkinnedMorphVertexInput") = #else : base("SkinnedVertexInput") = #endif { Language = "GLSL"; Pass = { Common = { varying vec2 uv0; varying vec3 tangentToWorldZ; varying vec4 tangentToWorldX; }; Vertex = { in vec3 bs_position; in vec3 bs_normal; in vec4 bs_tangent; in vec2 bs_texcoord0; in uvec4 bs_blendindices; in vec4 bs_blendweights; #ifdef USE_BLEND_SHAPES in vec3 bs_position1; in vec4 bs_normal1; #endif uniform samplerBuffer boneMatrices; struct VertexIntermediate { mat4x3 blendMatrix; vec3 worldNormal; vec4 worldTangent; }; out gl_PerVertex { vec4 gl_Position; }; void getBoneMatrix(uint idx, out mat4x3 result) { mat3x4 temp; temp[0] = texelFetch(boneMatrices, idx * 3 + 0); temp[1] = texelFetch(boneMatrices, idx * 3 + 1); temp[2] = texelFetch(boneMatrices, idx * 3 + 2); result = transpose(temp); } void getBlendMatrix(out mat4x3 result) { mat4x3 boneMatrix; getBoneMatrix(bs_blendindices.x, out boneMatrix); result = bs_blendweights.x * boneMatrix; getBoneMatrix(bs_blendindices.y, out boneMatrix); result += bs_blendweights.y * boneMatrix; getBoneMatrix(bs_blendindices.z, out boneMatrix); result += bs_blendweights.z * boneMatrix; getBoneMatrix(bs_blendindices.w, out boneMatrix); result += bs_blendweights.w * boneMatrix; } void getSkinnedTangentToLocal(mat4x3 blendMatrix, out float tangentSign, out mat3x3 tangentToLocal) { tangentSign = bs_tangent.w * 2.0f - 1.0f; vec3 normal = bs_normal * 2.0f - 1.0f; vec3 tangent = bs_tangent.xyz * 2.0f - 1.0f; #ifdef USE_BLEND_SHAPES vec3 deltaNormal = (bs_normal1.xyz * 2.0f - 1.0f) * 2.0f; normal = normalize(normal + deltaNormal * bs_normal1.w); tangent = normalize(tangent - dot(tangent, normal) * normal); #endif normal = (blendMatrix * vec4(normal, 0.0f)).xyz; tangent = (blendMatrix * vec4(tangent, 0.0f)).xyz; vec3 bitangent = cross(normal, tangent) * tangentSign; tangentSign *= gWorldDeterminantSign; tangentToLocal[0] = tangent.xyz; tangentToLocal[1] = bitangent; tangentToLocal[2] = normal; } void getVertexIntermediate(out VertexIntermediate result) { getBlendMatrix(result.blendMatrix); float tangentSign; mat3 tangentToLocal; getSkinnedTangentToLocal(tangentSign, tangentToLocal); mat3 tangentToWorld = mat3(gMatWorldNoScale) * tangentToLocal; result.worldNormal = tangentToWorld[2]; // Normal basis vector result.worldTangent = vec4(tangentToWorld[0].xyz, tangentSign); // Tangent basis vector } void getVertexWorldPosition(VertexIntermediate intermediate, out vec4 result) { #ifdef USE_BLEND_SHAPES vec4 position = vec4(bs_position + bs_position1, 1.0f); #else vec4 position = vec4(bs_position, 1.0f); #endif position = vec4(intermediate.blendMatrix * position, 1.0f); result = gMatWorld * position; } void populateVertexOutput(VertexIntermediate intermediate) { uv0 = bs_texcoord0; tangentToWorldZ = intermediate.worldNormal; tangentToWorldX = intermediate.worldTangent; } }; }; };