6

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

#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);
}

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

变形目标面部动画

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

而且,该算法在一个正常的长方体上完美地工作,它在 Blender 中创建了扭曲的混合形状:

Cube Twist Morph 目标动画

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

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

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

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

#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;
}

最后是我从更新代码中得到的动画:

更新了变形动画

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

更新:

所以按照建议,如果dot(vSmile_R, nSmile_R) < 0得到以下图像结果,我翻转了法线。

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

在此处输入图像描述

4

2 回答 2

2

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

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

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

初始网格的数据向量“I”最终网格的位置数据大小相等的向量“F” 它们的计数和顺序必须匹配才能使镶嵌保持不变。

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

当前_(λ) = 初始_ + λ。(final_ - initial_) = (1 - λ) 。初始_ + λ 。最后_。


从这个角度来看,这

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 的一条线),因此这些属性的含义尚不清楚。

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


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

如对此的答案所述

OpenGL 着色器编译器可以优化制服上的表达式吗?,

着色器优化器可以很好地优化纯统一表达式,如使用完成的 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;

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

如果没有进行相应的优化,这意味着每个顶点每帧执行一次,因此对于 LOD 为 1000 个顶点和 60Hz 的人脸,GPU 将每秒执行 60,000 次,而不是一劳永逸地由中央处理器。

如果将这些计算放在她的肩上,任何现代 CPU 都不会放弃灵魂,因此将 M/V/P 矩阵的共同三位一体作为制服传递似乎是合适的,而不是在着色器中构建这些矩阵。

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

于 2014-06-24T09:20:40.637 回答
1

前段时间我有一个非常相似的问题。正如您最终注意到的那样,您的问题很可能在于网格本身。就我而言,这是不一致的网格三角剖分。在 Blender 中使用Triangluate Modifier为我解决了这个问题。也许你也应该试一试。

于 2014-11-05T05:31:03.177 回答