Z坐标、深度缓存和透视投影

如需转载请注明出处:http://blog.csdn.net/skyman_2001

这个是3D图形学中很重要的内容,虽然基础,但对理解3D图形世界非常关键。所以了解透彻是很有用处的。

下面先讲讲Z坐标。Z坐标和X、Y坐标一样。在变换、裁减和透视除法后,Z的范围为-1.0~1.0。DepthRange映射指定Z坐标的变换,这与用于将X和Y映射到窗口坐标的视口变换类似,但DepthRange映射又与视口映射有所不同,因为深度缓存的硬件方案对应用程序来说是隐藏的。调用DepthRange的参数是[0.0,1.0],与一片断相联的Z值(深度值)表示到眼睛的距离。在默认情况下,最接近眼睛的片断(在近截面上)被映射到0.0,离眼睛最远的片断(在远截面上)映射到1.0。片断可以映射为深度缓存范围的子集(通过在DepthRange中指定更小的值)。映射也可以相反,这样的话离眼睛最远的片断在0.0,最近的片断在1.0(调用DepthRange(1.0,0.0)),虽然这样反向映射是可以的,但对实际应用作用不大。

要理解为什么渲染质量上会不一致,最重要的是要理解屏幕Z坐标的特性。Z值指定了从片断到眼睛的距离。在正交投影中距离和Z值的关系是线性的,但在透视投影中却不是的。在透视投影中这种关系是非线性的,而且非线性的程度与Frustum函数中的far/near(或gluPerspective函数中的zFar/zNear)成比例。这种非线性在靠近近截面时增加了Z值的精度,而且增加了深度缓存的效率;但是在视见体的其它部分则降低了精度,也就减少了深度缓存的精确性。根据经验,far/near比值大于1000会有这种不好的效果。所以一般far/near比值应小于1000。要想解决这个问题,最简单的方法是通过将近截面远离眼睛来降低far/near比值,其唯一的副作用是离眼睛很近的物体可能会被裁减掉,但在特定的应用程序中这很少是个问题,近截面的位置对X、Y坐标的投影没有影响,因此这对图像的影响很小。

还有OpenGL光栅化和深度缓存的一些其他方面值得一提。一个大问题是光栅化过程使用不精确的算法,所以很难处理共面的图元,除非它们共享相同的平面方程。这个问题在有限精度的深度缓存实现中更加严重。这些问题包括:贴花(decaling)、隐藏线消除、轮廓多边形和阴影等。不过现在已经提出许多方法来解决这些问题,如PolygonOffset技术等。

深度缓存的位数是衡量深度缓存精度的参数。深度缓存位数越高,则精确度越高,目前的显卡一般都可支持16位的Z Buffer,一些高级的显卡已经可以支持32位的Z Buffer,但一般用24位Z Buffer就已经足够了。

以下是使用OpenGL生成立方体并生成正交投影透视投影视图的示例代码: ```c++ #include <GL/glut.h> // 定义立方体顶点坐标 GLfloat vertices[] = { -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f }; // 定义立方体面索引 GLubyte indices[] = { 0, 1, 2, 3, 3, 2, 6, 7, 7, 6, 5, 4, 4, 5, 1, 0, 0, 3, 7, 4, 1, 2, 6, 5 }; // 定义正交投影透视投影的视角参数 GLfloat ortho_left = -2.0f; GLfloat ortho_right = 2.0f; GLfloat ortho_bottom = -2.0f; GLfloat ortho_top = 2.0f; GLfloat ortho_near = -2.0f; GLfloat ortho_far = 2.0f; GLfloat persp_fovy = 60.0f; GLfloat persp_aspect = 1.0f; GLfloat persp_near = 1.0f; GLfloat persp_far = 10.0f; // 绘制立方体 void draw_cube() { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(3, GL_FLOAT, 0, vertices); glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, indices); glDisableClientState(GL_VERTEX_ARRAY); } // 绘制正交投影视图 void draw_ortho_view() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(ortho_left, ortho_right, ortho_bottom, ortho_top, ortho_near, ortho_far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); draw_cube(); } // 绘制透视投影视图 void draw_persp_view() { glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(persp_fovy, persp_aspect, persp_near, persp_far); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0.0f, 0.0f, 4.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f); draw_cube(); } // 显示函数 void display() { glClear(GL_COLOR_BUFFER_BIT); // 绘制正交投影视图 glViewport(0, 0, 200, 200); draw_ortho_view(); // 绘制透视投影视图 glViewport(200, 0, 200, 200); draw_persp_view(); glutSwapBuffers(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize(400, 200); glutCreateWindow("OpenGL Cube"); glClearColor(1.0f, 1.0f, 1.0f, 0.0f); glutDisplayFunc(display); glutMainLoop(); return 0; } ``` 在上述代码中,首先定义了立方体的顶点坐标和面索引。然后定义了正交投影透视投影的视角参数。接着实现了一个绘制立方体的函数 `draw_cube()`。最后在 `display()` 函数中,使用 `glViewport()` 函数指定窗口区域,分别调用 `draw_ortho_view()` 和 `draw_persp_view()` 函数来绘制正交投影透视投影视图。其中,通过 `glMatrixMode()` 和 `glLoadIdentity()` 函数来重置投影矩阵和模型视图矩阵,并使用 `glOrtho()` 和 `gluPerspective()` 函数来设置正交投影透视投影的视角参数。通过 `gluLookAt()` 函数来设置相机位置和目标点位置。最后,通过 `glClear()` 函数清空颜色缓存,并使用 `glutSwapBuffers()` 函数交换前后缓冲区。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值