Я пытался реализовать анимацию Morph Target в OpenGL с помощью Facial Blendshapes, но следую этот руководство. Вершинный шейдер для анимации выглядит примерно так:
#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);
}
Хотя алгоритм для анимации морфируемой цели работает, я получаю недостающие грани в окончательной расчетной форме наложения. Анимация выглядит примерно так:
Смешанные формы экспортируются из безмаркерного программного обеспечения для анимации лица, известного как FaceShift.
Кроме того, алгоритм отлично работает на обычном кубоиде с его искривленной смешанной формой, созданной в Blender:
Может ли это быть что-то не так с blendshapes, которые я использую для лицевой анимации? Или я что-то не так делаю в вершинном шейдере?
—————————————————————Обновить————————————————- ———
Как и предполагалось, я внес изменения, необходимые для вершинного шейдера, и создал новую анимацию, и все же я получаю те же результаты.
Вот обновленный код вершинного шейдера:
#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, я попытался вычислить свои собственные (нормали лица и вершин), и все же я получил тот же результат.
я имел очень похожая проблема некоторое время назад. Как вы в конечном итоге заметили, ваша проблема, скорее всего, заключается в самом меше. В моем случае это была непоследовательная триангуляция сетки. С использованием Модификатор триангулята в блендер решил проблему для меня. Возможно, вам стоит попробовать.
Не попытка ответа, мне просто нужно больше форматирования, чем доступно для комментариев.
Я не могу сказать, какие данные были на самом деле экспортированы из Fasceshift и как они были помещены в пользовательские ADT приложения; мой хрустальный шар в настоящее время занят прогнозированием результатов Кубка мира по футболу.
Но, как правило, линейный морф это очень простая вещь:
Существует один вектор «Я» данных для яазотная сетка и вектор «F» одинакового размера для данных о положении еинальная сетка; их количество и порядок должны совпадать, чтобы тесселяция оставалась неизменной.
Для заданного j ∈ [0, count) соответствующие векторы initial_ = I [j], final_ = F [j] и коэффициент морфинга λ ∈ [0,1] j-го (на основе нуля) текущего вектора current_ (λ) дан кем-то
current_ (λ) = initial_ + λ. (final_ — initial_) = (1 — λ). Initial_ + λ. final_.
С этой точки зрения, это
vec3 vPosition = neutral_w * vNeutral + smile_w/2 * vSmile_L + smile_w/2 * vSmile_R;
выглядит сомнительно в лучшем случае.
Как я уже сказал, мой хрустальный шар в настоящее время не функционирует, но при присвоении имен это будет означать, что, учитывая стандартную систему отсчета OpenGL,
vSmile_L = vSmile_R * (-1,1,1),
это «*» обозначает умножение по компонентам, и это, в свою очередь, означало бы отмену x-компонента morph путем сложения выше.
Но, по-видимому, лицо не вырождается в плоскость (линию от проекции 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 вершин и частотой 60 Гц, что будет выполняться 60000 раз в секунду графическим процессором, а не раз и навсегда ЦПУ.
Ни один современный ЦП не откажется от души, если эти расчеты возложить на ее плечи, поэтому представляется целесообразным передавать общую троицу M / V / P-матриц в униформу вместо построения этих матриц в шейдере.
Для повторного использования кода из шейдеров — GLM предоставляет очень простой способ сделать математику, связанную с GL, в C ++.