Пришло время для рассмотрения зеркального отражения направленного света. Используемая световая можель — модель «Blinn-Phong», которая в свою очередь — упрощенная Phong-модель.
Рассмотрим модель Phong, чтобы потом проще понять Blinn-Phong.

Модель Phong говорит о том, что specular-компонент пропорционален коинусу между вектором луча света и вектором взгляда. Следующая картинка покажет это графически:

L — вектор от источника света до обрабатываемой шейдером вершины. N — вектор нормали, и Eye — вектор от вершины до «взгляда», т.е. камеры. R — отражение от поверхности вектора L. Коэффициент отражения пропорционален косинусу Альфы.

Если вектор взгляда совпадает с вектором отражения, мы получаем максимальную интенсивность.
По мере отклонения вектора взгляда от вектора отражения, интенсивность уменьшается. Насколько именно уменьшается, зависит от фактора блеска (shinies). Чем выше этот фактор, тем быстрее происходит уменьшение интенсивности. Это значит, что чем выше фактор блеска, тем меньше будет яркое пятно на обьекте, и наоборот. Просто попробуйте изменять значение блеска (в OpenGL между 0 и 128), чтобы контролировать размер бликов.


Shininess = 8 Shininess = 64 Shininess = 128

Формула вектора отражения:

И компонент отражения при использовании модели Phong будет:

Где ‘s’ — значение блеска, Ls — интенсивность рассеянного света, и Ms — коэф. отражения материала.

Блинн предложил более простую и быструю модель, известную как Blinn-Phong, которая базируется на полу-векторе. ‘Полу-вектор’ — вектор с направлением по центру между вектором взгляда и вектором освещения, как показано на следующем изображении:

Интенсивность отражения теперь основана на косинусе угла межу ‘полу-вектором’ и нормалью.
Формула ‘полу-вектора’ намного проще, чем вектора отражения:

И фактор блеска в OpenGL при использовании модели Blinn-Phong:

Именно второй способ в основном и используется в стандартном графическом конвейере большинства железа. Так как мы хотим эмулировать в OpenGL направленный свет, мы воспользуемся последней формулой в нашем шейдере. А вот и хорошая новость: OpenGL рассчитывает для нас ‘Полу-вектор’!
Следующий код делает всю работу:

    /* Рассчитываем specular если NdotL больше нуля */
if (NdotL > 0.0) {// Нормализуем полу-вектор, затем рассчитываем
// косинус (dot product) с нормалью
NdotHV = max(dot(normal, gl_LightSource[0].halfVector.xyz),0.0);
specular = gl_FrontMaterial.specular * gl_LightSource[0].specular *
pow(NdotHV,gl_FrontMaterial.shininess);
}