math - perspectiva - matriz de proyeccion
Cómo construir una matriz de proyección en perspectiva(sin API) (2)
A continuación se presenta una implementación típica de la matriz de proyección en perspectiva. Y aquí hay un buen enlace para explicar todo 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;
}
Desarrollé un motor 3D simple (sin ningún uso de API), transformé exitosamente mi escena en mundo y espacio de vista, pero tengo problemas para proyectar mi escena (desde el espacio de vista) usando la matriz de proyección de perspectiva (estilo OpenGL). No estoy seguro de los valores fov, cercanos y lejanos, y la escena que obtengo está distorsionada. Espero que alguien pueda indicarme cómo construir y usar la matriz de proyección de perspectiva correctamente con códigos de ejemplo. Gracias de antemano por cualquier ayuda.
La matriz de construcción:
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 }
};
El uso de la matriz:
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...
}
Otra función que puede ser útil.
Este se basa en los parámetros izquierdo / derecho / superior / inferior / cercano / lejano (utilizados en 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;
}