您的位置:首页 > 运维架构

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

2015-08-27 00:38 274 查看
[b]Opengl中矩阵和perspective/ortho的相互转换 [/b]

定义矩阵

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

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

四维向量

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

/// <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 = 0; 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[0], cols[1], cols[2], cols[3] };
}

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(1,0,0,0),
new vec4(0,1,0,0),
new vec4(0,0,1,0),
new vec4(0,0,0,1)
}
};
}

#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[0][0], cols[0][1], cols[0][2]),
new vec3(cols[1][0], cols[1][1], cols[1][2]),
new vec3(cols[2][0], cols[2][1], cols[2][2])});
}

#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[0, 0] * rhs[0] + lhs[1, 0] * rhs[1] + lhs[2, 0] * rhs[2] + lhs[3, 0] * rhs[3],
lhs[0, 1] * rhs[0] + lhs[1, 1] * rhs[1] + lhs[2, 1] * rhs[2] + lhs[3, 1] * rhs[3],
lhs[0, 2] * rhs[0] + lhs[1, 2] * rhs[1] + lhs[2, 2] * rhs[2] + lhs[3, 2] * rhs[3],
lhs[0, 3] * rhs[0] + lhs[1, 3] * rhs[1] + lhs[2, 3] * rhs[2] + lhs[3, 3] * rhs[3]
);
}

/// <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[0][0] * rhs[0][0] + lhs[1][0] * rhs[0][1] + lhs[2][0] * rhs[0][2] + lhs[3][0] * rhs[0][3],
lhs[0][1] * rhs[0][0] + lhs[1][1] * rhs[0][1] + lhs[2][1] * rhs[0][2] + lhs[3][1] * rhs[0][3],
lhs[0][2] * rhs[0][0] + lhs[1][2] * rhs[0][1] + lhs[2][2] * rhs[0][2] + lhs[3][2] * rhs[0][3],
lhs[0][3] * rhs[0][0] + lhs[1][3] * rhs[0][1] + lhs[2][3] * rhs[0][2] + lhs[3][3] * rhs[0][3]
),
new vec4(
lhs[0][0] * rhs[1][0] + lhs[1][0] * rhs[1][1] + lhs[2][0] * rhs[1][2] + lhs[3][0] * rhs[1][3],
lhs[0][1] * rhs[1][0] + lhs[1][1] * rhs[1][1] + lhs[2][1] * rhs[1][2] + lhs[3][1] * rhs[1][3],
lhs[0][2] * rhs[1][0] + lhs[1][2] * rhs[1][1] + lhs[2][2] * rhs[1][2] + lhs[3][2] * rhs[1][3],
lhs[0][3] * rhs[1][0] + lhs[1][3] * rhs[1][1] + lhs[2][3] * rhs[1][2] + lhs[3][3] * rhs[1][3]
),
new vec4(
lhs[0][0] * rhs[2][0] + lhs[1][0] * rhs[2][1] + lhs[2][0] * rhs[2][2] + lhs[3][0] * rhs[2][3],
lhs[0][1] * rhs[2][0] + lhs[1][1] * rhs[2][1] + lhs[2][1] * rhs[2][2] + lhs[3][1] * rhs[2][3],
lhs[0][2] * rhs[2][0] + lhs[1][2] * rhs[2][1] + lhs[2][2] * rhs[2][2] + lhs[3][2] * rhs[2][3],
lhs[0][3] * rhs[2][0] + lhs[1][3] * rhs[2][1] + lhs[2][3] * rhs[2][2] + lhs[3][3] * rhs[2][3]
),
new vec4(
lhs[0][0] * rhs[3][0] + lhs[1][0] * rhs[3][1] + lhs[2][0] * rhs[3][2] + lhs[3][0] * rhs[3][3],
lhs[0][1] * rhs[3][0] + lhs[1][1] * rhs[3][1] + lhs[2][1] * rhs[3][2] + lhs[3][1] * rhs[3][3],
lhs[0][2] * rhs[3][0] + lhs[1][2] * rhs[3][1] + lhs[2][2] * rhs[3][2] + lhs[3][2] * rhs[3][3],
lhs[0][3] * rhs[3][0] + lhs[1][3] * rhs[3][1] + lhs[2][3] * rhs[3][2] + lhs[3][3] * rhs[3][3]
)
);

