写给小白看的Metahuman: Pre-Scripted(Animated) Wrinkles\Animated Wrinkle map

目录

什么是Wrinkle maps

Wrinkle maps实现原理

metahuman的实现过程

 附上完整代码


什么是Wrinkle maps

通常当人物要表达非常强烈的情绪时,由于五官的挤压会导致面部出现明显的褶皱,比如说抬头纹、脸颊的酒窝等等,这些细节是非常重要的,并且细节的不同甚至会直接导致人物表达的情绪不同。举个例子,当一个人开怀大笑时,影响的肌肉范围不仅有嘴部,眼睛周围也会发生细微的变化。因此,如何自如去控制这些褶皱,并能够根据当前人物的情绪状态自定义褶皱(包括褶皱的强度、开启关闭、blend pose等等)是要解决的关键问题。

Wrinkle maps实现原理

为了能够让美术师(动画师)自由调节褶皱的深浅,开启关闭、或者和其他表情融合(blend),但是又不能影响原来的base normal map。这里采取一种方法:将wrinkle map与传统的base normal map独立出来,专门存储面部褶皱信息。这也是为什么wrinkle map不会烘焙到normal map里去,自由度也可以得到大幅度的增加。

它的计算原理是在base normal map之上直接叠加wrinkle map。

//Add wrinkle map to normal map
float3 vWrinkleNormal = normalize( float3(vWrinkleTS.xy + vNormalTS.xy, vWrinkleTS.z * vNormalTS.z))

有了wrinkle map,这时候人们依然还不满足,有没有办法细分wrinkle map,方便独立控制更多细节呢?

借助mask方法可以实现这点。一张texture map拥有4个颜色通道,每个通道能存一张mask。那么使用不同的mask就能在wrinkle map上划分出不同区域进行控制。将mask贴图采样得到的向量和4mroph target的权重组合成的向量做个点积,就能计算出mask贴图对wrinkle map实际的影响范围。

//Calculate final normal weight
float w = blend.r + foreheadWeight * blend.g + cheekWeight * blend.b;

metahuman的实现过程

1)从头部材质球中,能找到存有四个表情的贴图信息。

2)找到MF_AnimatedMaps这个材质函数,关键信息就隐藏在这里。

 

(4)mroph target权重是标量,存到四维向量里。再与受影响的mask做个点积。得到最后的结果。

(5)判断是否有权重影响值。有权重,就输出,反之,就不输出。

 附上完整代码

Sampler2D sWrinkleMask1; // <LBrow, RBrow, MidBrow, Lips> 
sampler2D sWrinkleMask2; // <LeftCheek, RightCheek, UpLeftCheek, UpRightCheek> 
sampler2D sWrinkleMapSampler1; // Stretch wrinkles map sampler 
sampler2D sWrinkleMapSampler2; // Compress wrinkles map sampler 
sampler2D sNormalMapSampler;   // Normal map sampler 

float4 vWrinkleMaskWeights1; // <LeftBrow, RightBrow, MidBrow, Lips> 
float4 vWrinkleMaskWeights2; // <LCheek, RCheek, UpLeftCheek, UpRightCheek> 

// Compute tangent space wrinkled normal 
float4 ComputeWrinkledNormal ( float2 vUV ) 
{    // Sample the mask textures    
    float4 vMask1 = tex2D( sWrinkleMask1, vUV );    
    float4 vMask2 = tex2D( sWrinkleMask2, vUV );    

    // Mask the weights to get each wrinkle map's influence    
    float fInfluence1 = dot(vMask1, max(0, -vWrinkleMaskWeights1)) +                       
dot(vMask2, max(0, -vWrinkleMaskWeights2));    
    float fInfluence2 = dot(vMask1, max(0, vWrinkleMaskWeights1)) +                         dot(vMask2, max(0, vWrinkleMaskWeights2));    
    
    // Clamp the influence [0,1].  This is only necessary if    
    // there are overlapping mask regions.    
    fInfluence1 = min(fInfluence1, 1);    
    fInfluence2 = min(fInfluence2, 1);    
    
    // Sample the normal & wrinkle maps (we could branch here    
    // if both influences are zero).  Scale and bias to get    
    // vectors into [-1, 1] range.    

    float3 vNormalTS = tex2D(sNormalMapSampler, vUV)*2-1;  // Normal map    
    float3 vWrink1TS = tex2D(sWrinkleMapSampler1, vUV)*2-1;// Wrinkle map 1    
    float3 vWrink2TS = tex2D(sWrinkleMapSampler2, vUV)*2-1;// Wrinkle map 2    
    // Composite the weighted wrinkle maps to get a final wrinkle     

    float3 vWrinkleTS;    
    vWrinkleTS = lerp( float3(0,0,1), vWrink1TS, fInfluence1 );    
    vWrinkleTS = lerp( vWrinkleTS, vWrink2TS, fInfluence2 );    

    // Add final wrinkle to the base normal map    
    vNormalTS = normalize( float3( vWrinkleTS.xy + vNormalTS.xy,                                   vNormalTS.z * vWrinkleTS.z ) );    
    return vNormalTS;
}
Reference

https://www.chrisoat.com/papers/Oat-Wrinkles(Siggraph07).pdf

https://www.chrisoat.com/papers/Chapter4-Oat-Animated_Wrinkle_Maps.pdf

Carl Granberg - Character Animation With Direct3D (2009).pdf

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值