gluPerspective右手坐标系中透视投影剖析-线性中都要除以-z 和 zn、zf是距离

1.结果是没有除以w之前的值
yScale = cot(fovY/2), 
fovY越小,yScale缩放比例差距越大, 一般fovY取90度,fovY/2为45度, 视图坐标系到透视4D空间中yScale = 1。
2.w/h比例越大说明xScale缩放比例越小,yScale由fovY指定,而且要正好反比例:w/h = yScale/xScale 。
xScale = yScale / aspect,
aspect = w/h. 
aspect 一般取屏幕的宽高,这样是等比缩放,没有拉伸效果,如果yScale = 1, w/h等于4:3,那么视图坐标系到透视4D空间中xScale = 3/4。

视锥体的上下夹角(6面体),z轴平分该夹角。

 fovy变小时候,因为也要在屏幕90度中用,所以高度方向被放大了,反之fovy变大,高度方向变小。

aspectaspect = w / h,fovy = 90度,由投影矩阵的计算过程,投影yScale = cot(fovy/2); xScale = yScale /aspect.

当屏幕w:h = 100:20时,当aspect = 5:1,那么视锥内的[5,1]正方形截面世界将被变换映射到(1,1)的平面内,

xScale压缩了1/5,当透视投影映射到屏幕坐标时候[5,1],xScale方向需要根据屏幕放大5倍,这样视锥体里面的世界等比缩放到屏幕。

aspect = 1变小;那么yScale = 1, xScale = 1,视锥体内的xy正方形截面[1,1]世界被放置到(1,1); 映射到屏幕为[5,1]xScale映射到屏幕被放大了5倍,yScale不变。

aspect = 10变大,那么yScale = 1,xScale = 1/10,视锥体内的xy正方形截面[10,1]世界->(1,1)->[5,1],xScale被缩小了2倍,yScale不变。


3.除z或-z后计算只是用后面的关系方便,转换为前面的关系(4D中)还是az+b = z'的。
m31 = 1或-1是最隐秘的雕虫小技。

4.在RH z NDC坐标系中转换到[0,1],OGL中转换到[-1,1],z'= (az + b)/-z => z' = -a + b/-z =>当用zn代入时因为zn是正数,-z也是正数,
所以为:z' = -a + b/zn
        z' = -a + b/zf

D3DXMatrixPerspectiveFovRH中,通过代码实验得到:
z' =( az + b) / -z = -a - b/z;
得到:
0 = -a + b / zn;
1 = -a + b / zf;
可以推导出矩阵中a,b的值。
D3DXMatrixPerspectiveFovRH变换矩阵:
D3DXMATRIX* D3DXMatrixPerspectiveFovRH(
  _Inout_ D3DXMATRIX *pOut,
  _In_    FLOAT      fovy,
  _In_    FLOAT      Aspect,
  _In_    FLOAT      zn,
  _In_    FLOAT      zf
);
xScale     0          0              0
0        yScale       0              0
0        0        zf/(zn-zf)        -1
0        0        zn*zf/(zn-zf)      0
where:
// 结果是没有除以w之前的值
yScale = cot(fovY/2)
// 通过yScale和缩放比例aspect = w/h,求取xScale
xScale = yScale / aspect ratio
D3DXMatrixPerspectiveFovLH变换矩阵:
D3DXMATRIX* D3DXMatrixPerspectiveFovLH(
  _Inout_ D3DXMATRIX *pOut,
  _In_    FLOAT      fovy,
  _In_    FLOAT      Aspect,
  _In_    FLOAT      zn,
  _In_    FLOAT      zf
);
xScale     0          0               0
0        yScale       0               0
0          0       zf/(zf-zn)         1
0          0       -zn*zf/(zf-zn)     0
where:
yScale = cot(fovY/2)

xScale = yScale / aspect ratio


同理在gluPerspective中:
z' =( az + b) / -z = -a - b/z;
得到:
-1 = -a + b / zn;
1 = -a + b / zf;
可以推导出矩阵中a,b的值。
void gluPerspective(GLdouble fovy,  GLdouble aspect,  GLdouble zNear,  GLdouble zFar);

变换公式:

gluPerspective右手坐标系中透视投影剖析-线性中都要除以-z 和 zn、zf是距离_第1张图片

Name

gluPerspective — set up a perspective projection matrix

C Specification

void gluPerspective( GLdouble fovy,

GLdouble aspect,

GLdouble zNear,

GLdouble zFar);
 

