OpenGL编程(七)3D模型的深度(z轴)检测

下图是我们要修改后的效果图:
这里写图片描述

一、深度检测

1、模型Z轴显示有问题:
上一次试验中,如果认真留意,会发现一个问题。当控制锥体在左右或上下旋转时,你会发现锥体看起来是在+-180度之间来回摆动,而不是360度的旋转。锥体的底面总是朝向观察者。这个我们可以通过修改锥体底面的颜色方便观察。如下图:
这里写图片描述
这里写图片描述

正如上面的两幅图,在不同的角度看,底面永远都是在最上面。其实这是跟我们前面代码上的画图有关。前面我们的代码是,先画锥体的侧面,再画底面。类似于我们小学画画时一样,最后画的都是把前面画的给覆盖了。

那应该怎样处理这个问题呢?

2、怎样处理Z轴显示的问题:

这个问题不需要我们操心,OpenGL已经帮我们处理好了。我们只需要添加一个语句,开启OpenGL的深度检测。

glEnable(GL_DEPTH_TEST);

3、深度检测原理
OpenGL在2D平面绘制3D场景时,对每一个绘画的像素点都分配一个Z轴的值(即这个点在3D场景里距离观察者的距离)。有一个深度缓冲区,每次绘画一个像素点就会把该像素点的信息添加进深度缓冲区中。绘制新的像素点时,先判断在同一个2D位置是否已经绘画过像素点,如果是,判断新绘画的像素点的Z轴是不是比已经花了的Z轴更接近观察者,如果是则覆盖掉前面的像素点,修改深度缓冲区,否则不需要画该像素点。

注意:每帧前我们都需要清空深度缓冲区,否则会导致z轴判断错误。因为每一帧中,某个像素点的z轴的值可能会变化。

glClear(GL_DEPTH_BUFFER_BIT);

4、增加深度检测后的效果
加入深度检测后:
这里写图片描述

这里写图片描述

二、剔除隐藏表面

1、剔除隐藏表面好处
首先介绍绘图的底层工作原理,有一个3D模型,首先我们先确定需要绘制的3D模型的像素点,把这些像素点发送给计算机图形处理(GPU)相关硬件;相关硬件把这个3D模型绘制到2D屏幕上会检测Z轴(我们前面说到的深度检测)。

从上面的工作步骤我们容易发现,对于3D模型背后的部分,有时我们不需要画出来,可是还是进行了Z轴检测。如果我们在前面处理,直接把背后我们看不见的部分,不用发送给GPU处理的相关硬件,将会提升渲染性能。

这种剔除技术就是“回溯剔除”,回溯剔除将剔除掉3D模型的内部。使用回溯剔除能大大提升渲染性能。

2、正面与背面
在上一节中,我们讲了怎样通过3角形绕出3D模型。我们通过三角形绕出来的面,有正面、背面。

按照OpenGL的约定,通过顺时针绕法,绕出来的面,我们看到的是正面,另外一面是背面,
这里写图片描述
如上图所示,对于底面,上面(靠近侧面的一面)是正面,底面是背面。对于侧面,按照我们的绕法,外面是正面,里面是背面。

2、开启剔除

glEnable(GL_CULL_FACE);

使用上面的语句能开启回溯剔除功能。

3、启用剔除容易引起的问题
如前面的例子,我们开启了回溯剔除的效果如下:
这里写图片描述
如上图所示,开启回溯剔除后,把 锥体的底面给剔除了。正如2中的分析,我们的锥体底面上面是正面,下面是背面,所我们看到的背面被剔除掉了。

那应该怎样处理这个问题呢?

很简单,我们在绕锥体底面的时候,我们使用逆时针绕法,这样锥体底面的在下面的是正面,靠近侧面的是背面,这样当我们转到锥体底面的角度的时候就不会被剔除掉了。
逆时针绕法

glFrontFace(GL_CCW);

顺时针绕法

glFrontFace(GL_CW);

我们在绕侧面事前开启顺时针绕法,在绕底面之前开发逆时针绕法。这样就能保证在开启回溯剔除不影响我们的视觉效果。如一下代码:

这里写图片描述

这里写图片描述
4、为什么需要回溯剔除

也许有同学会疑问“为什么还要设置开启、关闭回溯剔除呢?而不是直接设置成回溯剔除。为什么还需要我们去开启、关闭?”

因为在绘制3D图形的过程中,我们有的时候是不能看到物品的背面,可是当物品的前面是玻璃材料,具有半透明会透明效果的时候,我们是需要去显示背面,所以要根据我们的需求再自己决定是否需要开启回溯剔除。

三、总结

这次实验主要是在上一次实验的基础上进行修改。上次的实验看似完美,可是如果认真检查会发现前面提到的问题。通过这次实验,掌握了深度检测,回溯剔除的基本原理以及使用方法。

