Как построить матрицу перспективной проекции (без API)
Я разрабатываю простой 3D-движок (без использования API), успешно преобразовывая свою сцену в мир и пространство просмотра, но имею проблемы с проектированием моей сцены (из пространства просмотра) с использованием матрицы перспективных проекций (стиль OpenGL). Я не уверен в значениях fov, near и far, и сцена, которую я получаю, искажена.
Я надеюсь, что кто-то может направить меня, как правильно построить и использовать матрицу перспективной проекции с примерами кодов. Заранее благодарю за любую помощь.
Строка матрицы:
double f = 1 / Math.Tan(fovy / 2);
return new double[,] {
{ f / Aspect, 0, 0, 0 },
{ 0, f, 0, 0 },
{ 0, 0, (Far + Near) / (Near - Far), (2 * Far * Near) / (Near - Far) },
{ 0, 0, -1, 0 }
};
Использование матрицы:
foreach (Point P in T.Points)
{
.
. // Transforming the point to homogen point matrix, to world space, and to view space (works fine)
.
// projecting the point with getProjectionMatrix() specified in the previous code :
double[,] matrix = MatrixMultiply( GetProjectionMatrix(Fovy, Width/Height, Near, Far) , viewSpacePointMatrix );
// translating to Cartesian coordinates (from homogen):
matrix [0, 0] /= matrix [3, 0];
matrix [1, 0] /= matrix [3, 0];
matrix [2, 0] /= matrix [3, 0];
matrix [3, 0] = 1;
P = MatrixToPoint(matrix);
// adjusting to the screen Y axis:
P.y = this.Height - P.y;
// Printing...
}
Ответы
Ответ 1
Ниже приводится типичная реализация проекционной матрицы перспективы.
И вот хорошая ссылка, чтобы объяснить все OpenGL Projection Matrix
void ComputeFOVProjection( Matrix& result, float fov, float aspect, float nearDist, float farDist, bool leftHanded /* = true */ )
{
//
// General form of the Projection Matrix
//
// uh = Cot( fov/2 ) == 1/Tan(fov/2)
// uw / uh = 1/aspect
//
// uw 0 0 0
// 0 uh 0 0
// 0 0 f/(f-n) 1
// 0 0 -fn/(f-n) 0
//
// Make result to be identity first
// check for bad parameters to avoid divide by zero:
// if found, assert and return an identity matrix.
if ( fov <= 0 || aspect == 0 )
{
Assert( fov > 0 && aspect != 0 );
return;
}
float frustumDepth = farDist - nearDist;
float oneOverDepth = 1 / frustumDepth;
result[1][1] = 1 / tan(0.5f * fov);
result[0][0] = (leftHanded ? 1 : -1 ) * result[1][1] / aspect;
result[2][2] = farDist * oneOverDepth;
result[3][2] = (-farDist * nearDist) * oneOverDepth;
result[2][3] = 1;
result[3][3] = 0;
}
Ответ 2
Другая полезная функция.
Этот параметр основан на параметрах left/right/top/bottom/near/far (используется в OpenGL):
static void test(){
float projectionMatrix[16];
// width and height of viewport to display on (screen dimensions in case of fullscreen rendering)
float ratio = (float)width/height;
float left = -ratio;
float right = ratio;
float bottom = -1.0f;
float top = 1.0f;
float near = -1.0f;
float far = 100.0f;
frustum(projectionMatrix, 0, left, right, bottom, top, near, far);
}
static void frustum(float *m, int offset,
float left, float right, float bottom, float top,
float near, float far) {
float r_width = 1.0f / (right - left);
float r_height = 1.0f / (top - bottom);
float r_depth = 1.0f / (far - near);
float x = 2.0f * (r_width);
float y = 2.0f * (r_height);
float z = 2.0f * (r_depth);
float A = (right + left) * r_width;
float B = (top + bottom) * r_height;
float C = (far + near) * r_depth;
m[offset + 0] = x;
m[offset + 3] = -A;
m[offset + 5] = y;
m[offset + 7] = -B;
m[offset + 10] = -z;
m[offset + 11] = -C;
m[offset + 1] = 0.0f;
m[offset + 2] = 0.0f;
m[offset + 4] = 0.0f;
m[offset + 6] = 0.0f;
m[offset + 8] = 0.0f;
m[offset + 9] = 0.0f;
m[offset + 12] = 0.0f;
m[offset + 13] = 0.0f;
m[offset + 14] = 0.0f;
m[offset + 15] = 1.0f;
}