#ifdef USE_BLEND_SHAPES mixin MorphVertexInput #else mixin NormalVertexInput #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 }; 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; #ifdef USE_BLEND_SHAPES float3 deltaPosition : POSITION1; float4 deltaNormal : NORMAL1; #endif }; // Vertex input containing only position data struct VertexInput_PO { float3 position : POSITION; #ifdef USE_BLEND_SHAPES float3 deltaPosition : POSITION1; #endif }; struct VertexIntermediate { float3 worldNormal; // Note: Half-precision could be used float4 worldTangent; // Note: Half-precision could be used float tangentSign; }; float3x3 getTangentToLocal(VertexInput input, out float tangentSign) { 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 float3 bitangent = cross(normal, tangent) * input.tangent.w; tangentSign = input.tangent.w * gWorldDeterminantSign; // Note: Maybe it's better to store everything in row vector format? float3x3 result = float3x3(tangent, bitangent, normal); result = transpose(result); return result; } VertexIntermediate getVertexIntermediate(VertexInput input) { VertexIntermediate result; float tangentSign; float3x3 tangentToLocal = getTangentToLocal(input, 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 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 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; } }; };