我一直在尝试使用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。
混合形状从名为FaceShift的无标记面部动画软件导出。
但是,该算法在普通长方体上完美运行,并且在Blender中创建了扭曲的混合形状:
我用于面部动画的混合形状有问题吗?或者我在顶点着色器中做错了什么?
----------------------------------------- ---------------------更新---------------------------- ------------------------------
正如所建议的那样,我对顶点着色器进行了所需的更改,并制作了一个新的动画,但我仍然得到了相同的结果。
这里是更新的顶点着色器代码:
#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文件中获取法线,而是尝试计算自己的(面和顶点法线),但仍然得到了相同的结果。
答案 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解决了我的问题。也许你也应该尝试一下。