C++ 像素完美投影矩阵?

C++ 像素完美投影矩阵?,c++,opengl,matrix,glm-math,C++,Opengl,Matrix,Glm Math,我试图理解我应该在lookat函数(或模型矩阵中的对象)中放置相机位置多远,才能在顶点着色器中传递像素完美坐标 这对于正交投影矩阵来说其实很简单,但我无法想象透视投影的数学原理 以下是我使用的透视矩阵: glm::mat4 projection = glm::perspective(45.0f, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 10000.0f); 着色器中的顶点乘法非常简单,如下所示: gl_Position = projection

我试图理解我应该在lookat函数(或模型矩阵中的对象)中放置相机位置多远,才能在顶点着色器中传递像素完美坐标

这对于正交投影矩阵来说其实很简单,但我无法想象透视投影的数学原理

以下是我使用的透视矩阵:

glm::mat4 projection = glm::perspective(45.0f, (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 10000.0f);
着色器中的顶点乘法非常简单,如下所示:

gl_Position = projection * view * model * vec4(position.xy, 0.0f, 1.0);

我基本上是想在屏幕上显示一个需要旋转并显示透视效果的四边形(因此我不能使用正交投影),但我想在像素坐标中指定它在屏幕上显示的位置和大小。

它只能在一个“z平面”中有像素坐标如果要使用梯形视锥体

基础数学 如果使用标准相机,则(0,0,0)处相机的基本数学为

阿尔法为垂直视场(在您的情况下为45°)

目标y=tan(α/2)*z距离*((像素y/高度)*2-1)
目标x=tan(α/2)*z距离*((像素x/宽度)*纵横比*2-1)

反向投影 至于一般情况。您可以“取消项目”以在所有变换结束于特定点之前找到三维中的点的位置

基本上你需要不做数学题

gl_Position = projection * view * model * vec4(position.xy, 0.0f, 1.0);
因此,如果您拥有最终位置并希望恢复,请执行以下操作:

unprojection =  model^-1 * view^-1 *projection^-1 * gl_Position //not actual glsl notation, '^-1' being the inverse
这基本上就是像
glunproject
glm::gtc::matrix_transform::unProject
这样的函数所做的

但您应该注意,应用投影矩阵后的最终剪辑空间通常为[-1,-1,0]到[1,1,1],因此,如果要输入像素坐标,可以应用附加矩阵变换到该空间

比如:

               [2/width,        0,     0    -1]
               [      0, 2/height,     0    -1]
screenToClip = [      0,        0,     1     0]
               [      0,        0,     0     1]
将[0,0,0,1]转换为[-1,-1,0,1],并将[width,height,0,1]转换为[1,1,0,1]


此外,您最好尝试一些z值,如0.5,以确保您位于视锥体内部,并且不在前面或后面进行剪裁。

如果您要使用梯形视锥体,则它只能在一个“z平面”中具有像素坐标

基础数学 如果使用标准相机,则(0,0,0)处相机的基本数学为

阿尔法为垂直视场(在您的情况下为45°)

目标y=tan(α/2)*z距离*((像素y/高度)*2-1)
目标x=tan(α/2)*z距离*((像素x/宽度)*纵横比*2-1)

反向投影 至于一般情况。您可以“取消项目”以在所有变换结束于特定点之前找到三维中的点的位置

基本上你需要不做数学题

gl_Position = projection * view * model * vec4(position.xy, 0.0f, 1.0);
因此,如果您拥有最终位置并希望恢复,请执行以下操作:

unprojection =  model^-1 * view^-1 *projection^-1 * gl_Position //not actual glsl notation, '^-1' being the inverse
这基本上就是像
glunproject
glm::gtc::matrix_transform::unProject
这样的函数所做的

但您应该注意,应用投影矩阵后的最终剪辑空间通常为[-1,-1,0]到[1,1,1],因此,如果要输入像素坐标,可以应用附加矩阵变换到该空间

比如:

               [2/width,        0,     0    -1]
               [      0, 2/height,     0    -1]
screenToClip = [      0,        0,     1     0]
               [      0,        0,     0     1]
将[0,0,0,1]转换为[-1,-1,0,1],并将[width,height,0,1]转换为[1,1,0,1]


此外,您最好尝试一些z值,如0.5,以确保您位于视图平截体内,而不是在靠近前面或后面的位置进行剪裁。

我将扩展PeterT答案,并在这里留下我用于通过非投影查找平截体平面之一的世界坐标的实用代码

这假设一个基本视图矩阵(相机位置为0,0,0)

glm::mat4 projectionInv(0);
glm::mat4投影=glm::透视图(45.0f,(浮动)SCR_宽度/(浮动)SCR_高度,0.1f,500.0f);
projectionInv=glm::逆(投影);
std::载体NDCCube;
NDCCube.向后推(glm::vec4(-1.0f,-1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,-1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,-1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,-1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,1.0f,-1.0f,1.0f));
NDCCube.push_back(glm::vec4(1.0f,1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,1.0f,1.0f,1.0f));
std::向量截锥体的垂直度;
对于(int i=0;i<8;i++)
{
glm::vec4 tempvec;
tempvec=projectionInv*NDCCube.at(i);//乘以投影矩阵逆,得到截锥体顶点
平截头体的曲率。向后推(glm::vec3(tempvec.x/=tempvec.w,tempvec.y/=tempvec.w,tempvec.z/=tempvec.w));
}

请记住,如果您的透视远距低于我在投影矩阵中设置的远距,这些坐标不会出现在屏幕上

我将扩展PeterT答案,并将我用于通过非投影查找一个平截头体平面的世界坐标的实用代码留在这里

这假设一个基本视图矩阵(相机位置为0,0,0)

glm::mat4 projectionInv(0);
glm::mat4投影=glm::透视图(45.0f,(浮动)SCR_宽度/(浮动)SCR_高度,0.1f,500.0f);
projectionInv=glm::逆(投影);
std::载体NDCCube;
NDCCube.向后推(glm::vec4(-1.0f,-1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,-1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,-1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,-1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,1.0f,-1.0f,1.0f));
NDCCube.向后推(glm::vec4(1.0f,1.0f,-1.0f,1.0f));
NDCCube.push_back(glm::vec4(1.0f,1.0f,1.0f,1.0f));
NDCCube.向后推(glm::vec4(-1.0f,1.0f,1.0f,1.0f));
std::向量截锥体的垂直度;
对于(int i=0;i<8;i++)
{
glm::vec4 tempvec;
tempvec=projectionInv*NDCCube.at(i);//乘以投影矩阵逆,得到截锥体顶点
平截头体的曲率。向后推(glm::vec3(tempvec.x/=tempvec.w,tempvec.y/=tempvec.w,tempvec.z/=tempvec.w));
}