Opengl中矩阵和perspective/ortho的相互转换
Opengl变换需要用四维矩阵。我们来定义这样的矩阵。
首先,我们定义一个四维向量vec4。
1 /// <summary> 2 /// Represents a four dimensional vector. 3 /// </summary> 4 public struct vec4 5 { 6 public float x; 7 public float y; 8 public float z; 9 public float w; 10 11 public float this[int index] 12 { 13 get 14 { 15 if (index == 0) return x; 16 else if (index == 1) return y; 17 else if (index == 2) return z; 18 else if (index == 3) return w; 19 else throw new Exception("Out of range."); 20 } 21 set 22 { 23 if (index == 0) x = value; 24 else if (index == 1) y = value; 25 else if (index == 2) z = value; 26 else if (index == 3) w = value; 27 else throw new Exception("Out of range."); 28 } 29 } 30 31 public vec4(float s) 32 { 33 x = y = z = w = s; 34 } 35 36 public vec4(float x, float y, float z, float w) 37 { 38 this.x = x; 39 this.y = y; 40 this.z = z; 41 this.w = w; 42 } 43 44 public vec4(vec4 v) 45 { 46 this.x = v.x; 47 this.y = v.y; 48 this.z = v.z; 49 this.w = v.w; 50 } 51 52 public vec4(vec3 xyz, float w) 53 { 54 this.x = xyz.x; 55 this.y = xyz.y; 56 this.z = xyz.z; 57 this.w = w; 58 } 59 60 public static vec4 operator +(vec4 lhs, vec4 rhs) 61 { 62 return new vec4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); 63 } 64 65 public static vec4 operator +(vec4 lhs, float rhs) 66 { 67 return new vec4(lhs.x + rhs, lhs.y + rhs, lhs.z + rhs, lhs.w + rhs); 68 } 69 70 public static vec4 operator -(vec4 lhs, float rhs) 71 { 72 return new vec4(lhs.x - rhs, lhs.y - rhs, lhs.z - rhs, lhs.w - rhs); 73 } 74 75 public static vec4 operator -(vec4 lhs, vec4 rhs) 76 { 77 return new vec4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); 78 } 79 80 public static vec4 operator *(vec4 self, float s) 81 { 82 return new vec4(self.x * s, self.y * s, self.z * s, self.w * s); 83 } 84 85 public static vec4 operator *(float lhs, vec4 rhs) 86 { 87 return new vec4(rhs.x * lhs, rhs.y * lhs, rhs.z * lhs, rhs.w * lhs); 88 } 89 90 public static vec4 operator *(vec4 lhs, vec4 rhs) 91 { 92 return new vec4(rhs.x * lhs.x, rhs.y * lhs.y, rhs.z * lhs.z, rhs.w * lhs.w); 93 } 94 95 public static vec4 operator /(vec4 lhs, float rhs) 96 { 97 return new vec4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); 98 } 99 100 public float[] to_array() 101 { 102 return new[] { x, y, z, w }; 103 } 104 105 /// <summary> 106 /// 归一化向量 107 /// </summary> 108 /// <param name="vector"></param> 109 /// <returns></returns> 110 public void Normalize() 111 { 112 var frt = (float)Math.Sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 113 114 this.x = x / frt; 115 this.y = y / frt; 116 this.z = z / frt; 117 this.w = w / frt; 118 } 119 120 public override string ToString() 121 { 122 return string.Format("{0:0.00},{1:0.00},{2:0.00},{3:0.00}", x, y, z, w); 123 } 124 }
然后,我们定义一个四维矩阵mat4。它用4个vec4表示,每个vec4代表一个列向量。(这是glm中的定义)
1 /// <summary> 2 /// Represents a 4x4 matrix. 3 /// </summary> 4 public struct mat4 5 { 6 public override string ToString() 7 { 8 if (cols == null) 9 { return "<null>"; } 10 var builder = new System.Text.StringBuilder(); 11 for (int i = 0; i < cols.Length; i++) 12 { 13 builder.Append(cols[i]); 14 builder.Append(" + "); 15 } 16 return builder.ToString(); 17 //return base.ToString(); 18 } 19 #region Construction 20 21 /// <summary> 22 /// Initializes a new instance of the <see cref="mat4"/> struct. 23 /// This matrix is the identity matrix scaled by <paramref name="scale"/>. 24 /// </summary> 25 /// <param name="scale">The scale.</param> 26 public mat4(float scale) 27 { 28 cols = new[] 29 { 30 new vec4(scale, 0.0f, 0.0f, 0.0f), 31 new vec4(0.0f, scale, 0.0f, 0.0f), 32 new vec4(0.0f, 0.0f, scale, 0.0f), 33 new vec4(0.0f, 0.0f, 0.0f, scale), 34 }; 35 } 36 37 /// <summary> 38 /// Initializes a new instance of the <see cref="mat4"/> struct. 39 /// The matrix is initialised with the <paramref name="cols"/>. 40 /// </summary> 41 /// <param name="cols">The colums of the matrix.</param> 42 public mat4(vec4[] cols) 43 { 44 this.cols = new[] { cols[0], cols[1], cols[2], cols[3] }; 45 } 46 47 public mat4(vec4 a, vec4 b, vec4 c, vec4 d) 48 { 49 this.cols = new[] 50 { 51 a, b, c, d 52 }; 53 } 54 55 /// <summary> 56 /// Creates an identity matrix. 57 /// </summary> 58 /// <returns>A new identity matrix.</returns> 59 public static mat4 identity() 60 { 61 return new mat4 62 { 63 cols = new[] 64 { 65 new vec4(1,0,0,0), 66 new vec4(0,1,0,0), 67 new vec4(0,0,1,0), 68 new vec4(0,0,0,1) 69 } 70 }; 71 } 72 73 #endregion 74 75 #region Index Access 76 77 /// <summary> 78 /// Gets or sets the <see cref="vec4"/> column at the specified index. 79 /// </summary> 80 /// <value> 81 /// The <see cref="vec4"/> column. 82 /// </value> 83 /// <param name="column">The column index.</param> 84 /// <returns>The column at index <paramref name="column"/>.</returns> 85 public vec4 this[int column] 86 { 87 get { return cols[column]; } 88 set { cols[column] = value; } 89 } 90 91 /// <summary> 92 /// Gets or sets the element at <paramref name="column"/> and <paramref name="row"/>. 93 /// </summary> 94 /// <value> 95 /// The element at <paramref name="column"/> and <paramref name="row"/>. 96 /// </value> 97 /// <param name="column">The column index.</param> 98 /// <param name="row">The row index.</param> 99 /// <returns> 100 /// The element at <paramref name="column"/> and <paramref name="row"/>. 101 /// </returns> 102 public float this[int column, int row] 103 { 104 get { return cols[column][row]; } 105 set { cols[column][row] = value; } 106 } 107 108 #endregion 109 110 #region Conversion 111 112 /// <summary> 113 /// Returns the matrix as a flat array of elements, column major. 114 /// </summary> 115 /// <returns></returns> 116 public float[] to_array() 117 { 118 return cols.SelectMany(v => v.to_array()).ToArray(); 119 } 120 121 /// <summary> 122 /// Returns the <see cref="mat3"/> portion of this matrix. 123 /// </summary> 124 /// <returns>The <see cref="mat3"/> portion of this matrix.</returns> 125 public mat3 to_mat3() 126 { 127 return new mat3(new[] { 128 new vec3(cols[0][0], cols[0][1], cols[0][2]), 129 new vec3(cols[1][0], cols[1][1], cols[1][2]), 130 new vec3(cols[2][0], cols[2][1], cols[2][2])}); 131 } 132 133 #endregion 134 135 #region Multiplication 136 137 /// <summary> 138 /// Multiplies the <paramref name="lhs"/> matrix by the <paramref name="rhs"/> vector. 139 /// </summary> 140 /// <param name="lhs">The LHS matrix.</param> 141 /// <param name="rhs">The RHS vector.</param> 142 /// <returns>The product of <paramref name="lhs"/> and <paramref name="rhs"/>.</returns> 143 public static vec4 operator *(mat4 lhs, vec4 rhs) 144 { 145 return new vec4( 146 lhs[0, 0] * rhs[0] + lhs[1, 0] * rhs[1] + lhs[2, 0] * rhs[2] + lhs[3, 0] * rhs[3], 147 lhs[0, 1] * rhs[0] + lhs[1, 1] * rhs[1] + lhs[2, 1] * rhs[2] + lhs[3, 1] * rhs[3], 148 lhs[0, 2] * rhs[0] + lhs[1, 2] * rhs[1] + lhs[2, 2] * rhs[2] + lhs[3, 2] * rhs[3], 149 lhs[0, 3] * rhs[0] + lhs[1, 3] * rhs[1] + lhs[2, 3] * rhs[2] + lhs[3, 3] * rhs[3] 150 ); 151 } 152 153 /// <summary> 154 /// Multiplies the <paramref name="lhs"/> matrix by the <paramref name="rhs"/> matrix. 155 /// </summary> 156 /// <param name="lhs">The LHS matrix.</param> 157 /// <param name="rhs">The RHS matrix.</param> 158 /// <returns>The product of <paramref name="lhs"/> and <paramref name="rhs"/>.</returns> 159 public static mat4 operator *(mat4 lhs, mat4 rhs) 160 { 161 mat4 result = new mat4( 162 new vec4( 163 lhs[0][0] * rhs[0][0] + lhs[1][0] * rhs[0][1] + lhs[2][0] * rhs[0][2] + lhs[3][0] * rhs[0][3], 164 lhs[0][1] * rhs[0][0] + lhs[1][1] * rhs[0][1] + lhs[2][1] * rhs[0][2] + lhs[3][1] * rhs[0][3], 165 lhs[0][2] * rhs[0][0] + lhs[1][2] * rhs[0][1] + lhs[2][2] * rhs[0][2] + lhs[3][2] * rhs[0][3], 166 lhs[0][3] * rhs[0][0] + lhs[1][3] * rhs[0][1] + lhs[2][3] * rhs[0][2] + lhs[3][3] * rhs[0][3] 167 ), 168 new vec4( 169 lhs[0][0] * rhs[1][0] + lhs[1][0] * rhs[1][1] + lhs[2][0] * rhs[1][2] + lhs[3][0] * rhs[1][3], 170 lhs[0][1] * rhs[1][0] + lhs[1][1] * rhs[1][1] + lhs[2][1] * rhs[1][2] + lhs[3][1] * rhs[1][3], 171 lhs[0][2] * rhs[1][0] + lhs[1][2] * rhs[1][1] + lhs[2][2] * rhs[1][2] + lhs[3][2] * rhs[1][3], 172 lhs[0][3] * rhs[1][0] + lhs[1][3] * rhs[1][1] + lhs[2][3] * rhs[1][2] + lhs[3][3] * rhs[1][3] 173 ), 174 new vec4( 175 lhs[0][0] * rhs[2][0] + lhs[1][0] * rhs[2][1] + lhs[2][0] * rhs[2][2] + lhs[3][0] * rhs[2][3], 176 lhs[0][1] * rhs[2][0] + lhs[1][1] * rhs[2][1] + lhs[2][1] * rhs[2][2] + lhs[3][1] * rhs[2][3], 177 lhs[0][2] * rhs[2][0] + lhs[1][2] * rhs[2][1] + lhs[2][2] * rhs[2][2] + lhs[3][2] * rhs[2][3], 178 lhs[0][3] * rhs[2][0] + lhs[1][3] * rhs[2][1] + lhs[2][3] * rhs[2][2] + lhs[3][3] * rhs[2][3] 179 ), 180 new vec4( 181 lhs[0][0] * rhs[3][0] + lhs[1][0] * rhs[3][1] + lhs[2][0] * rhs[3][2] + lhs[3][0] * rhs[3][3], 182 lhs[0][1] * rhs[3][0] + lhs[1][1] * rhs[3][1] + lhs[2][1] * rhs[3][2] + lhs[3][1] * rhs[3][3], 183 lhs[0][2] * rhs[3][0] + lhs[1][2] * rhs[3][1] + lhs[2][2] * rhs[3][2] + lhs[3][2] * rhs[3][3], 184 lhs[0][3] * rhs[3][0] + lhs[1][3] * rhs[3][1] + lhs[2][3] * rhs[3][2] + lhs[3][3] * rhs[3][3] 185 ) 186 ); 187 188 return result; 189 } 190 191 public static mat4 operator *(mat4 lhs, float s) 192 { 193 return new mat4(new[] 194 { 195 lhs[0]*s, 196 lhs[1]*s, 197 lhs[2]*s, 198 lhs[3]*s 199 }); 200 } 201 202 #endregion 203 204 /// <summary> 205 /// The columms of the matrix. 206 /// </summary> 207 private vec4[] cols; 208 }
根据传入的参数可以获得一个代表平行投影的矩阵。
1 /// <summary> 2 /// Creates a matrix for an orthographic parallel viewing volume. 3 /// </summary> 4 /// <param name="left">The left.</param> 5 /// <param name="right">The right.</param> 6 /// <param name="bottom">The bottom.</param> 7 /// <param name="top">The top.</param> 8 /// <param name="zNear">The z near.</param> 9 /// <param name="zFar">The z far.</param> 10 /// <returns></returns> 11 public static mat4 ortho(float left, float right, float bottom, float top, float zNear, float zFar) 12 { 13 var result = mat4.identity(); 14 result[0, 0] = (2f) / (right - left); 15 result[1, 1] = (2f) / (top - bottom); 16 result[2, 2] = -(2f) / (zFar - zNear); 17 result[3, 0] = -(right + left) / (right - left); 18 result[3, 1] = -(top + bottom) / (top - bottom); 19 result[3, 2] = -(zFar + zNear) / (zFar - zNear); 20 return result; 21 }
反过来,当我们手上有一个矩阵时,我们可以分析出这个矩阵是由ortho用怎样的参数计算得到的。(当然,并非所有矩阵都能用ortho计算出来)
1 /// <summary> 2 /// 如果此矩阵是glm.ortho()的结果,那么返回glm.ortho()的各个参数值。 3 /// </summary> 4 /// <param name="matrix"></param> 5 /// <param name="left"></param> 6 /// <param name="right"></param> 7 /// <param name="bottom"></param> 8 /// <param name="top"></param> 9 /// <param name="zNear"></param> 10 /// <param name="zFar"></param> 11 /// <returns></returns> 12 public static bool TryParse(this mat4 matrix, 13 out float left, out float right, out float bottom, out float top, out float zNear, out float zFar) 14 { 15 { 16 float negHalfLeftRight = matrix[3, 0] / matrix[0, 0]; 17 float halfRightMinusLeft = 1.0f / matrix[0][0]; 18 left = -(halfRightMinusLeft + negHalfLeftRight); 19 right = halfRightMinusLeft - negHalfLeftRight; 20 } 21 22 { 23 float negHalfBottomTop = matrix[3, 1] / matrix[1, 1]; 24 float halfTopMinusBottom = 1.0f / matrix[1, 1]; 25 bottom = -(halfTopMinusBottom + negHalfBottomTop); 26 top = halfTopMinusBottom - negHalfBottomTop; 27 } 28 29 { 30 float halfNearFar = matrix[3, 2] / matrix[2, 2]; 31 float negHalfFarMinusNear = 1.0f / matrix[2, 2]; 32 zNear = negHalfFarMinusNear + halfNearFar; 33 zFar = halfNearFar - negHalfFarMinusNear; 34 } 35 36 if (matrix[0, 0] == 0.0f || matrix[1, 1] == 0.0f || matrix[2, 2] == 0.0f) 37 { 38 return false; 39 } 40 41 if (matrix[1, 0] != 0.0f || matrix[2, 0] != 0.0f 42 || matrix[0, 1] != 0.0f || matrix[2, 1] != 0.0f 43 || matrix[0, 2] != 0.0f || matrix[1, 2] != 0.0f 44 || matrix[0, 3] != 0.0f || matrix[1, 3] != 0.0f || matrix[2, 3] != 0.0f) 45 { 46 return false; 47 } 48 49 if (matrix[3, 3] != 1.0f) 50 { 51 return false; 52 } 53 54 return true; 55 }
根据传入的参数可以获得一个代表透视投影的矩阵。
1 /// <summary> 2 /// Creates a perspective transformation matrix. 3 /// </summary> 4 /// <param name="fovy">The field of view angle, in radians.</param> 5 /// <param name="aspect">The aspect ratio.</param> 6 /// <param name="zNear">The near depth clipping plane.</param> 7 /// <param name="zFar">The far depth clipping plane.</param> 8 /// <returns>A <see cref="mat4"/> that contains the projection matrix for the perspective transformation.</returns> 9 public static mat4 perspective(float fovy, float aspect, float zNear, float zFar) 10 { 11 var result = mat4.identity(); 12 float tangent = (float)Math.Tan(fovy / 2.0f); 13 float height = zNear * tangent; 14 float width = height * aspect; 15 float l = -width, r = width, b = -height, t = height, n = zNear, f = zFar; 16 result[0, 0] = 2.0f * n / (r - l);// = 2.0f * zNear / (2.0f * zNear * tangent * aspect) 17 result[1, 1] = 2.0f * n / (t - b);// = 2.0f * zNear / (2.0f * zNear * tangent) 18 //result[2, 0] = (r + l) / (r - l);// = 0.0f 19 //result[2, 1] = (t + b) / (t - b);// = 0.0f 20 result[2, 2] = -(f + n) / (f - n); 21 result[2, 3] = -1.0f; 22 result[3, 2] = -(2.0f * f * n) / (f - n); 23 result[3, 3] = 0.0f; 24 25 return result; 26 }
反过来,当我们手上有一个矩阵时,我们可以分析出这个矩阵是由perpspective用怎样的参数计算得到的。(当然,并非所有矩阵都能用perpspective计算出来)
1 /// <summary> 2 /// 如果此矩阵是glm.perspective()的结果,那么返回glm.perspective()的各个参数值。 3 /// </summary> 4 /// <param name="matrix"></param> 5 /// <param name="fovy"></param> 6 /// <param name="aspectRatio"></param> 7 /// <param name="zNear"></param> 8 /// <param name="zFar"></param> 9 /// <returns></returns> 10 public static bool TryParse(this mat4 matrix, 11 out float fovy, out float aspectRatio, out float zNear, out float zFar) 12 { 13 float tanHalfFovy = 1.0f / matrix[1, 1]; 14 fovy = 2 * (float)(Math.Atan(tanHalfFovy)); 15 if (fovy < 0) { fovy = -fovy; } 16 //aspectRatio = 1.0f / matrix[0, 0] / tanHalfFovy; 17 aspectRatio = matrix[1, 1] / matrix[0, 0]; 18 if (matrix[2, 2] == 1.0f) 19 { 20 zFar = 0.0f; 21 zNear = 0.0f; 22 } 23 else if (matrix[2, 2] == -1.0f) 24 { 25 zNear = 0.0f; 26 zFar = float.PositiveInfinity; 27 } 28 else 29 { 30 zNear = matrix[3, 2] / (matrix[2, 2] - 1); 31 zFar = matrix[3, 2] / (matrix[2, 2] + 1); 32 } 33 34 if (matrix[0, 0] == 0.0f || matrix[1, 1] == 0.0f || matrix[2, 2] == 0.0f) 35 { 36 return false; 37 } 38 39 if (matrix[1, 0] != 0.0f || matrix[3, 0] != 0.0f 40 || matrix[0, 1] != 0.0f || matrix[3, 1] != 0.0f 41 || matrix[0, 2] != 0.0f || matrix[1, 2] != 0.0f 42 || matrix[0, 3] != 0.0f || matrix[1, 3] != 0.0f || matrix[3, 3] != 0.0f) 43 { 44 return false; 45 } 46 47 if (matrix[2, 3] != -1.0f) 48 { 49 return false; 50 } 51 52 return true; 53 }
本篇就写这些,今后再写一些相关的内容。