默认情况下,NDC 是基于左手坐标系
首先要知道当不调用 glDepthRange 修改映射时, OpenGL NDC 是基于左手坐标系的。这就意味着默认时,物体的 z 轴越大,则物体的坐标越远。继续往下看,你将会发现它的用处。下面就写程序来验证一下。先程序运行结果。
程序中一会绘制了 5 个图形,左半部分两个,右半部份两个,中间的矩形一个。点坐标如下
const GLfloat vertices_leftup[] = { // left up red
-1.0f, -0.5f, -1.0f,
0.0f, -0.5f, -1.0f,
-0.5f, 1.0f, -1.0f,
};
const GLfloat vertices_leftdown[] = { // left down blue
-1.0f, -1.0f, 0.5f,
0.0f, -1.0f, 0.5f,
-0.5f, 0.5f, 0.5f,
};
const GLfloat vertices_rightup[] = { // right up red
0.0f, -0.5f, 0.5f,
1.0f, -0.5f, 0.5f,
0.5f, 1.0f, 0.5f,
};
const GLfloat vertices_rightdown[] = { // right down blue
0.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
0.5f, 0.5f, -1.0f,
};
const GLfloat vertices_rect[] = { // center green
-1.0f, -0.5f, 0.0f,
1.0f, -0.5f, 0.0f,
1.0f, 0.5f, 0.0f,
-1.0f, 0.5f, 0.0f,
};
顶点的坐标本身就处于 [-1, 1] 范围内,我们也未做任何坐标变换,因此这些坐标就等于最终的 NDC 坐标值。若 NDC 是基于左手坐标系,则 z 轴上的坐标值越小,就越显示在前面。按照 z 值从小到大排序则是 left up red, right down blue(-1.0f) > center green(0.0f) > left down blue, right up red(0.5f) 。左上角的红色三角形与右下角的蓝色三角形显示在最前面,中间的绿色矩形显示在中间,左下的蓝色三角形和右上的红色三角形显示在最远处。验证了 NDC 是基于左手坐标系。注意,代码中要开启深度测试。 lookAt 参数
/// Build a look at view matrix based on the default handedness.
/// @param eye Position of the camera
/// @param center Position where the camera is looking at
/// @param up Normalized up vector, how the camera is oriented. Typically (0, 0, 1)
template <typename T, precision P>
GLM_FUNC_DECL tmat4x4<T, P> lookAt(
tvec3<T, P> const & eye,
tvec3<T, P> const & center,
tvec3<T, P> const & up);
glm::lookAt 可用于产生视图矩阵(view matrix)将 world space 中的点转换到 view/eye space 。函数参数 eye 是 world space 中摄像机的坐标位置,参数 center 是 world space 中摄像机指向的点,up 是指向上方的向量,通常是 (0, 0, 1) 。lookAt 的结果受到是左手坐标系还是右手坐标系的影响,若代码中定义了宏 GLM_LEFT_HANDED 则 glm 调用左手坐标系版本,若没有定义则调用右手坐标系版本,默认是没有定义的,因此默认用的就是右手坐标系版本,glm::perspective 也一样受到此宏的影响。
下面就通过代码来验证 glm::lookAt函数参数中点的位置是 world space 。需求就是计算 world space 某一点 somepoint 到摄像机的距离。有两种计算方式。
void
calc_distance_from_camera() {
glm::vec4 somepoint(5.0f, 5.0f, 5.0f, 1.0f);
glm::vec3 camerapos(2.0f, 2.0f, 2.0f);
glm::mat4 viewmat = glm::lookAt(camerapos, glm::vec3(0, 0, 0), glm::vec3(0, 1.0f, 0));
glm::vec4 somepoint_view = viewmat * somepoint; // transform somepoint to view space
// 当只有 view matrix 时,也可以计算出摄像机的位置,下面代码就展示了这种计算方式,
// 至于原理可以先忽略,涉及到数学的部分总是让人害怕。
glm::mat4 viewmat_inverse = glm::inverse(viewmat);
glm::vec3 camerapos_calc(viewmat_inverse[3]);
printf("here camera pos: %.2f %.2f, %.2f\n", camerapos.x, camerapos.y, camerapos.z);
printf("calc camera pos: %.2f %.2f, %.2f\n", camerapos_calc.x, camerapos_calc.y, camerapos_calc.z);
// 计算点 somepoint 距离摄像机的距离
// 方式 1 ,在 world space 中计算距离
float distance_world = glm::distance2(glm::vec3(somepoint), camerapos_calc);
printf("distance calc in world space:%.2f\n", distance_world);
// 方式 2 ,在 view space 中计算距离,
// view space 就是在摄像机位置中观看对象,此时摄像机就相当于原点
glm::vec3 camerapos_view(0, 0, 0);
float distance_view = glm::distance2(glm::vec3(somepoint_view), camerapos_view);
printf("distance calc in view space:%.2f\n", distance_view);
}
输出结果如下:
here camera pos: 2.00 2.00, 2.00
calc camera pos: 2.00 2.00, 2.00
distance calc in world space:27.00
distance calc in view space:27.00
perspective 参数
/// Creates a matrix for a symetric perspective-view frustum based on the default handedness.
/// @param fovy Specifies the field of view angle in the y direction. Expressed in radians.
/// @param 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).
/// @param near Specifies the distance from the viewer to the near clipping plane (always positive).
/// @param far Specifies the distance from the viewer to the far clipping plane (always positive).
/// @tparam T Value type used to build the matrix. Currently supported: half (not recommanded), float or double.
template <typename T>
GLM_FUNC_DECL tmat4x4<T, defaultp> perspective(T fovy, T aspect, T near, T far);