我一直在尝试使用面部混合形状在 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);
}
此外,我的片段着色器看起来像这样。 (与之前相比,我只是添加了新的 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;
}
最后是我通过更新代码获得的动画:
如您所见,变形目标动画中仍然缺少一些三角形/面。关于该问题的任何更多建议/意见将非常有帮助。再次感谢。 :)
更新:
所以按照建议,如果dot(vSmile_R, nSmile_R) < 0
,我翻转了法线我得到了以下图像结果。
此外,我没有从 obj 文件中获取法线,而是尝试计算自己的(面和顶点法线),但我仍然得到相同的结果。
最佳答案
不是尝试回答,我只需要比评论可用的格式更多的格式。
我不知道哪些数据实际上是从 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/