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

2006 - 2024,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号