OpenGL中的Glitchy Facial Morph目标动画(C ++)

时间:2014-06-12 02:02:20

标签: c++ opengl animation shader morphing

我一直在尝试使用Facial Blendshapes在OpenGL中实现变形目标动画,但遵循this教程。动画的顶点着色器看起来像这样:

#version 400 core

in vec3 vNeutral;
in vec3 vSmile_L;
in vec3 nNeutral;
in vec3 nSmile_L;
in vec3 vSmile_R;
in vec3 nSmile_R;

uniform float left;
uniform float right;
uniform float top;
uniform float bottom;
uniform float near;
uniform float far;

uniform vec3 cameraPosition;
uniform vec3 lookAtPosition;
uniform vec3 upVector;

uniform vec4 lightPosition;

out vec3 lPos;
out vec3 vPos;
out vec3 vNorm;

uniform vec3 pos;
uniform vec3 size;
uniform mat4 quaternion;

uniform float smile_w;

void main(){

    //float smile_l_w = 0.9;
    float neutral_w = 1 - 2 * smile_w;
    clamp(neutral_w, 0.0, 1.0);

    vec3 vPosition = neutral_w * vNeutral + smile_w * vSmile_L + smile_w * vSmile_R;
    vec3 vNormal = neutral_w * nNeutral + smile_w * nSmile_L + smile_w * nSmile_R;
    //vec3 vPosition = vNeutral + (vSmile_L - vNeutral) * smile_w;
    //vec3 vNormal = nNeutral + (nSmile_L - nNeutral) * smile_w;

    normalize(vPosition);
    normalize(vNormal);

    mat4 translate = mat4(1.0, 0.0, 0.0, 0.0,
                          0.0, 1.0, 0.0, 0.0,
                          0.0, 0.0, 1.0, 0.0,
                          pos.x, pos.y, pos.z, 1.0);

    mat4 scale = mat4(size.x, 0.0, 0.0, 0.0,
                      0.0, size.y, 0.0, 0.0,
                      0.0, 0.0, size.z, 0.0,
                      0.0, 0.0, 0.0, 1.0);

    mat4 model = translate * scale * quaternion;

    vec3 n = normalize(cameraPosition - lookAtPosition);
    vec3 u = normalize(cross(upVector, n));
    vec3 v = cross(n, u);

    mat4 view=mat4(u.x,v.x,n.x,0,
                    u.y,v.y,n.y,0,
                    u.z,v.z,n.z,0,
                    dot(-u,cameraPosition),dot(-v,cameraPosition),dot(-n,cameraPosition),1);

    mat4 modelView = view * model;

    float p11=((2.0*near)/(right-left));
    float p31=((right+left)/(right-left));
    float p22=((2.0*near)/(top-bottom));
    float p32=((top+bottom)/(top-bottom));
    float p33=-((far+near)/(far-near));
    float p43=-((2.0*far*near)/(far-near));

    mat4 projection = mat4(p11, 0, 0, 0,
                           0, p22, 0, 0,
                           p31, p32, p33, -1,
                           0, 0, p43, 0);


    //lighting calculation
    vec4 vertexInEye = modelView * vec4(vPosition, 1.0);
    vec4 lightInEye = view * lightPosition;
    vec4 normalInEye = normalize(modelView * vec4(vNormal, 0.0));


    lPos = lightInEye.xyz;
    vPos = vertexInEye.xyz;
    vNorm = normalInEye.xyz;


    gl_Position = projection * modelView * vec4(vPosition, 1.0);
}

虽然变形目标动画的算法有效,但我在最终计算的混合形状上会丢失面。动画有点像跟随gif。

Morph Target Facial Animation

混合形状从名为FaceShift的无标记面部动画软件导出。

但是,该算法在普通长方体上完美运行,并且在Blender中创建了扭曲的混合形状:

Cube Twist Morph Target Animation

我用于面部动画的混合形状有问题吗?或者我在顶点着色器中做错了什么?

----------------------------------------- ---------------------更新---------------------------- ------------------------------

正如所建议的那样,我对顶点着色器进行了所需的更改,并制作了一个新的动画,但我仍然得到了相同的结果。

这里是更新的顶点着色器代码:

#version 400 core

in vec3 vNeutral;
in vec3 vSmile_L;
in vec3 nNeutral;
in vec3 nSmile_L;
in vec3 vSmile_R;
in vec3 nSmile_R;

uniform float left;
uniform float right;
uniform float top;
uniform float bottom;
uniform float near;
uniform float far;

uniform vec3 cameraPosition;
uniform vec3 lookAtPosition;
uniform vec3 upVector;

uniform vec4 lightPosition;

out vec3 lPos;
out vec3 vPos;
out vec3 vNorm;

uniform vec3 pos;
uniform vec3 size;
uniform mat4 quaternion;

uniform float smile_w;

