c++ - OpenGL 中的故障面部变形目标动画 (C++)

标签 c++ opengl animation shader morphing

我一直在尝试使用面部混合形状在 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);
}

此外,我的片段着色器看起来像这样。 (与之前相比,我只是添加了新的 Material 设置)

#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

最佳答案

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

我不知道哪些数据实际上是从 Fasceshift 导出的,以及这些数据是如何放入应用程序的自定义 ADT 中的;我的 Crystal 球目前正忙于预测 FIFA 世界杯的结果。

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

i初始网格有一个数据 vector “I”,f初始网格的位置数据有一个大小相等的 vector “F”;它们的数量和顺序必须匹配才能使镶嵌保持完整。

给定 j ∈ [0,count),相应的 vector initial_ = I[j], final_ = F[j] 和变形因子 λ ∈ [0,1] 第 j 个(基于零的)当前 vector current_ (λ) 由

current_(λ) = initial_ + λ 。 (final_ - initial_) = (1 - λ ) 。初始_ + λ 。最终_.


从这个角度来看,这

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

充其量看起来很可疑。

正如我所说,我的 Crystal 球目前已经失效,但命名意味着,给定 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 和 60Hz 的人脸,GPU 每秒将执行 60,000 次,而不是一次又一次全部由 CPU 控制。

如果将这些计算放在她的肩上,没有现代 CPU 会放弃灵魂,因此将 M/V/P 矩阵的通用三元组作为制服传递似乎是合适的,而不是在着色器中构建这些矩阵。

重用着色器中的代码 - glm提供了一种非常类似于 glsl 的方式来在 C++ 中进行与 GL 相关的数学运算。

关于c++ - OpenGL 中的故障面部变形目标动画 (C++),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24175414/

相关文章:

c++ - 指向不同类型的指针可以有不同的二进制表示吗?

c++ - 如何在 C++ builder 中渲染 openGL 帧?

macos - NSOpenGLView 子类不随窗口调整大小

python - 强制即时 matplotlib 图形更新

c++ - 如何将字符串中的多个 float 转换为多个 float ? (sscanf)

c++ - UDP TX/RX 中的套接字错误

animation - QML : Fade in/Fade out animation for Image Element

animation - 如何在父状态更改后重建子级(和重播动画)

c++ - 使用字体资源编译

完全不透明或透明的 openGL Alpha 混合