Войти
ПрограммированиеФорумГрафика

Отсечение по пирамиде видимости(frustum culling) (2 стр)

Страницы: 1 2
#15
13:35, 21 июля 2014

Вот тут хорошо написано gl_projectionmatrix.html.


#16
15:07, 21 июля 2014

alorken
спасибо

#17
7:22, 4 сен. 2014

В функции D3DXMatrixPerspectiveFovLH происходит умножение вектор-строки на матрицу или матрицы на вектор-столбец?
Я как-то попривык к умножению матрицы на вектор столбец и долго не мог понять смысл умножения матрицы

xScale     0          0               0
0        yScale       0               0
0          0       zf/(zf-zn)         1
0          0       -zn*zf/(zf-zn)     0
where:
yScale = cot(fovY/2)

xScale = yScale / aspect ratio
на вектор - столбец.
Но если умножать вектор-строку на матрицу, то все становится понятным.
Как я понял z вектора преобразуется так, что находится в диапазоне [0, zf]?

#18
10:43, 4 сен. 2014

Помню как то в прошлом году расписывал алгоритм вычисления проекции в видеокарте на совфтверном примере.
alorken
Конечно там все замечательно написано но только про саму матрицу проекции. Для примера буду рассматривать OpenGL.
Во первых рассмотрим само хранение матрицы:
Если представить матрицу в виде следующей структуры:

struct vector4f {
float x;
float y;
float z;
float w;
};
struct matrix4f {
vector4f x;
vector4f y;
vector4f z;
vector4f w;
};
Соответственно матрица проекции будет выглядеть следующем образом
matrix4f proj(0.0f);
proj.x.x=xScale;
proj.y.y=yScale;
proj.z.z=-(_aend+_astart)/(_aend-_astart);
proj.z.w=-1.0f;
proj.w.z=-2.0f*_aend*_astart/(_aend-_astart);
Теперь сразу перейдем к рассмотрению алгоритма расчета проекции:
  int _V4FProjDEF(float *_invector,void *_matrix,float *_viewport_scale,float *_viewport_offset,float *_rez) {
    float *_matrixa=(float *)_matrix;
    float r[5];
// умножение вектора на матрицу
    r[0]=_invector[0]*_matrixa[0]+_invector[1]*_matrixa[4]+_invector[2]*_matrixa[8]+_invector[3]*_matrixa[12];
    r[1]=_invector[0]*_matrixa[1]+_invector[1]*_matrixa[5]+_invector[2]*_matrixa[9]+_invector[3]*_matrixa[13];
    r[2]=_invector[0]*_matrixa[2]+_invector[1]*_matrixa[6]+_invector[2]*_matrixa[10]+_invector[3]*_matrixa[14];
    r[3]=_invector[0]*_matrixa[3]+_invector[1]*_matrixa[7]+_invector[2]*_matrixa[11]+_invector[3]*_matrixa[15];
// интересующая нас часть про отсечение по пирамиде видимости для точки
    r[4]=-r[3]; // коэффициент указывающий на размеры среза пирамиды видимости в плоскости параллельной плоскости проекции пересекающей проецируемую точку.
    int flag=0;
    if (r[0]>r[3]) flag|=0x01;
    if (r[1]>r[3]) flag|=0x02;
    if (r[2]>r[3]) flag|=0x04;
    if (r[0]<r[4]) flag|=0x10;
    if (r[1]<r[4]) flag|=0x20;
    if (r[2]<r[4]) flag|=0x40;
// пересчет проекции в масштабе единичной пирамиды
    if ((r[3]!=0) && (r[3]!=1.0)) {
      r[3]=1.0f/r[3];
      r[0]=r[0]*r[3];
      r[1]=r[1]*r[3];
      r[2]=r[2]*r[3];
    }
// масштабирование вектора по размеру экрана
    _rez[0]=r[0]*_viewport_scale[0]+_viewport_offset[0];
    _rez[1]=r[1]*_viewport_scale[1]+_viewport_offset[1];
    _rez[2]=r[2]*_viewport_scale[2]+_viewport_offset[2];
    _rez[3]=r[3];
    return flag;
  }
От последнего элемента можно избавится если представить матрицу проекции в следующем виде:
matrix4f proj(0.0f);
proj.x.x=xScale*_viewport_scale[0];
proj.y.y=yScale*_viewport_scale[1];
proj.z.z=-(_aend+_astart)/(_aend-_astart)*_viewport_scale[2];
proj.z.w=-1.0f;
proj.w.x=_viewport_offset[0];
proj.w.y=_viewport_offset[1];
proj.w.z=-2.0f*_aend*_astart/(_aend-_astart)+_viewport_offset[2];
Но тогда вторая часть вычисления отсечения потребует характеристик viewport и их в любом случае придется передавать отдельно.
    if (r[0]-_viewport_offset[0]>r[3]*_viewport_scale[0]) flag|=0x01;
    if (r[1]-_viewport_offset[1]>r[3]*_viewport_scale[1]) flag|=0x02;
    if (r[2]-_viewport_offset[2]>r[3]*_viewport_scale[2]) flag|=0x04;
    if (r[0]-_viewport_offset[0]<r[4]*_viewport_scale[0]) flag|=0x10;
    if (r[1]-_viewport_offset[1]<r[4]*_viewport_scale[1]) flag|=0x20;
    if (r[2]-_viewport_offset[2]<r[4]*_viewport_scale[2]) flag|=0x40;
