OpenGL透视投影像素完美绘图
OpenGL Perspective Projection pixel perfect drawing
目标是绘制一个形状,比方说一个三角形,像素完美(顶点应以像素为单位指定)并能够在第 3 维中对其进行变换。
我用 正交投影矩阵 试过了,一切正常,但形状没有任何深度 - 如果我围绕 Y
轴看起来我只是围绕 X
轴缩放它。 (因为 正交 投影显然是这样的)。现在我想尝试使用 透视 投影。但是有了这个投影,坐标系就完全改变了,因此我不能用像素指定我的三角形顶点。此外,如果我的 window 的大小发生变化,形状的大小也会发生变化(因为坐标系发生了变化)。
有什么方法可以改变透视投影的坐标系,这样我就可以像使用正交投影一样指定我的顶点吗?或者有人知道如何实现第一句中描述的目标吗?
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。它从眼睛space变换到剪辑space,剪辑space中的坐标通过除以剪辑坐标。 NDC 的范围是 (-1,-1,-1) 到 (1,1,1)。
在透视投影中,投影矩阵描述了从针孔相机看到的世界中的 3D 点到视口的 2D 点的映射。
相机平截头体(截棱锥)中的眼睛 space 坐标映射到立方体(归一化设备坐标)。
透视投影矩阵:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
其中:
aspect = w / h
tanFov = tan( fov_y * 0.5 );
prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect)
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov
我假设视图矩阵是单位矩阵,因此视图 space 坐标等于世界坐标。
如果你想绘制一个多边形,其中顶点坐标被转换 1:1 为像素,那么你必须在与视口平行的平面上绘制多边形。这意味着所有点都必须以相同的深度绘制。
深度必须选择这种方式,即通过逆投影矩阵对归一化设备坐标中的点进行变换,得到以像素为单位的顶点坐标。请注意,通过逆投影矩阵变换给出的齐次坐标必须除以齐次坐标的 w
分量,才能得到笛卡尔坐标。
这意味着,平面的深度取决于投影的视角:
假设您设置了这样的透视投影:
float vp_w = .... // width of the viewport in pixel
float vp_h = .... // height of the viewport in pixel
float fov_y = ..... // field of view angle (y axis) of the view port in degrees < 180°
gluPerspective( fov_y, vp_w / vp_h, 1.0, vp_h*2.0f );
那么顶点坐标和像素的1:1关系的平面的depthZ
,会这样计算:
float angRad = fov_y * PI / 180.0;
float depthZ = -vp_h / (2.0 * tan( angRad / 2.0 ));
注意,投影到视口的中心点是(0,0),所以平面的左下角点是(-vp_w/2
,-vp_h/2
,depthZ
),右上角点为(vp_w/2
, vp_h/2
, depthZ
)。确保透视投影的近平面小于 -depthZ
,远平面大于 -depthZ
。
进一步了解:
目标是绘制一个形状,比方说一个三角形,像素完美(顶点应以像素为单位指定)并能够在第 3 维中对其进行变换。
我用 正交投影矩阵 试过了,一切正常,但形状没有任何深度 - 如果我围绕 Y
轴看起来我只是围绕 X
轴缩放它。 (因为 正交 投影显然是这样的)。现在我想尝试使用 透视 投影。但是有了这个投影,坐标系就完全改变了,因此我不能用像素指定我的三角形顶点。此外,如果我的 window 的大小发生变化,形状的大小也会发生变化(因为坐标系发生了变化)。
有什么方法可以改变透视投影的坐标系,这样我就可以像使用正交投影一样指定我的顶点吗?或者有人知道如何实现第一句中描述的目标吗?
投影矩阵描述了从场景的 3D 点到视口的 2D 点的映射。它从眼睛space变换到剪辑space,剪辑space中的坐标通过除以剪辑坐标。 NDC 的范围是 (-1,-1,-1) 到 (1,1,1)。
在透视投影中,投影矩阵描述了从针孔相机看到的世界中的 3D 点到视口的 2D 点的映射。
相机平截头体(截棱锥)中的眼睛 space 坐标映射到立方体(归一化设备坐标)。
透视投影矩阵:
r = right, l = left, b = bottom, t = top, n = near, f = far
2*n/(r-l) 0 0 0
0 2*n/(t-b) 0 0
(r+l)/(r-l) (t+b)/(t-b) -(f+n)/(f-n) -1
0 0 -2*f*n/(f-n) 0
其中:
aspect = w / h
tanFov = tan( fov_y * 0.5 );
prjMat[0][0] = 2*n/(r-l) = 1.0 / (tanFov * aspect)
prjMat[1][1] = 2*n/(t-b) = 1.0 / tanFov
我假设视图矩阵是单位矩阵,因此视图 space 坐标等于世界坐标。
如果你想绘制一个多边形,其中顶点坐标被转换 1:1 为像素,那么你必须在与视口平行的平面上绘制多边形。这意味着所有点都必须以相同的深度绘制。
深度必须选择这种方式,即通过逆投影矩阵对归一化设备坐标中的点进行变换,得到以像素为单位的顶点坐标。请注意,通过逆投影矩阵变换给出的齐次坐标必须除以齐次坐标的 w
分量,才能得到笛卡尔坐标。
这意味着,平面的深度取决于投影的视角:
假设您设置了这样的透视投影:
float vp_w = .... // width of the viewport in pixel
float vp_h = .... // height of the viewport in pixel
float fov_y = ..... // field of view angle (y axis) of the view port in degrees < 180°
gluPerspective( fov_y, vp_w / vp_h, 1.0, vp_h*2.0f );
那么顶点坐标和像素的1:1关系的平面的depthZ
,会这样计算:
float angRad = fov_y * PI / 180.0;
float depthZ = -vp_h / (2.0 * tan( angRad / 2.0 ));
注意,投影到视口的中心点是(0,0),所以平面的左下角点是(-vp_w/2
,-vp_h/2
,depthZ
),右上角点为(vp_w/2
, vp_h/2
, depthZ
)。确保透视投影的近平面小于 -depthZ
,远平面大于 -depthZ
。
进一步了解: