大约 2 天前,我决定编写代码来显式计算模型-视图-投影(“MVP”)矩阵,以了解它是如何工作的。从那时起,我就遇到了麻烦,似乎是因为我使用的投影矩阵。
使用 iPhone 显示器,我创建了一个由以下 4 个角顶点描述的屏幕中心正方形:
const CGFloat cy = screenHeight/2.0f;
const CGFloat z = -1.0f;
const CGFloat dim = 50.0f;
vxData[0] = cx-dim;
vxData[1] = cy-dim;
vxData[2] = z;
vxData[3] = cx-dim;
vxData[4] = cy+dim;
vxData[5] = z;
vxData[6] = cx+dim;
vxData[7] = cy+dim;
vxData[8] = z;
vxData[9] = cx+dim;
vxData[10] = cy-dim;
vxData[11] = z;
因为我使用的是 OGLES 2.0,所以我将 MVP 作为统一传递给我的顶点着色器,然后简单地将转换应用于当前顶点位置:
uniform mat4 mvp;
attribute vec3 vpos;
void main()
{
gl_Position = mvp * vec4(vpos, 1.0);
}
现在我已经将我的 MVP 简化为 P 矩阵。下面显示的代码中列出了两个投影矩阵。第一个是标准透视投影矩阵,第二个是我在网上找到的显式值投影矩阵。
CGRect screenBounds = [[UIScreen mainScreen] bounds];
const CGFloat screenWidth = screenBounds.size.width;
const CGFloat screenHeight = screenBounds.size.height;
const GLfloat n = 0.01f;
const GLfloat f = 100.0f;
const GLfloat fov = 60.0f * 2.0f * M_PI / 360.0f;
const GLfloat a = screenWidth/screenHeight;
const GLfloat d = 1.0f / tanf(fov/2.0f);
// Standard perspective projection.
GLKMatrix4 projectionMx = GLKMatrix4Make(d/a, 0.0f, 0.0f, 0.0f,
0.0f, d, 0.0f, 0.0f,
0.0f, 0.0f, (n+f)/(n-f), -1.0f,
0.0f, 0.0f, (2*n*f)/(n-f), 0.0f);
// The one I found online.
GLKMatrix4 projectionMx = GLKMatrix4Make(2.0f/screenWidth,0.0f,0.0f,0.0f,
0.0f,2.0f/-screenHeight,0.0f,0.0f,
0.0f,0.0f,1.0f,0.0f,
-1.0f,1.0f,0.0f,1.0f);
当使用显式值矩阵时,正方形在屏幕中心以正确的尺寸精确呈现。使用透视投影矩阵时,屏幕上不会显示任何内容。我已经打印(screenWidth/2, screenHeight/2, 0)
了透视投影矩阵为屏幕中心生成的位置值,它们非常大。显式值矩阵正确地产生零。
我认为显式值矩阵是正交投影矩阵 - 对吗?我的沮丧是我无法弄清楚为什么我的透视投影矩阵无法工作。
如果有人能帮助我解决这个问题,我将不胜感激。非常感谢。
更新克里斯蒂安·劳:
#define Zn 0.0f
#define Zf 100.0f
#define PRIMITIVE_Z 1.0f
//...
CGRect screenBounds = [[UIScreen mainScreen] bounds];
const CGFloat screenWidth = screenBounds.size.width;
const CGFloat screenHeight = screenBounds.size.height;
//...
glUseProgram(program);
//...
glViewport(0.0f, 0.0f, screenBounds.size.width, screenBounds.size.height);
//...
const CGFloat cx = screenWidth/2.0f;
const CGFloat cy = screenHeight/2.0f;
const CGFloat z = PRIMITIVE_Z;
const CGFloat dim = 50.0f;
vxData[0] = cx-dim;
vxData[1] = cy-dim;
vxData[2] = z;
vxData[3] = cx-dim;
vxData[4] = cy+dim;
vxData[5] = z;
vxData[6] = cx+dim;
vxData[7] = cy+dim;
vxData[8] = z;
vxData[9] = cx+dim;
vxData[10] = cy-dim;
vxData[11] = z;
//...
const GLfloat n = Zn;
const GLfloat f = Zf;
const GLfloat fov = 60.0f * 2.0f * M_PI / 360.0f;
const GLfloat a = screenWidth/screenHeight;
const GLfloat d = 1.0f / tanf(fov/2.0f);
GLKMatrix4 projectionMx = GLKMatrix4Make(d/a, 0.0f, 0.0f, 0.0f,
0.0f, d, 0.0f, 0.0f,
0.0f, 0.0f, (n+f)/(n-f), -1.0f,
0.0f, 0.0f, (2*n*f)/(n-f), 0.0f);
//...
// ** Here is the matrix you recommended, Christian:
GLKMatrix4 ts = GLKMatrix4Make(2.0f/screenWidth, 0.0f, 0.0f, -1.0f,
0.0f, 2.0f/screenHeight, 0.0f, -1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
GLKMatrix4 mvp = GLKMatrix4Multiply(projectionMx, ts);
更新 2
新的 MVP 代码:
GLKMatrix4 ts = GLKMatrix4Make(2.0f/screenWidth, 0.0f, 0.0f, -1.0f,
0.0f, 2.0f/-screenHeight, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f);
// Using Apple perspective, view matrix generators
// (I can solve bugs in my own implementation later..!)
GLKMatrix4 _p = GLKMatrix4MakePerspective(60.0f * 2.0f * M_PI / 360.0f,
screenWidth / screenHeight,
Zn, Zf);
GLKMatrix4 _mv = GLKMatrix4MakeLookAt(0.0f, 0.0f, 1.0f,
0.0f, 0.0f, -1.0f,
0.0f, 1.0f, 0.0f);
GLKMatrix4 _mvp = GLKMatrix4Multiply(_p, _mv);
GLKMatrix4 mvp = GLKMatrix4Multiply(_mvp, ts);
屏幕中心仍然看不到任何东西,并且屏幕中心的转换后的 x,y 坐标不为零。
更新 3
在上面的代码中使用转置ts
代替!但是正方形不再是正方形了;它现在似乎具有纵横比screenHeight/screenWidth
,即它具有平行于(短)屏幕宽度的较长尺寸,以及平行于(长)屏幕高度的较短尺寸。
我非常想知道(a)为什么需要转置以及它是否是有效的修复,(b)如何正确纠正非正方形维度,以及(c)transpose(ts)
我们使用的这个附加矩阵如何适合Viewport * Projection * View * Model * Point的变换链。
对于 (c):我理解矩阵的作用,即 Christian Rau 关于我们如何转换到范围 [-1, 1] 的解释。但是将这个额外的工作作为一个单独的转换矩阵包含在内是否正确,或者我们的 MVP 链的某些部分是否应该代替做这项工作?
衷心感谢 Christian Rau 迄今为止所做的宝贵贡献。
更新 4
我关于“ts 如何适应”的问题很愚蠢,不是吗 - 重点是矩阵只需要,因为我选择使用屏幕坐标作为我的顶点;如果我从一开始就使用世界空间中的坐标,那么这项工作就不需要了!
感谢 Christian 的所有帮助,这是非常宝贵的 :) 问题已解决。