return result;
}

public static mat4 operator *(mat4 lhs, float s)
{
return new mat4(new[]
{
lhs[0]*s,
lhs[1]*s,
lhs[2]*s,
lhs[3]*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[0, 0] = (2f) / (right - left);
result[1, 1] = (2f) / (top - bottom);
result[2, 2] = -(2f) / (zFar - zNear);
result[3, 0] = -(right + left) / (right - left);
result[3, 1] = -(top + bottom) / (top - bottom);
result[3, 2] = -(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[3, 0] / matrix[0, 0];
float halfRightMinusLeft = 1.0f / matrix[0][0];
left = -(halfRightMinusLeft + negHalfLeftRight);
right = halfRightMinusLeft - negHalfLeftRight;
}

{
float negHalfBottomTop = matrix[3, 1] / matrix[1, 1];
float halfTopMinusBottom = 1.0f / matrix[1, 1];
bottom = -(halfTopMinusBottom + negHalfBottomTop);
top = halfTopMinusBottom - negHalfBottomTop;
}

{
float halfNearFar = matrix[3, 2] / matrix[2, 2];
float negHalfFarMinusNear = 1.0f / matrix[2, 2];
zNear = negHalfFarMinusNear + halfNearFar;
zFar = halfNearFar - negHalfFarMinusNear;
}

if (matrix[0, 0] == 0.0f || matrix[1, 1] == 0.0f || matrix[2, 2] == 0.0f)
{
return false;
}

if (matrix[1, 0] != 0.0f || matrix[2, 0] != 0.0f
|| matrix[0, 1] != 0.0f || matrix[2, 1] != 0.0f
|| matrix[0, 2] != 0.0f || matrix[1, 2] != 0.0f
|| matrix[0, 3] != 0.0f || matrix[1, 3] != 0.0f || matrix[2, 3] != 0.0f)
{
return false;
}

if (matrix[3, 3] != 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[0, 0] = 2.0f * n / (r - l);// = 2.0f * zNear / (2.0f * zNear * tangent * aspect)
result[1, 1] = 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[2, 2] = -(f + n) / (f - n);
result[2, 3] = -1.0f;
result[3, 2] = -(2.0f * f * n) / (f - n);
result[3, 3] = 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[1, 1];
fovy = 2 * (float)(Math.Atan(tanHalfFovy));
if (fovy < 0) { fovy = -fovy; }
//aspectRatio = 1.0f / matrix[0, 0] / tanHalfFovy;
aspectRatio = matrix[1, 1] / matrix[0, 0];
if (matrix[2, 2] == 1.0f)
{
zFar = 0.0f;
zNear = 0.0f;
}
else if (matrix[2, 2] == -1.0f)
{
zNear = 0.0f;
zFar = float.PositiveInfinity;
}
else
{
zNear = matrix[3, 2] / (matrix[2, 2] - 1);
zFar = matrix[3, 2] / (matrix[2, 2] + 1);
}

if (matrix[0, 0] == 0.0f || matrix[1, 1] == 0.0f || matrix[2, 2] == 0.0f)
{
return false;
}

if (matrix[1, 0] != 0.0f || matrix[3, 0] != 0.0f
|| matrix[0, 1] != 0.0f || matrix[3, 1] != 0.0f
|| matrix[0, 2] != 0.0f || matrix[1, 2] != 0.0f
|| matrix[0, 3] != 0.0f || matrix[1, 3] != 0.0f || matrix[3, 3] != 0.0f)
{
return false;
}

if (matrix[2, 3] != -1.0f)
{
return false;
}

return true;
}


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

总结

本篇就写这些,今后再写一些相关的内容。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: