Opengl中矩阵和perspective/ortho的相互转换

时间:2021-08-27 14:16:06

Opengl中矩阵和perspective/ortho的相互转换

定义矩阵

Opengl变换需要用四维矩阵。我们来定义这样的矩阵。

+BIT祝威+悄悄在此留下版了个权的信息说:

四维向量

首先,我们定义一个四维向量vec4。

     /// <summary>
/// Represents a four dimensional vector.
/// </summary>
public struct vec4
{
public float x;
public float y;
public float z;
public float w; public float this[int index]
{
get
{
if (index == ) return x;
else if (index == ) return y;
else if (index == ) return z;
else if (index == ) return w;
else throw new Exception("Out of range.");
}
set
{
if (index == ) x = value;
else if (index == ) y = value;
else if (index == ) z = value;
else if (index == ) w = value;
else throw new Exception("Out of range.");
}
} public vec4(float s)
{
x = y = z = w = s;
} public vec4(float x, float y, float z, float w)
{
this.x = x;
this.y = y;
this.z = z;
this.w = w;
} public vec4(vec4 v)
{
this.x = v.x;
this.y = v.y;
this.z = v.z;
this.w = v.w;
} public vec4(vec3 xyz, float w)
{
this.x = xyz.x;
this.y = xyz.y;
this.z = xyz.z;
this.w = w;
} public static vec4 operator +(vec4 lhs, vec4 rhs)
{
return new vec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w);
} public static vec4 operator +(vec4 lhs, float rhs)
{
return new vec4(lhs.x + rhs, lhs.y + rhs, lhs.z + rhs, lhs.w + rhs);
} public static vec4 operator -(vec4 lhs, float rhs)
{
return new vec4(lhs.x - rhs, lhs.y - rhs, lhs.z - rhs, lhs.w - rhs);
} public static vec4 operator -(vec4 lhs, vec4 rhs)
{
return new vec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w);
} public static vec4 operator *(vec4 self, float s)
{
return new vec4(self.x * s, self.y * s, self.z * s, self.w * s);
} public static vec4 operator *(float lhs, vec4 rhs)
{
return new vec4(rhs.x * lhs, rhs.y * lhs, rhs.z * lhs, rhs.w * lhs);
} public static vec4 operator *(vec4 lhs, vec4 rhs)
{
return new vec4(rhs.x * lhs.x, rhs.y * lhs.y, rhs.z * lhs.z, rhs.w * lhs.w);
} public static vec4 operator /(vec4 lhs, float rhs)
{
return new vec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs);
} public float[] to_array()
{
return new[] { x, y, z, w };
} /// <summary>
/// 归一化向量
/// </summary>
/// <param name="vector"></param>
/// <returns></returns>
public void Normalize()
{
var frt = (float)Math.Sqrt(this.x * this.x + this.y * this.y + this.z * this.z); this.x = x / frt;
this.y = y / frt;
this.z = z / frt;
this.w = w / frt;
} public override string ToString()
{
return string.Format("{0:0.00},{1:0.00},{2:0.00},{3:0.00}", x, y, z, w);
}
}

vec4

四维矩阵

然后,我们定义一个四维矩阵mat4。它用4个vec4表示,每个vec4代表一个列向量。(这是glm中的定义)

     /// <summary>
