Матрица перспективы, которая полностью инкапсулирует объект

Я хотел бы создать проекционную матрицу, которая будет отображать объект с произвольной камеры. Мне удалось настроить viewMatrix, который будет смотреть на объект с произвольного положения глаза, но у меня возникли трудности с настройкой проекционной матрицы.

Учитывая, что объект с центром в (x, y, z), самая дальняя точка которого находится на расстоянии r от центра, может для любой произвольной ориентации быть полностью заключенным в сферу радиуса r с началом (x, y, z), я вычислил свою перспективу матрица следующим образом:

float dist2Object = (float) Math.sqrt(vec.lengthSquared());

float objectRadius = (float) Math.sqrt(3 * Math.pow(0.5, 2));
float far = dist2Object + objectRadius;
float near = dist2Object - objectRadius;

// fov_2 = FOV / 2
float fov_2 = (float) (Math.asin(objectRadius / dist2Object));

float r = (float) (Math.tan(fov_2));
float frustum_length = far - near;

depthProjectionMatrix.m00 = 1 / r;
depthProjectionMatrix.m11 = depthProjectionMatrix.m00;
depthProjectionMatrix.m22 = -((far + near) / frustum_length);
depthProjectionMatrix.m23 = -1;
depthProjectionMatrix.m32 = -((2 * near * far) / frustum_length);
depthProjectionMatrix.m33 = 0;

В моем примере:

  • vec — вектор от камеры к объекту
  • объект представляет собой куб, самая дальняя вершина которого равна (0,5, 0,5, 0,5), что дает r sqrt (0,75)

Насколько я могу судить, геометрия и тригонометрия должны быть правильными, но рендеринг координат с использованием следующего фрагментного шейдера:

#version 150 core

in vec3 pCoord;

out vec4 out_Color;

void main(void) {
    out_Color = vec4(0,1,0,1);
    if(pCoord.x <= -1){
        out_Color = vec4(1,0,0,1);
    }
    if(pCoord.x >= 1){
        out_Color = vec4(0,0,1,1);
    }
    if(pCoord.z <= -1){
        out_Color = vec4(1,0,1,1);
    }
    if(pCoord.z >= 1){
        out_Color = vec4(1,0,1,1);
    }
}

показано на изображении, показывает, что поле зрения слишком узкое и что ближняя и дальняя плоскости тоже слишком узкие.

Как я могу это исправить?


person AdvancedGarde89    schedule 04.07.2014    source источник
comment
Если вы хотите, чтобы близко и далеко просто касались объекта, то расстояние от камеры до объекта должно быть введено в ваши формулы. Я не вижу этого в коде.   -  person Dawnkeeper    schedule 04.07.2014
comment
@Dawnkeeper Вектор от камеры к объекту хранится в vec, а расстояние между камерой и объектом хранится в dist2Object.   -  person AdvancedGarde89    schedule 04.07.2014


Ответы (1)


Давно этого не делал, но, похоже, в расчетах не указан размер области просмотра. Вот что я использовал для проекта. Имейте в виду, что матрица здесь начинается с _11, где вы используете m00:

double aspectRatio = (double)this.viewPort.Width /(double)this.viewPort.Height;
double fov = Math.toRadians(fieldOfView/2.0);
double size = nearClip * Math.tan(fov);

double left = -size* aspectRatio, right = size* aspectRatio, bottom = -size , top = size ;

projectionMatrix.resetToZero();

// the values in comments are for non symetrical frustrum 

// First Column
projectionMatrix._11 = (float) (nearClip/right);//(float) ((2 * nearClip )/ (double)(right - left));

// Second Column
projectionMatrix._22 = (float)(nearClip/top);//(float) (2 * nearClip / (double)(top - bottom));

// Third Column
projectionMatrix._31 = 0;//(float) ((right + left) / (right - left));
projectionMatrix._32 = 0;//(float) ((top + bottom) / (top - bottom));
projectionMatrix._33 = -1*(farClip + nearClip) / (float)(farClip - nearClip);
projectionMatrix._34 = -1;

// Fourth Column
projectionMatrix._43 = -(2 * farClip * nearClip) / (float)(farClip - nearClip);
person Dawnkeeper    schedule 04.07.2014
comment
Благодарю за ваш ответ. Если соотношение сторон равно 1 (квадрат), то верх == право == размер. Поскольку размер = около * загар (FOV/2), присвоение m.11 и m.22 становится: около / (около * размер). Это то же самое, что 1/размер, что сделало бы наши две проекционные матрицы эквивалентными. Единственная разница будет заключаться в том, как рассчитывается FOV (не показано в вашем примере кода). Я проверю правильность моего FOV. - person AdvancedGarde89; 04.07.2014
comment
FOV в моем примере задавался вручную/кодом и не рассчитывался. И ваш FOV, кажется, ... подождите, разве это не должно быть Math.sin(objectRadius / dist2Object)? - person Dawnkeeper; 04.07.2014
comment
Я использую арксинус (обратный грех, Math.asin в java) для вычисления угла от гипотенузы (расстояние) до противоположного (радиус). Однако это не совсем правильно. Я должен использовать арксеканс. Я проверю это сейчас. - person AdvancedGarde89; 04.07.2014
comment
Поскольку у вас есть прямоугольный треугольник, синус должен быть в порядке. см. Вики - person Dawnkeeper; 04.07.2014
comment
Таким образом, исправление расчета FOV на asin(objectRadius / dist2Object) [тригонометрический эквивалент arcsec(dist2Object / objectRadius)] устранило проблему, связанную со слишком маленьким FOV. Тем не менее, все еще есть проблема с расчетом ближней и дальней плоскости, как показано на image Судя по экспериментам, это какая-то нелинейная шкала... но больше я не могу понять. - person AdvancedGarde89; 04.07.2014
comment
Итак, я обнаружил, что большая часть проблемы заключалась в том, что мои векторы не нормализовались (я не делил xyz на w). Вычисление FOV теперь работает, и объекты хорошо связаны по z, что все еще дает некоторое неожиданное число. - person AdvancedGarde89; 04.07.2014