Parameters

fovy

Specifies the field of view angle, in degrees, in the y direction.

aspect

Specifies the aspect ratio that determines the field of view in the x direction. The aspect ratio is the ratio of x (width) to y (height).

zNear

Specifies the distance from the viewer to the near clipping plane (always positive).

zFar

Specifies the distance from the viewer to the far clipping plane (always positive).

Description

gluPerspective specifies a viewing frustum into the world coordinate system. In general, the aspect ratio in gluPerspective should match the aspect ratio of the associated viewport. For example, aspect = 2.0 means the viewer's angle of view is twice as wide in x as it is in y. If the viewport is twice as wide as it is tall, it displays the image without distortion.

The matrix generated by gluPerspective is multipled by the current matrix, just as if glMultMatrix were called with the generated matrix. To load the perspective matrix onto the current matrix stack instead, precede the call to gluPerspective with a call to glLoadIdentity.

Given f defined as follows:

f = cotangent ⁡ fovy 2

The generated matrix is

f aspect 0 0 0 0 f 0 0 0 0 zFar + zNear zNear - zFar 2 × zFar × zNear zNear - zFar 0 0 -1 0

Notes

Depth buffer precision is affected by the values specified for zNear and zFar. The greater the ratio of zFar to zNear is, the less effective the depth buffer will be at distinguishing between surfaces that are near each other. If

r = zFar zNear

roughly log 2 ⁡ r bits of depth buffer precision are lost. Because r approaches infinity as zNear approaches 0, zNear must never be set to 0.


1.转换到4D投影矩阵和真正的投影比较研究:
真正的3D物理透视投影是:x'=-d*x / z; y' = -d*y / z; z' = -d
而图形管线中的透视投影是先由投影矩阵投影到4D裁剪空间中,再由硬件进行/w透视除法到DNC坐标空间中,实现真正的透视投影。
其实图形管线中也是有特别的透视投影规定的,比如规定投影到的平面不是z = -d,而是z=d,投影是:
x'=d*x / z; y' = d*y / z; z' = d;

而且不单单是投影到一个平面上,而是投影到一个立方体(OGL)或长方体(DX)中,因此就有了远近缩放的问题,其中视锥体,也包含了x,y缩放在内。
所以 y' = y * cot(fovy/2) / z,在裁剪4D空间中 y' = y * cot(fovy/2).当fovy越小时候y'表现越大符合观察现象。
当确定了yScale = cot(fovy/2)后,真实中zoomx / zoomy =  xScale' / yScale' = w / h。但因为x, y都要先规范化到DNC [-1, 1]坐标系中,也就是x要先缩小放入[-1,1]中,后面进行视口计算时候才能将xScale按照屏幕比例放大到屏幕中。
所以:xScale = yScale / (w / h);因为后面视口计算会dncX会乘以w放到到屏幕片元中。

这里:cot(fovy/2)其实是标准透视投影中的z=d了,但是又不完全相同,因为x的d和y的d又不同,因为要保持等比例的缩放,其实也不是纯洁的物理透视投影了,只是为了实现等比透视缩放的数学表达形式。


2.真正透视投影到NDC坐标系,NDC是否都是左手坐标系。
OGL中确实是把,z= -zn, z=-zf的z值坐标转换到了[-1,1]坐标系中,也就是OGL视图(摄像机)坐标系中的z轴本来是向里是-z轴的,经过透视矩阵其实是进行部分透视投影到裁剪空间时,其实是进行了z轴的翻转的,也就是变到了左手标准NDC坐标系中,只是z值在[-zn, zf]中才不会被裁剪掉,裁剪掉时候对比的[-w,w]值,其实w就是单个顶点上的w值,该w值是视图空间中的每个点的z乘以-1得到的。裁剪后硬件进行除以w也就是-z为正数,透视除法得到NDC坐标系值,NDC坐标系也是规范化的左手坐标系,只是z在[-1,1], DX是z在[0,1]。
az大于负数b=2*Zf*Zn / (Zn - Zf)的才为正数。

后面硬件进行视口变换,也就是根据不同的屏幕坐标系定义而已,整体还是NDC中左上角在屏幕左上角,NDC中右上角在屏幕右上角。(具体视口变换只是符合OGL,或DX的屏幕坐标系要求而已)。


你可能感兴趣的:(gluPerspective右手坐标系中透视投影剖析-线性中都要除以-z 和 zn、zf是距离)