Ваше вычисление половины вектора, похоже, неверно.Половина вектора - это вектор между направлением света и направлением обзора (направлением на камеру), который также является частью, где направление глаза влияет на освещение.Но поскольку вы вычисляете свое освещение в мировом пространстве, а не в пространстве обзора, вы не можете просто использовать положение пространства обзора в качестве направления глаза, вместо этого вам нужно положение наблюдателя / камеры в мировом пространстве в качестве входных данных для шейдера.Да, и не забудьте нормализовать вектор света перед вычислением половины вектора (а также интерполировать нормализованный вектор света).Поэтому замена
halfVectors[i] = normalize(lights.allLights[i].position + lightDirections[i]);
на
uniform vec3 viewerPos;
...
vec3 eyeDirection = normalize(viewerPos - worldPosition.xyz);
...
lightDirections[i] /= dist;
halfVectors[i] = normalize(eyeDirection + lightDirections[i]);
должна помочь.Направление глаза больше не должно изменяться, так как оно не требуется в фрагментном шейдере (оно неявно сохраняется в половинном векторе).
Я не уверен, дает ли интерполяция половинного вектора именноте же результаты, что и при интерполяции направления глаза и вычислении половинного вектора в фрагментном шейдере (таким образом, вы также избавите от множества вариаций).Но по крайней мере это должно работать сейчас.
РЕДАКТИРОВАТЬ: То же самое с коэффициентом затухания.Поскольку оно нелинейно на расстоянии до источника света, вы можете не получить точно такие же результаты, как при расчете его для каждого фрагмента.Но это может быть терпимо, в зависимости от качества тесселяции.