Используя выше приведенный алгоритм можно также проверить попадает ли в область видимости треугольник.
float viewport_scale[3]={0.5*screen_width,0.5*screen_height,(_aend-_astart)*0.5};
float viewport_offset[3]={screen_x,screen_y,_astart};
int v1flag=_V4FProjDEF(&vert1.v,&matrixmodel_proj,viewport_scale,viewport_offset,&screenvert1.v);
int v2flag=_V4FProjDEF(&vert2.v,&matrixmodel_proj,viewport_scale,viewport_offset,&screenvert2.v);
int v3flag=_V4FProjDEF(&vert3.v,&matrixmodel_proj,viewport_scale,viewport_offset,&screenvert3.v);
bool onscreen=((v1flag & v2flag & v3flag & 0x33)==0);
Смысл вычисления отсечения состоит в том что отсечение происходит по кубу, под который подгоняется среда в которой находится проецируемая точка.
После чего проекция пересчитывается в пирамидальные координаты:
    if ((r[3]!=0) && (r[3]!=1.0)) {
      r[3]=1.0f/r[3];
      r[0]=r[0]*r[3];
      r[1]=r[1]*r[3];
      r[2]=r[2]*r[3];
    }
То есть используемый куб отсечения существует только для каждой отдельной проецируемой точки.
Рассмотрим вычисление r[3]:
//для перспективной проекции:
proj.z.w=-1.0f; //_matrixa[11]=-1.0f;
proj.w.w=0.0f; //_matrixa[15]=0.0f;
//для ортогональной:
proj.z.w=0.0f; //_matrixa[11]=0.0f;
proj.w.w=1.0f; //_matrixa[15]=1.0f;
...
r[3]=_invector[0]*_matrixa[3]+_invector[1]*_matrixa[7]+_invector[2]*_matrixa[11]+_invector[3]*_matrixa[15];
_invector[2]*_matrixa[11] - то есть чем больше расстояние точки от экрана тем больше размеры, теперь уже, куба отсечения.
Что бы привести координаты точки в кубическое представление экрана параметр xScale представляют как отношение ширины и высоты экрана, что приводит форму экрана для координат точки, а заодно и основание пирамиды, к квадратной.
//для перспективной проекции:
float xScale = _w*tangFov/_h;
float yScale = tangFov;
//для ортогональной:
float xScale = _w*2.0f/_h;
float yScale = 2.0f;
Координату Z делят на высоту пирамиды _aend-_astart.
//для перспективной проекции:
proj.z.z=-(_aend+_astart)/(_aend-_astart);
//для ортогональной:
proj.z.z=-2.0f/(_aend-_astart);

#19
12:00, 4 сен. 2014

TheGrayWolf
Есть альтернатива классическому отсечению. Чаще всего в играх делают дополнительное отсечение при помощи 6 плоскостей рассматривая область видимости как пирамиду или куб с 6 гранями. Иногда вместо 6 плоскостей берут 4 по сторонам экрана, тогда вычисление значительно упрощается. Достаточно иметь одну матрицу (_NormalsMatrix) которая содержит 4 нормали плоскостей отсечения, с общей точкой (положение камеры).
Проверку отсечения можно выполнить следующим образом:

    v[0]=_invector[0]-campos.x;
    v[1]=_invector[1]-campos.y;
    v[2]=_invector[2]-campos.z;
    r[0]=v[0]*_Nmatrix[0]+v[1]*_Nmatrix[1]+v[2]*_Nmatrix[2];
    r[1]=v[0]*_Nmatrix[4]+v[1]*_Nmatrix[5]+v[2]*_Nmatrix[6];
    r[2]=v[0]*_Nmatrix[8]+v[1]*_Nmatrix[9]+v[2]*_Nmatrix[10];
    r[3]=v[0]*_Nmatrix[12]+v[1]*_Nmatrix[13]+v[2]*_Nmatrix[14];
    bool onscreen=(r[0]>=0 & r[1]>=0 & r[2]>=0 & r[3]>=0);
Но чаще это применяется для отсечения всего объекта, если знаем радиус описанной его окружности и центральную точку:
    v[0]=_centervector[0]-campos.x;
    v[1]=_centervector[1]-campos.y;
    v[2]=_centervector[2]-campos.z;
    r[0]=v[0]*_Nmatrix[0]+v[1]*_Nmatrix[1]+v[2]*_Nmatrix[2];
    r[1]=v[0]*_Nmatrix[4]+v[1]*_Nmatrix[5]+v[2]*_Nmatrix[6];
    r[2]=v[0]*_Nmatrix[8]+v[1]*_Nmatrix[9]+v[2]*_Nmatrix[10];
    r[3]=v[0]*_Nmatrix[12]+v[1]*_Nmatrix[13]+v[2]*_Nmatrix[14];
    bool onscreen=(r[0]>=-radius & r[1]>=-radius & r[2]>=-radius & r[3]>=-radius);
как видно из умножения вектора на матрицу это транспонированное умножение, если использовать транспонированную матрицу нормалей то можно использовать стандартное умножение вектора на матрицу.
#20
12:22, 5 сен. 2014

foxes
Спасибо, буду разбираться.

Страницы: 1 2
ПрограммированиеФорумГрафика

Тема в архиве.