#ifdef USE_BLEND_SHAPES mixin SkinnedMorphVertexInput #else mixin SkinnedVertexInput #endif { code { struct VStoFS { float4 position : SV_Position; float2 uv0 : TEXCOORD0; float3 worldPosition : TEXCOORD1; float3 tangentToWorldZ : NORMAL; // Note: Half-precision could be used float4 tangentToWorldX : TANGENT; // Note: Half-precision could be used }; Buffer 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 }; // Vertex input containing only position data struct VertexInput_PO { float3 position : POSITION; uint4 blendIndices : BLENDINDICES; float4 blendWeights : BLENDWEIGHT; #ifdef USE_BLEND_SHAPES float3 deltaPosition : POSITION1; #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(float4 blendWeights, uint4 blendIndices) { float3x4 result = blendWeights.x * getBoneMatrix(blendIndices.x); result += blendWeights.y * getBoneMatrix(blendIndices.y); result += blendWeights.z * getBoneMatrix(blendIndices.z); result += blendWeights.w * getBoneMatrix(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.blendWeights, input.blendIndices); float tangentSign; float3x3 tangentToLocal = getSkinnedTangentToLocal(input, result.blendMatrix, tangentSign); float3x3 tangentToWorld = mul((float3x3)gMatWorldNoScale, tangentToLocal); // Note: Consider transposing these externally, for easier reads result.worldNormal = float3(tangentToWorld[0][2], tangentToWorld[1][2], tangentToWorld[2][2]); // Normal basis vector result.worldTangent = float4(tangentToWorld[0][0], tangentToWorld[1][0], tangentToWorld[2][0], 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); } float4 getVertexWorldPosition(VertexInput_PO input) { #ifdef USE_BLEND_SHAPES float4 position = float4(input.position + input.deltaPosition, 1.0f); #else float4 position = float4(input.position, 1.0f); #endif float3x4 blendMatrix = getBlendMatrix(input.blendWeights, input.blendIndices); position = float4(mul(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; } }; };