11.4.5 AnimatedModel Effect
在每个时间间隔中,你需要通过硬件(GPU)或软件(CPU)处理过程根据骨骼模型转换模型的网格。在GPU中处理的有点是这样做通常比软件处理快得多。本节我们将创建一个effect在verter shader中转换顶点,这个effect还支持两个全局光源和纹理。
AnimatedModel Vertex Processing
首先是vertex shader,vertex shader接受一个包含顶点位置、法线、纹理坐标、bone索引和权重的结构。每个顶点都拥有bone的索引和对应的权重。注意顶点索引和权重属性是通过XNA的ModelProcessor类进行处理的:
struct a2v { float4 position : POSITION; float3 normal : NORMAL; float2 uv0 : TEXCOORD0; float4 boneIndex : BLENDINDICES0; float4 boneWeight : BLENDWEIGHT0; };
vertex shader 的输出包含顶点的最终位置、法线、纹理坐标、观察向量和两个光线向量:
struct v2f { float4 hposition : POSITION; float2 uv0 : TEXCOORD0; float3 normal : TEXCOORD1; float3 lightVec1 : TEXCOORD2; float3 lightVec2 : TEXCOORD3; float3 eyeVec : TEXCOORD4; };
在顶点处理中,你首先计算用来转换顶点位置和法线的最终bone矩阵:
// Calculate the final bone transformation matrix float4x4 matTransform = matBones[IN.boneIndex.x] * IN.boneWeight.x; matTransform += matBones[IN.boneIndex.y] * IN.boneWeight.y; matTransform += matBones[IN.boneIndex.z] * IN.boneWeight.z; float finalWeight = 1.0f -(IN.boneWeight.x + IN.boneWeight.y + IN.boneWeight.z); matTransform += matBones[IN.boneIndex.w] * finalWeight;
matBones数组是一个包含每个bone配置的矩阵的uniform变量,它的大小是根据GPU的常数寄存器的数量决定的。因为你的shader是工作在shader model 2.0下,所以这个值是58。
接着你根据最终bone矩阵转换顶点位置和法线,之后,由world,view和projection的组合矩阵再次进行转换。
// Transform vertex and normal float4 position = mul(IN.position, matTransform); float3 normal = mul(IN.normal, matTransform); OUT.hposition = mul(position, matWVP); OUT.normal = mul(normal, matWV);
最后计算观察向量和两个光线向量用于计算光线
// Calculate light and eye vectors float4 worldPosition = mul(position, matW); OUT.eyeVec = mul(matVI[3].xyz - worldPosition, matV); OUT.lightVec1 = mul(light1Position - worldPosition, matV); OUT.lightVec2 = mul(light2Position - worldPosition, matV); OUT.uv0 = IN.uv0;
下面是完整的vertex处理代码:
v2f animatedModelVS(a2v IN) { v2f OUT; // Calculate the final bone transformation matrix float4x4 matTransform = matBones[IN.boneIndex.x] * IN.boneWeight.x; matTransform += matBones[IN.boneIndex.y] * IN.boneWeight.y; matTransform += matBones[IN.boneIndex.z] * IN.boneWeight.z; float finalWeight = 1.0f - (IN.boneWeight.x + IN.boneWeight.y +IN.boneWeight.z); matTransform += matBones[IN.boneIndex.w] * finalWeight; // Transform vertex and normal float4 position = mul(IN.position, matTransform); float3 normal = mul(IN.normal, matTransform); OUT.hposition = mul(position, matWVP); OUT.normal = mul(normal, matWV); // Calculate light and eye vectors float4 worldPosition = mul(position, matW); OUT.eyeVec = mul(matVI[3].xyz - worldPosition, matV); OUT.lightVec1 = mul(light1Position - worldPosition, matV); OUT.lightVec2 = mul(light2Position - worldPosition, matV); OUT.uv0 = IN.uv0; return OUT; }
AnimatedModel Pixel Processing
pixel shader中接受的所有数据都是从vertex shader而来的数据的插值。首先要归一化所有向量,确保它们是单位向量:
// Normalize all input vectors float3 normal = normalize(IN.normal); float3 eyeVec = normalize(IN.eyeVec); float3 lightVec1 = normalize(IN.lightVec1); float3 lightVec2 = normalize(IN.lightVec2); float3 halfVec1 = normalize(lightVec1 + eyeVec); float3 halfVec2 = normalize(lightVec2 + eyeVec);
由以上代码你获取了光线计算所需的所有向量。你使用Phong方程进行光线计算,phongShading函数实现了Phong方程并返回一个漫反射和镜面反射量。
// Calculate diffuse and specular color for each light float3 diffuseColor1, diffuseColor2; float3 specularColor1, specularColor2; phongShading(normal, lightVec1, halfwayVec1, light1Color, diffuseColor1, specularColor1); phongShading(normal, lightVec2, halfwayVec2, light2Color, diffuseColor2, specularColor2);
像素还从纹理中读取颜色:
float4 materialColor = tex2D(diffuse1Sampler, IN.uv0);
最后你计算像素的最终颜色:
float4 finalColor; finalColor.a = 1.0f; finalColor.rgb = materialColor *( (diffuseColor1+diffuseColor2) * diffuseColor + ambientLightColor) + (specularColor1 + specularColor2) * specularColor ;
phongShading函数的代码在第10章中已经介绍过了,下面是pixel shader的代码:
float4 animatedModelPS(v2f IN): COLOR0 { // Normalize all input vectors float3 normal = normalize(IN.normal); float3 eyeVec = normalize(IN.eyeVec); float3 lightVec1 = normalize(IN.lightVec1); float3 lightVec2 = normalize(IN.lightVec2); float3 halfwayVec1 = normalize(lightVec1 + eyeVec); float3 halfwayVec2 = normalize(lightVec2 + eyeVec); // Calculate diffuse and specular color for each light float3 diffuseColor1, diffuseColor2; float3 specularColor1, specularColor2; phongShading(normal, lightVec1, halfwayVec1,light1Color, diffuseColor1, specularColor1); phongShading(normal, lightVec2, halfwayVec2,light2Color, diffuseColor2, specularColor2); // Read texture diffuse color float4 materialColor = tex2D(diffuse1Sampler, IN.uv0); // Phong lighting result float4 finalColor; finalColor.a = 1.0f; finalColor.rgb = materialColor *( (diffuseColor1+diffuseColor2) * diffuseColor +ambientLightColor) + (specularColor1+specularColor2) *specularColor ; return finalColor; }
下面是technique代码:
technique AnimatedModel { pass p0 { VertexShader = compile vs_2_0 animatedModelVS(); PixelShader = compile ps_2_a animatedModelPS(); } }
发布时间:2009/5/13 上午8:36:53 阅读次数:5031