/// Represents a 4x4 matrix.
/// </summary>
public struct mat4
{
public override string ToString()
{
if (cols == null)
{ return "<null>"; }
var builder = new System.Text.StringBuilder();
for (int i = ; i < cols.Length; i++)
{
builder.Append(cols[i]);
builder.Append(" + ");
}
return builder.ToString();
//return base.ToString();
}
#region Construction /// <summary>
/// Initializes a new instance of the <see cref="mat4"/> struct.
/// This matrix is the identity matrix scaled by <paramref name="scale"/>.
/// </summary>
/// <param name="scale">The scale.</param>
public mat4(float scale)
{
cols = new[]
{
new vec4(scale, 0.0f, 0.0f, 0.0f),
new vec4(0.0f, scale, 0.0f, 0.0f),
new vec4(0.0f, 0.0f, scale, 0.0f),
new vec4(0.0f, 0.0f, 0.0f, scale),
};
} /// <summary>
/// Initializes a new instance of the <see cref="mat4"/> struct.
/// The matrix is initialised with the <paramref name="cols"/>.
/// </summary>
/// <param name="cols">The colums of the matrix.</param>
public mat4(vec4[] cols)
{
this.cols = new[] { cols[], cols[], cols[], cols[] };
} public mat4(vec4 a, vec4 b, vec4 c, vec4 d)
{
this.cols = new[]
{
a, b, c, d
};
} /// <summary>
/// Creates an identity matrix.
/// </summary>
/// <returns>A new identity matrix.</returns>
public static mat4 identity()
{
return new mat4
{
cols = new[]
{
new vec4(,,,),
new vec4(,,,),
new vec4(,,,),
new vec4(,,,)
}
};
} #endregion #region Index Access /// <summary>
/// Gets or sets the <see cref="vec4"/> column at the specified index.
/// </summary>
/// <value>
/// The <see cref="vec4"/> column.
/// </value>
/// <param name="column">The column index.</param>
/// <returns>The column at index <paramref name="column"/>.</returns>
public vec4 this[int column]
{
get { return cols[column]; }
set { cols[column] = value; }
} /// <summary>
/// Gets or sets the element at <paramref name="column"/> and <paramref name="row"/>.
/// </summary>
/// <value>
/// The element at <paramref name="column"/> and <paramref name="row"/>.
/// </value>
/// <param name="column">The column index.</param>
/// <param name="row">The row index.</param>
/// <returns>
/// The element at <paramref name="column"/> and <paramref name="row"/>.
/// </returns>
public float this[int column, int row]
{
get { return cols[column][row]; }
set { cols[column][row] = value; }
} #endregion #region Conversion /// <summary>
/// Returns the matrix as a flat array of elements, column major.
/// </summary>
/// <returns></returns>
public float[] to_array()
{
return cols.SelectMany(v => v.to_array()).ToArray();
} /// <summary>
/// Returns the <see cref="mat3"/> portion of this matrix.
/// </summary>
/// <returns>The <see cref="mat3"/> portion of this matrix.</returns>
public mat3 to_mat3()
{
return new mat3(new[] {
new vec3(cols[][], cols[][], cols[][]),
new vec3(cols[][], cols[][], cols[][]),
new vec3(cols[][], cols[][], cols[][])});
} #endregion #region Multiplication /// <summary>
/// Multiplies the <paramref name="lhs"/> matrix by the <paramref name="rhs"/> vector.
/// </summary>
/// <param name="lhs">The LHS matrix.</param>
/// <param name="rhs">The RHS vector.</param>
/// <returns>The product of <paramref name="lhs"/> and <paramref name="rhs"/>.</returns>
public static vec4 operator *(mat4 lhs, vec4 rhs)
{
return new vec4(
lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[],
lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[],
lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[],
lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[] + lhs[, ] * rhs[]
);
} /// <summary>
/// Multiplies the <paramref name="lhs"/> matrix by the <paramref name="rhs"/> matrix.
/// </summary>
/// <param name="lhs">The LHS matrix.</param>
/// <param name="rhs">The RHS matrix.</param>
/// <returns>The product of <paramref name="lhs"/> and <paramref name="rhs"/>.</returns>
public static mat4 operator *(mat4 lhs, mat4 rhs)
{
mat4 result = new mat4(
new vec4(
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][]
),
new vec4(
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][]
),
new vec4(
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][]
),
new vec4(
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][],
lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][] + lhs[][] * rhs[][]
)
); return result;
} public static mat4 operator *(mat4 lhs, float s)
{
return new mat4(new[]
{
lhs[]*s,
lhs[]*s,
lhs[]*s,
lhs[]*s
});
} #endregion /// <summary>
/// The columms of the matrix.
/// </summary>
private vec4[] cols;
}

mat4

+BIT祝威+悄悄在此留下版了个权的信息说:

矩阵与ortho的转换

从ortho到矩阵

根据传入的参数可以获得一个代表平行投影的矩阵。

         /// <summary>
/// Creates a matrix for an orthographic parallel viewing volume.
/// </summary>
/// <param name="left">The left.</param>
/// <param name="right">The right.</param>
/// <param name="bottom">The bottom.</param>
/// <param name="top">The top.</param>
/// <param name="zNear">The z near.</param>
/// <param name="zFar">The z far.</param>
/// <returns></returns>
public static mat4 ortho(float left, float right, float bottom, float top, float zNear, float zFar)
{
var result = mat4.identity();
result[, ] = (2f) / (right - left);
result[, ] = (2f) / (top - bottom);
result[, ] = -(2f) / (zFar - zNear);
result[, ] = -(right + left) / (right - left);
result[, ] = -(top + bottom) / (top - bottom);
result[, ] = -(zFar + zNear) / (zFar - zNear);
return result;
}

从矩阵到ortho

反过来,当我们手上有一个矩阵时,我们可以分析出这个矩阵是由ortho用怎样的参数计算得到的。(当然,并非所有矩阵都能用ortho计算出来)

         /// <summary>
