Здесь нас интересует вычисление преобразования координат камеры (CC) в координаты нормализованных устройств (NDC).
Подумайте о E
как о положении плоскости проекции в координатах камеры, а не о положении точки глаз в соответствии с плоскостью проекции. В координатах камеры точка обзора по определению находится в начале координат, по крайней мере, в моей интерпретации «Координата камеры» означает: координатный кадр, ориентированный от того места, где вы смотрите на сцену. (Вы можете математически определить перспективное преобразование, центрированное из любого места, но это означает, что ваше пространство для входа не является пространством камеры, imho. Это то, к чему стремится трансформация World-> Camera, как вы увидите в chapter 6)
Резюме :
- вы находитесь в пространстве камеры, следовательно, ваша точка глаза находится в точке (0,0,0)
- вы смотрите в стороне отрицательного Z-оси
- проекционной плоскости параллелен хОу плоскость с размером [-1,1] в обоих направлениях
Это изображение здесь (каждый тик равен 0,5 единицы):
На этой картинке вы можете видеть, что плоскость проекции (нижняя часть серой трапеции) находится в центре (0 , 0, -1) с размером [-1,1] в обоих направлениях X и Y.
Теперь, вместо того чтобы выбрать (0,0, -1) для центра этой плоскости, выбрать произвольное (E.x, E.y, E.z) положение (предполагается, что E.z отрицательно). Но плоскость все еще должна быть параллельна оси xOy и того же размера.
Вы можете видеть, что размер E.xy играет совсем другую роль, чем E.z, причина, по которой E.xy будет вовлечена в вычитание, тогда как E.z будет вовлечена в деление. Это легко увидеть на примере:
- предположить zNear = -ez (не обязательно так, но вы можете на самом деле всегда изменить frustumScale иметь эквивалентную перспективу, удовлетворяющую этим)
- Рассмотрим точку Е (который является центром плоскости проекции).
Какова его координата в пространстве НДЦ? Это (0,0, -1) по определению. То, что вы сделали, - это вытеснение E.xy, но деление на -E_z.
Ваш код получил эту идею, но все-таки некоторые вещи неправильно:
- Во-первых, вы определили
uniform vec2 E;
вместо uniform vec3 E;
(просто опечатка, не имеет большого значения)
- Линия
clipPos.xy = ... ;
о vec2
арифметика. Следовательно, вы можете умножать только скалярные значения (т. Е. Float) или добавлять/вычитать значения vec2
. Следовательно, vec4(E.x, E.y, 0.0, 0.0)
имеет неправильный тип, вместо этого вы должны использовать E.xy
, который имеет правильный тип vec2
.
- Вы должны на самом деле вычесть
E.xy
, а не добавлять его. Это легко увидеть в моем примере выше.
- Наконец, все более тонкие ;-)
Я сделал картину для иллюстрации изменения:
Каждый тик 1 единица в этой картинке. В верхнем левом углу находится ваше координатное пространство камеры с отображаемыми zNear, zFar и двумя возможными проекционными плоскостями. В синем - тот, который используется в объяснении и шейдере here, а красный - тот, который вы сейчас хотите использовать. Цветные области соответствуют тому, что должно быть видно на вашем последнем экране, например. что должно быть в кубе [-1,1]^3 в пространстве НДЦ. Следовательно, если вы используете синюю плоскость проекции, вы хотите получить пространство в правом верхнем углу, и если вы используете красную проекционную плоскость, вы хотите выбрать пространство внизу. Для этого вы можете заметить, что вам необходимо выполнить масштабирование и перевод в пространстве NDC, например. после перспективного деления! (Я думаю, что то, что написано в книге, либо неверно, либо интерпретирует вопрос по-разному).
Следовательно, вы хотите сделать это в Координата euclidean (т. Е. Не однородная координата, например.без W координат):
clipPosEuclideanRed.xy = clipPosEuclideanBlue.xy * (-E.z) - E.xy;
clipPosEuclideanRed.z = clipPosEuclideanBlue.z;
Однако, поскольку вы в однородных координатах, это значения, на самом деле:
clipPosEuclidean.xyz = clipPos.xyz/clipPos.w; // with clipPos.w = -cameraPos.z;
Таким образом, вы должны composate написав:
clipPosRed.xy = clipPosBlue.xy * (-E.z) - E.xy * (-cameraPos.z);
clipPosRed.z = clipPosBlue.z;
Поэтому моим решением этой проблемы было бы добавить только одну строку:
void main()
{
vec4 cameraPos = position + vec4(offset.x, offset.y, 0.0, 0.0);
vec4 clipPos;
clipPos.xy = cameraPos.xy * frustumScale;
// only add this line
clipPos.xy = - clipPos.xy * E.z + E.xy * cameraPos.z;
clipPos.z = cameraPos.z * (zNear + zFar)/(zNear - zFar);
clipPos.z += 2 * zNear * zFar/(zNear - zFar);
clipPos.w = -cameraPos.z;
gl_Position = clipPos;
theColor = color;
}
Я отредактировал свой ответ, улучшив то, что я изначально написал, и добавив объяснение с реальным кодом и цифрой, иллюстрирующей то, что я думаю, задано в книге. – Boris
Рад, что это помогло. Я умножаю затем вычитание, так как это правильный результат: -P Помните, что в вашем NDC цветная область является кубом единицы: границы X красной области в верхнем правом рисунке составляют 1/5 и 3/5 (это соотношение где серые линии пересекают синюю плоскость проекции в верхнем левом рисунке). Затем, если вы сделаете то, что предлагаете применить к этим двум значениям {1/5 | 3/5}, вы получите: ({1/5 | 3/5} - 2) * 5 = {-9; -7}. И если вы сделаете это в правильном порядке: ({1/5 | 3/5} * 5) - 2 = {-1; 1} :-) – Boris
Кстати, плоскость проекции не имеет - размер [-1; 1]. Это просто соглашение, принятое в книге: автор предпочитает масштабировать пространство до (scaleFrustrum), что эквивалентно масштабированию плоскости проектирования (если только E.xy! = 0, я на самом деле не учел это в моем код...). Обратите внимание, что Ez и frustrumScale являются избыточными: перемещение плоскости проекции дальше от глаза равносильно уменьшению его размера, что эквивалентно увеличению frustrumScale (эквивалентно масштабированию) – Boris