void main(){

    float neutral_w = 1.0 - smile_w;
    float neutral_f = clamp(neutral_w, 0.0, 1.0);

    vec3 vPosition = neutral_f * vNeutral + smile_w/2 * vSmile_L + smile_w/2 * vSmile_R;
    vec3 vNormal = neutral_f * nNeutral + smile_w/2 * nSmile_L + smile_w/2 * nSmile_R;

    mat4 translate = mat4(1.0, 0.0, 0.0, 0.0,
                          0.0, 1.0, 0.0, 0.0,
                          0.0, 0.0, 1.0, 0.0,
                          pos.x, pos.y, pos.z, 1.0);

    mat4 scale = mat4(size.x, 0.0, 0.0, 0.0,
                      0.0, size.y, 0.0, 0.0,
                      0.0, 0.0, size.z, 0.0,
                      0.0, 0.0, 0.0, 1.0);

    mat4 model = translate * scale * quaternion;

    vec3 n = normalize(cameraPosition - lookAtPosition);
    vec3 u = normalize(cross(upVector, n));
    vec3 v = cross(n, u);

    mat4 view=mat4(u.x,v.x,n.x,0,
                    u.y,v.y,n.y,0,
                    u.z,v.z,n.z,0,
                    dot(-u,cameraPosition),dot(-v,cameraPosition),dot(-n,cameraPosition),1);

    mat4 modelView = view * model;

    float p11=((2.0*near)/(right-left));
    float p31=((right+left)/(right-left));
    float p22=((2.0*near)/(top-bottom));
    float p32=((top+bottom)/(top-bottom));
    float p33=-((far+near)/(far-near));
    float p43=-((2.0*far*near)/(far-near));

    mat4 projection = mat4(p11, 0, 0, 0,
                           0, p22, 0, 0,
                           p31, p32, p33, -1,
                           0, 0, p43, 0);


    //lighting calculation
    vec4 vertexInEye = modelView * vec4(vPosition, 1.0);
    vec4 lightInEye = view * lightPosition;
    vec4 normalInEye = normalize(modelView * vec4(vNormal, 0.0));


    lPos = lightInEye.xyz;
    vPos = vertexInEye.xyz;
    vNorm = normalInEye.xyz;


    gl_Position = projection * modelView * vec4(vPosition, 1.0);
}

另外,我的片段着色器看起来像这样。 (与之前相比,我刚添加了新材料设置)

#version 400 core
uniform vec4 lightColor;
uniform vec4 diffuseColor;

in vec3 lPos;
in vec3 vPos;
in vec3 vNorm;

void main(){
    //copper like material light settings
    vec4 ambient = vec4(0.19125, 0.0735, 0.0225, 1.0);
    vec4 diff = vec4(0.7038,    0.27048, 0.0828, 1.0);
    vec4 spec = vec4(0.256777, 0.137622, 0.086014, 1.0);

    vec3 L = normalize (lPos - vPos);
    vec3 N = normalize (vNorm);
    vec3 Emissive = normalize(-vPos);
    vec3 R = reflect(-L, N);
    float dotProd = max(dot(R, Emissive), 0.0);
    vec4 specColor = lightColor*spec*pow(dotProd,0.1 * 128);
    vec4 diffuse = lightColor * diff * (dot(N, L));
    gl_FragColor = ambient + diffuse + specColor;
}

最后我通过更新代码获得了动画:

Updated Morph animation

正如您所看到的,我仍然在变形目标动画中得到一些丢失的三角形/面部。有关该问题的任何更多建议/意见将非常有帮助。再次感谢您的提前。 :)

更新

正如建议的那样,如果dot(vSmile_R, nSmile_R) < 0我翻了法线,我得到了以下图像结果。

此外,我没有从obj文件中获取法线,而是尝试计算自己的(面和顶点法线),但仍然得到了相同的结果。

enter image description here

2 个答案:

答案 0 :(得分:2)

不是回答尝试,我只需要比评论可用的格式更多的格式。

我无法分辨哪些数据实际上是从Fasceshift导出的,以及如何将其放入应用程序的自定义ADT中;我的水晶球目前正在忙于预测FIFA Wold Cup的成绩。

但一般来说,线性变形是一件非常简单的事情:

i nitial网格有一个向量“I”,对于 f inal网格的位置数据,有一个相等大小的向量“F”;他们的计数和排序必须匹配以保持原封不动。

给定j∈[0,count),对应的向量initial_ = I [j],final_ = F [j]和变形因子λ∈[0,1]第j个(从零开始)的当前向量current_ (λ)由

给出

current_(λ)= initial_ +λ。 (final_ - initial_)=(1 - λ)。 initial_ +λ。最后_


从这个角度来看,这个

vec3 vPosition = neutral_w * vNeutral + 
                 smile_w/2 * vSmile_L + smile_w/2 * vSmile_R;

充其量只是半信半疑。

正如我所说,我的水晶球目前已经不存在了,但鉴于OpenGL标准参考框架,命名意味着

vSmile_L = vSmile_R *( - 1,1,1),

这个“*”表示分量乘法,而这反过来意味着通过上面的加法取消变形x分量。

但显然,脸部不会退化成一个平面(投射的pov中的一条线),所以这些属性的含义还不清楚。

这就是我想查看有效数据的原因,如评论中所述。


另一件事,与所讨论的效果无关,而是与着色算法有关。

如本答案中所述

Can OpenGL shader compilers optimize expressions on uniforms?

着色器优化器可以很好地优化纯粹的统一表达式,例如用

完成的M / V / P计算
uniform float left;
uniform float right;
uniform float top;
uniform float bottom;
uniform float near;
uniform float far;

uniform vec3 cameraPosition;
uniform vec3 lookAtPosition;
uniform vec3 upVector;

/* */   

uniform vec3 pos;
uniform vec3 size;
uniform mat4 quaternion;

但我发现依靠这种假设的优化非常乐观。

如果它没有相应地进行优化,这意味着每个顶点每帧执行一次,因此对于具有1000个顶点的LOD的人脸,以及GPU将每秒执行60,000次的60Hz,而不是一次且对于全部由CPU。

如果将这些计算放在她的肩膀上,没有现代的CPU会放弃灵魂,所以将M / V / P矩阵的常见三位一体作为制服似乎是合适的,而不是在着色器中构造那些矩阵。

为了重用着色器中的代码 - glm提供了一种非常简单的方法来在C ++中进行与GL相关的数学运算。

答案 1 :(得分:1)

前一段时间我有a very similar problem。正如您最终注意到的,您的问题很可能在于网格本身。在我的情况下,它是不一致的网格三角测量。在Blender中使用Triangluate Modifier解决了我的问题。也许你也应该尝试一下。