/// 如果此矩阵是glm.ortho()的结果,那么返回glm.ortho()的各个参数值。
/// </summary>
/// <param name="matrix"></param>
/// <param name="left"></param>
/// <param name="right"></param>
/// <param name="bottom"></param>
/// <param name="top"></param>
/// <param name="zNear"></param>
/// <param name="zFar"></param>
/// <returns></returns>
public static bool TryParse(this mat4 matrix,
out float left, out float right, out float bottom, out float top, out float zNear, out float zFar)
{
{
float negHalfLeftRight = matrix[, ] / matrix[, ];
float halfRightMinusLeft = 1.0f / matrix[][];
left = -(halfRightMinusLeft + negHalfLeftRight);
right = halfRightMinusLeft - negHalfLeftRight;
} {
float negHalfBottomTop = matrix[, ] / matrix[, ];
float halfTopMinusBottom = 1.0f / matrix[, ];
bottom = -(halfTopMinusBottom + negHalfBottomTop);
top = halfTopMinusBottom - negHalfBottomTop;
} {
float halfNearFar = matrix[, ] / matrix[, ];
float negHalfFarMinusNear = 1.0f / matrix[, ];
zNear = negHalfFarMinusNear + halfNearFar;
zFar = halfNearFar - negHalfFarMinusNear;
} if (matrix[, ] == 0.0f || matrix[, ] == 0.0f || matrix[, ] == 0.0f)
{
return false;
} if (matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f || matrix[, ] != 0.0f)
{
return false;
} if (matrix[, ] != 1.0f)
{
return false;
} return true;
}

矩阵与perpspective的转换

从perspective到矩阵

根据传入的参数可以获得一个代表透视投影的矩阵。

         /// <summary>
/// Creates a perspective transformation matrix.
/// </summary>
/// <param name="fovy">The field of view angle, in radians.</param>
/// <param name="aspect">The aspect ratio.</param>
/// <param name="zNear">The near depth clipping plane.</param>
/// <param name="zFar">The far depth clipping plane.</param>
/// <returns>A <see cref="mat4"/> that contains the projection matrix for the perspective transformation.</returns>
public static mat4 perspective(float fovy, float aspect, float zNear, float zFar)
{
var result = mat4.identity();
float tangent = (float)Math.Tan(fovy / 2.0f);
float height = zNear * tangent;
float width = height * aspect;
float l = -width, r = width, b = -height, t = height, n = zNear, f = zFar;
result[, ] = 2.0f * n / (r - l);// = 2.0f * zNear / (2.0f * zNear * tangent * aspect)
result[, ] = 2.0f * n / (t - b);// = 2.0f * zNear / (2.0f * zNear * tangent)
//result[2, 0] = (r + l) / (r - l);// = 0.0f
//result[2, 1] = (t + b) / (t - b);// = 0.0f
result[, ] = -(f + n) / (f - n);
result[, ] = -1.0f;
result[, ] = -(2.0f * f * n) / (f - n);
result[, ] = 0.0f; return result;
}

从矩阵到perspective

反过来,当我们手上有一个矩阵时,我们可以分析出这个矩阵是由perpspective用怎样的参数计算得到的。(当然,并非所有矩阵都能用perpspective计算出来)

         /// <summary>
/// 如果此矩阵是glm.perspective()的结果,那么返回glm.perspective()的各个参数值。
/// </summary>
/// <param name="matrix"></param>
/// <param name="fovy"></param>
/// <param name="aspectRatio"></param>
/// <param name="zNear"></param>
/// <param name="zFar"></param>
/// <returns></returns>
public static bool TryParse(this mat4 matrix,
out float fovy, out float aspectRatio, out float zNear, out float zFar)
{
float tanHalfFovy = 1.0f / matrix[, ];
fovy = * (float)(Math.Atan(tanHalfFovy));
if (fovy < ) { fovy = -fovy; }
//aspectRatio = 1.0f / matrix[0, 0] / tanHalfFovy;
aspectRatio = matrix[, ] / matrix[, ];
if (matrix[, ] == 1.0f)
{
zFar = 0.0f;
zNear = 0.0f;
}
else if (matrix[, ] == -1.0f)
{
zNear = 0.0f;
zFar = float.PositiveInfinity;
}
else
{
zNear = matrix[, ] / (matrix[, ] - );
zFar = matrix[, ] / (matrix[, ] + );
} if (matrix[, ] == 0.0f || matrix[, ] == 0.0f || matrix[, ] == 0.0f)
{
return false;
} if (matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f
|| matrix[, ] != 0.0f || matrix[, ] != 0.0f || matrix[, ] != 0.0f)
{
return false;
} if (matrix[, ] != -1.0f)
{
return false;
} return true;
}
+BIT祝威+悄悄在此留下版了个权的信息说:

总结

本篇就写这些,今后再写一些相关的内容。