Как преобразовать направленный свет в пространство камеры в GLSL
У меня есть следующий код GLSL для освещения:
uniform vec3 lightDir; // Parallel light
uniform float ambient;
uniform vec3 lightColour;
void main()
{
gl_Position = ftransform();
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
float intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
gl_FrontColor = gl_Color * vec4(lightColour, 1.0) * intensity;
}
Вектор света указан в мировом пространстве. Я устанавливаю ориентацию камеры и положение с помощью gluLookAt
, Поскольку OpenGL предполагает, что вектор света находится в пространстве камеры, свет перемещается вместе с камерой, а не остается в фиксированной ориентации.
Я попытался активировать шейдер и установить вектор света до и после вызова gluLookAt
, но я получаю тот же эффект. Что именно я должен сделать, чтобы правильно преобразовать вектор света?
5 ответов
У вас есть общая проблема, часто встречающаяся в онлайн-учебниках по GLSL. Вы передаете свои значения освещения через униформу. Почему бы вам не использовать glLight
вызовы, как с фиксированным рендерингом функции, и получить значения из gl_LightSource[n]
, Разница в том, что openGL автоматически переведет все в поле зрения.
void main()
{
// remember: directional lights are GL_POSITION with w = 0
vec3 lightDir = vec3(gl_LightSource[0].position);
float ambient = gl_LightSource[0].ambient;
vec3 lightColour = vec3(gl_LightSource[0].diffuse);
gl_Position = ftransform();
vec3 eyeNormal = normalize(gl_NormalMatrix * gl_Normal);
float intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
gl_FrontColor = gl_Color * vec4(lightColour, 1.0) * intensity;
}
Всегда отличный ресурс на эту тему: http://www.lighthouse3d.com/opengl/glsl/index.php?lights
Всегда полезен Cheat Cheat GLSL: http://www.cs.brown.edu/courses/cs123/resources/glslQuickRef.pdf
Вы должны решить, в каком месте вы будете делать освещение. Если вы хотите следовать "стандартному" OpenGL, тогда это пространство просмотра. Ваш световой вектор должен быть повернут вращением части матрицы вида перед тем, как перейти к шейдеру (это самое простое решение).
Вы также можете применять источники света в других местах (касательная, объект, мир или даже пространство экрана - для отложенного затенения), но это другая тема. Также обратите внимание, что для продвинутого использования вам нужно будет забыть OpenGL и делать все с вашими собственными матрицами и шейдерами (без освещения и преобразования по умолчанию).
Проблема в шейдере заключается в том, что у вас есть глазные нормальные пространства и световые пространства.
Я не уверен, но преобразование вашего светового вектора в вашу нормальную матрицу должно решить проблему.
Или, чтобы быть более эффективным, вы можете преобразовать vec освещения в объектном пространстве (в программном коде), тогда никакое преобразование для каждой нормали не требуется.
Я подозреваю, что часть проблемы заключается в: float intensity = max(ambient, dot(eyeNormal, normalize(-lightDir));
,
Окружающий термин в ламбертовских шейдерах масштабируется точечным произведением. Здесь окружающий термин будет утверждаться часто, потому что он, вероятно, будет больше, чем скалярное произведение. Использование gl_NormalMatrix также является проблемой, потому что, как прокомментировано выше, это означает, что lightDir находится в пространстве объектов, в то время как вектор нормали преобразуется из пространства объектов в координаты глаза.
Пытаться:
vec3 n=normalize(normal);
float ndotl=max(0.0, dot(n, normalize(lightDir)));
vec3 diffuse=ambient * ndotl;
Код в шейдере вычисляет направленный свет, который не меняется в зависимости от положения света, поэтому вам следует использовать функции spotLight или pointLight.
Попробуйте найти шейдеры для этих источников света. если вы не можете найти, скажите мне, чтобы я отправил вам один.