四、修改后的效果

这里写图片描述

五、完整代码

#include <windows.h>
#include <gl/glut.h>
#include<math.h>

const GLfloat PI = 3.1415f;
GLfloat xRot = 0.0f;
GLfloat yRot = 0.0f;

void rendererScene(void);
void changeWindowSize(GLsizei w, GLsizei h);
void setupRC(void);
void rotateMode(int key, int x, int y);

int main(int argc, char* argv[])
{
    //设置显示模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);

    //设置窗口大小
    glutInitWindowSize(300, 300);

    //设置窗口在屏幕上的位置
    glutInitWindowPosition(200, 200);

    //创建窗口标题
    glutCreateWindow("三角形绘3D模型");

    //注册显示窗口时回调渲染函数
    glutDisplayFunc(rendererScene);


    glOrtho(-100.0f, 100.0f, -100.0f, 100.0f, -100.0f, 100.0f);

    //注册窗口大小改变时回调函数
    glutReshapeFunc(changeWindowSize);

    //注册点击上下左右方向按钮时回调rotateMode函数
    glutSpecialFunc(rotateMode);


    setupRC();

    //消息循环(处理操作系统等的消息,例如键盘、鼠标事件等)
    glutMainLoop();
    return 0;
}

/**
渲染函数
*/
void rendererScene(void)
{
    GLfloat x, y, angle;

    BOOL bCull = TRUE;  //是否开启回溯剔除
    BOOL bDepth = FALSE;    //是否开启深度检测

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    if(bCull) glEnable(GL_CULL_FACE);
    else glDisable(GL_CULL_FACE);

    if(bDepth) glEnable(GL_DEPTH_TEST);
    else glDisable(GL_DEPTH_TEST);

    glPushMatrix();
    glRotatef(xRot, 1.0f, 0.0f, 0.0f);
    glRotatef(yRot, 0.0f, 1.0f, 0.0f);

    int ipvot = 0;
    glFrontFace(GL_CW);

    glBegin(GL_TRIANGLE_FAN);
    glVertex3f(0.0f, 0.0f, 75.0f);


    for(angle = 0.0f; angle < (2.0f * PI); angle += (PI / 8.0f))
    {
        x = 50.0f * sin(angle);
        y = 50.0f * cos(angle);

        if(ipvot % 2 == 0) glColor3f(0.0f, 1.0f, 0.0f);
        else glColor3f(1.0f, 0.0f, 0.0f);

        ipvot++;

        glVertex3f(x, y, 0.0f);

    }
    glEnd();

    glFrontFace(GL_CCW);

    glBegin(GL_TRIANGLE_FAN);
    glVertex3f(0.0f, 0.0f, 0.0f);

    for(angle = 0; angle < (2.0f * PI); angle += (PI / 8.0f))
    {
        x = 50.0f * sin(angle);
        y = 50.0f * cos(angle);

        if(ipvot % 2 == 0) glColor3f(0.0f, 1.0f, 1.0f);
        else glColor3f(0.0f, 0.0f, 1.0f);

        ipvot++;

        glVertex3f(x, y, 0.0f);
    }

    glEnd();



    glPopMatrix();

    glutSwapBuffers();


}

/**
改变窗口大小时回调函数
*/
void changeWindowSize(GLsizei w, GLsizei h)
{
    GLfloat length = 100.0f;
    if(h == 0) h = 1;
    glViewport(0, 0, w, h);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    if(w <= h) glOrtho(-length, length, -length * h / w, length * h / w, -length, length);
    else glOrtho(-length * w / h, length * w / h, -length, length, -length, length);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/**
设置
*/
void setupRC(void)
{
    //背景颜色
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

    glShadeModel(GL_FLAT);
}

/**
旋转
*/
void rotateMode(int key, int x, int y)
{
    if(key == GLUT_KEY_UP) xRot -= 5.0f;
    else if(key == GLUT_KEY_DOWN) xRot += 5.0f;
    else if(key == GLUT_KEY_LEFT) yRot -= 5.0f;
    else if(key == GLUT_KEY_RIGHT) yRot += 5.0f;

    if(xRot < 0) xRot = 355.0f;
    if(xRot > 360.0f) xRot = 0.0f;

    if(yRot < 0) yRot = 355.0f;
    if(yRot > 360.0f) yRot = 0.0f;

    glutPostRedisplay();

}

<script type="text/javascript"> $(function () { $('pre.prettyprint code').each(function () { var lines = $(this).text().split('\n').length; var $numbering = $('<ul/>').addClass('pre-numbering').hide(); $(this).addClass('has-numbering').parent().append($numbering); for (i = 1; i <= lines; i++) { $numbering.append($('<li/>').text(i)); }; $numbering.fadeIn(1700); }); }); </script>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值