Почему мой шейдерный свет GLSL перемещается по сцене вместе с объектами, на которые он светит?
Я следую учебному пособию по OpenGL ES 2.0 и объединяю его с учебным пособием по освещению GLSL, которое я нашел, используя удобный чайник из Юты из developer.apple.com.
После долгих потрясений и экспериментов у меня заварен чайник на экране в меру правильно, вращаясь вокруг всех трех осей с помощью "затенения мультяшек" из учебника по освещению. В геометрии есть несколько глюков из-за того, что я просто рисую весь список вершин в виде треугольных полос (если вы посмотрите в файл teapot.h, там будет встроен '-1', где я должен начинать новые треугольные полоски, но это только тестовые данные и не имеющие отношения к моей проблеме).
Что меня действительно смущает, так это то, как нужно расположить свет на сцене. В моем коде Objective-C у меня есть вектор с плавающей точкой 3, который содержит {0,1,0}, и я передаю его в шейдер, чтобы затем рассчитать интенсивность света.
Почему свет появляется в сцене тоже? Я имею в виду, что свет ведет себя так, как будто он прикреплен к чайнику невидимой палочкой, всегда указывая на одну и ту же сторону независимо от того, в каком направлении смотрит чайник.
Это вершинный шейдер
attribute vec4 Position;
attribute vec4 SourceColor;
attribute vec3 Normal;
uniform mat4 Projection;
uniform mat4 Modelview;
varying vec3 normal;
void main(void) {
normal = Normal;
gl_Position = Projection * Modelview * Position;
}
"Положение" устанавливается кодом Obj-C и является вершинами для объекта, "Нормальное" - это список нормалей как из массива вершин (VBO), так и "Проекция" и "Представление модели" вычисляются следующим образом:
(CC3GLMatrix из библиотеки Cocos3D, упомянутой в учебном пособии GLES, связанном выше)
CC3GLMatrix *projection = [CC3GLMatrix matrix];
float h = 4.0f * self.frame.size.height / self.frame.size.width;
[projection populateFromFrustumLeft:-2 andRight:2 andBottom:-h/2 andTop:h/2 andNear:1 andFar:100];
glUniformMatrix4fv(_projectionUniform, 1, 0, projection.glMatrix);
CC3GLMatrix *modelView = [CC3GLMatrix matrix];
[modelView populateFromTranslation:CC3VectorMake(0, 0, -7)];
[modelView scaleBy:CC3VectorMake(30, 30, 30)];
_currentRotation += displayLink.duration * 90;
[modelView rotateBy:CC3VectorMake(_currentRotation, _currentRotation, _currentRotation)];
glUniformMatrix4fv(_modelViewUniform, 1, 0, modelView.glMatrix);
И я установил свет в сцене, делая
float lightDir[] = {1,0,1};
glUniform3fv(_lightDirUniform, 1, lightDir);
Фрагмент шейдера выглядит так
varying lowp vec4 DestinationColor; // 1
varying highp vec3 normal;
uniform highp vec3 LightDir;
void main(void) {
highp float intensity;
highp vec4 color;
intensity = dot(LightDir,normal);
if (intensity > 0.95)
color = vec4(1.0,0.5,0.5,1.0);
else if (intensity > 0.5)
color = vec4(0.6,0.3,0.3,1.0);
else if (intensity > 0.25)
color = vec4(0.4,0.2,0.2,1.0);
else
color = vec4(0.2,0.1,0.1,1.0);
gl_FragColor = color;
}
Пытаясь решить эту проблему, я сталкиваюсь с кодом, который ссылается на (несуществующий в GLES) gl_LightSource и gl_NormalMatrix, но не знаю, что поместить в эквиваленты, которые я должен передать в шейдеры из своего кода. Ссылки на "пространство глаза", "пространство камеры", "пространство мира" и т. Д. Сбивают с толку, я знаю, что, вероятно, мне следует конвертировать вещи между ними, но не понимаю, почему и как (и где - в коде или в шейдере?)
Каждый кадр мне нужно изменить источник света? Код для настройки выглядит слишком упрощенно. Я на самом деле не двигаю чайник, не так ли? Я перемещаю всю сцену - свет и все вокруг?
1 ответ
Прежде всего, некоторые определения:
мировое пространство: пространство, в котором определяется весь ваш мир. По соглашению это статическое пространство, которое никогда не движется.
пространство обзора / пространство камеры / пространство глаза: пространство, в котором определяется ваша камера. Обычно это положение и поворот относительно мирового пространства
пространство модели: пространство, в котором определяется ваша модель. Как и пространство камеры, обычно это положение и поворот относительно мирового пространства.
световое пространство: то же, что и модельное пространство
В простых примерах (и, я полагаю, в ваших) модельное пространство и мировое пространство совпадают. Кроме того, OpenGL сам по себе не имеет понятия мирового пространства, что не означает, что вы не можете его использовать. Это пригодится, когда вы хотите, чтобы в вашей сцене независимо перемещалось более одного объекта.
Теперь то, что вы делаете со своим объектом перед рендерингом, это создание матрицы, которая преобразует вершины модели в пространство вида, следовательно, "modelViewMatrix".
Со светом в этом случае все немного по-другому. Подсчет света в вашем шейдере выполняется в пространстве модели, поэтому вы должны преобразовывать свое положение света в каждом кадре в пространство модели.
Это делается путем расчета что-то вроде:
_lightDirUniform = inverseMatrix(model) * inverseMatrix(light) * lightPosition;
Положение света трансформируется из света в мир, а затем в модельное пространство. Если у вас нет мирового пространства, просто не используйте трансформацию модельного пространства, и все будет в порядке.