• OpenGL_Qt学习笔记之_06(纹理滤波、光照和色彩融合)


             

          本次实验主要是学习下opengl中光照的使用方法,opengl中的光照分为环境光,漫射光,镜面光,反射光4种,这里主要是学习环境光和漫射光的设置,同时对比下opengl中支持的几种纹理滤波方式的效果,另外也可以加入色彩融合效果。

     

      纹理滤波

         在上篇文章OpenGL_Qt学习笔记之_05(纹理映射)中我们采用的是GL_LINEAR方式进行滤波的,opengl还支持GL_NEAREST, GL_NEAREST_MIPMAP_NEAREST, GL_LINEAR_MIPMAP_NEAREST, GL_NEAREST_MIPMAP_LINEAR, GL_LINEAR_MIPMAP_LINEAR等几种方式,其中mipmap为计算量最大的方式,因为它保留了纹理的很多细节。

         由于我们不想让纹理图片的像素限制为2的n次方,所以在创建纹理时我们可以使用gluBuild2DMipmaps()函数代替glTexImage2D(),而gluBuild2DMipmaps()又是以glu开头的。

      其实我在opengl学习系列的第一篇文章中就发现了Qt新版本中不能使用opengl的glu开头的函数,文章为OpenGL_Qt学习笔记之_01(创建一个OpenGL窗口),因为那个时候初学,所以遇到编译找不到gluPerspective()标识符时,我直接把这个函数屏蔽掉了,反正当时也不理解该函数的功能。而在本人博客OpenGL_Qt学习笔记之_02(绘制简单平面几何图形)中也发现没有使用gluPerspective()函数时,画几何图形时,坐标和网上很多人的例子不同,我这里全是-1~+1之间的小数,因此这些函数的功能还是挺重要的,比不可少。

         下面给出我在windows平台下的解决方法。

          http://hi.baidu.com/%B3%A4%C6%BD%C5%A9%B7%F2/blog/item/573cb1f9dbf83a07d8f9fd53.html#0  

         过在它的方法步骤4中,在建立glut文件,是这样的:新建一个txt文件,里面输入代码:#include “glut.h”,然后把该txt文件的名字重命名为glut,注意这个地方不需要加后缀。另外需要特别注意的是,虽然我们把glut.h文件放入在qt目录下的\include\QtOpenGL下,且我们也在GLWidget.cpp中也加入了头文件#include<QtOpenGL>,但是因为QtSDK库编译的原因,我们在GLWidget.cpp中仍然需要添加#include<glut>语句才行。

      既然上面提到了gluPerspective()对结果的影响很大,那么就很有必要了解该函数。在NeHe等人的教程中,该函数使用其各个参数设置为如下:

      gluPerspective( 45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0 );

      该函数是设置场景的透视效果的,所谓透视,可以简单的理解为越远处的物体看起来越小。所以这里的参数1,2,3表示的是透视是按照基于窗口宽度和高度的45度视角来计算的,参数0.1和参数100是我们场景中所能绘制深度的起点和终点值。

     

         光照

      Opengl中使用光照时,首先定义了3个跟光滑有关的数组:

      GLfloat lightAmbient[4] = { 0.5, 0.5, 0.5, 1.0 };

      GLfloat lightDiffuse[4] = { 1.0, 1.0, 1.0, 1.0 };

      GLfloat lightPosition[4] = { 0.0, 0.0, 2.0, 1.0 };

      LightAmbient数组用来设置环境光的参数,前面3个参数表示光的rgb分量为0.5,后面那个是alpha通道,alph为1表示不透明,所谓环境光就是指物体所在的环境中的光照,一般比较均匀,四面八方都有;lightDiffuse数组用来设置漫射光的参数,这里讲漫射光强度设置为最大,漫射光是指的特定位置发出的光;lightPosition就是该光源的位置。

     

      上面数组只是这些光源的参数,如果我们要设置光源,则应该使用下面的代码:

      /*opengl中支持8个光源,即GL_LIGHT0~GL_LIGHT7*/

      glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);//指定光源1的环境光参数

      glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);//指定光源1的漫射光参数

      glLightfv(GL_LIGHT1, GL_POSITION, light_position);//指定光源1的位置

      glEnable(GL_LIGHT1);//允许光源1的使用

      glEnable(GL_LIGHTING);//我们还需要启动总光源开关

     

      然后在对空间物体进行贴图时,需要指定物体表面的法向量,使用下面的函数

      glNormal3f(x, y, z);

      该函数指定是法线的方向为向量(x, y, z)方向,在使用光源时,且对空间物体进行纹理映射时,每个面都需要指定其法线的方向,否则会出现各种意外的结果。

         在使用光照时,在前面课程基础上注意上面几点就Ok了。

     

      色彩融合

      色彩融合简单的说就是将2幅图片按照一定的透明度比例进行叠加(每个像素点都进行叠加),其公式如下所示:

      (Rs As + Rd (1 - As), Gs As + Gd (1 - As), Bs As + Bd (1 - As), As As + Ad (1 - As))

      其中Rs,Gs,Bs,As分别表示源目标图像的r,g,b,alpha分量值,Rd,Gd,Bd,Ad分别表示目标图像的r,g,b,alpha分量值。如果As等于0.5,则这个公式会生成透明/半透明的效果。

      上面的公式表示各颜色通道的alpha相同,且源像素因子和目标像素因子之和为1.一般情况下,源像素因子和目标像素因子采用的是:

      void WINAPI glBlendFunc(GLenum sfactor, GLenum dfactor);

      参数1为源像素因子的取值,我在程序中用的GL_SRC_ALPHA表示采用alpha通道的取值;参数2表示目标像素因子的取值,程序中用的是GL_ONE,表示目标像素因子取值1.0.具体该函数的用法可以参考网友的这篇文章:http://www.cnblogs.com/yujunyong/archive/2011/04/13/2015467.html

             

     

      实验说明

      这次实验是将一个木箱纹理贴到一个立方体上,然后我们在空间屏幕正外方设置了一个光源,可以用键盘的L键来控制该光源的开启和关闭;用F键来旋转程序中用到的3种滤波方式,按下F键后,依次切换该3种方式;用PageUp键来使物体离观察者越来越远,相反,用PageDown来使物体离我们越来越近;使用向上光标键加快物体旋转的速度,向下光标键减小物体旋转的速度。

      开发环境:windows+QtCreator2.5.1+Qt4.8.2

     

      实验结果:

      关闭灯光时效果:

      

      开启灯光时效果:

      

      三种纹理对比效果,纹理1:

      

      纹理2:

      

      纹理3:

      

      将物体远离观察者效果:

      

      

      开灯后无色彩融合效果:

      

      开灯后有色彩融合效果:

      

     

      实验主要部分代码及注释(附录有工程code下载链接地址):

    #include "glwidget.h"
    #include "ui_glwidget.h"
    
    #include <QtGui>
    #include <QtCore>
    #include <QtOpenGL>
    #include <glut>
    
    /*c++中可以在类的外部定义变量*/
    GLfloat light_ambient[4]={0.5, 0.5, 0.5, 1.0};
    GLfloat light_diffuse[4]={1.0, 1.0, 1.0, 1.0};
    GLfloat light_position[4]={0.0, 0.0, 2.0, 0.0};
    
    GLWidget::GLWidget(QGLWidget *parent) :
        QGLWidget(parent),
        ui(new Ui::GLWidget)
    {
      //  setCaption("The Opengl for Qt Framework");
        ui->setupUi(this);
        fullscreen = false;
        rotate_angle = 0.0;
        zoom = -5.0;
        rotate_speed = 3.0;
        filter = 0;
        light = false;
        blend = false;
    }
    
    //这是对虚函数,这里是重写该函数
    void GLWidget::initializeGL()
    {
        setGeometry(300, 150, 500, 500);//设置窗口初始位置和大小
        loadTextures();
        glEnable(GL_TEXTURE_2D);//允许采用2D纹理技术
        glShadeModel(GL_SMOOTH);//设置阴影平滑模式
        glClearColor(0.0, 0.0, 0.0, 0);//改变窗口的背景颜色,不过我这里貌似设置后并没有什么效果
        glClearDepth(1.0);//设置深度缓存
        glEnable(GL_DEPTH_TEST);//允许深度测试
        glDepthFunc(GL_LEQUAL);//设置深度测试类型
        glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);//进行透视校正
    
        /*opengl中支持8个光源,即GL_LIGHT0~GL_LIGHT7*/
        glLightfv(GL_LIGHT1, GL_AMBIENT, light_ambient);//指定光源1的环境光参数
        glLightfv(GL_LIGHT1, GL_DIFFUSE, light_diffuse);//指定光源1的漫射光参数
        glLightfv(GL_LIGHT1, GL_POSITION, light_position);//指定光源1的位置
        glEnable(GL_LIGHT1);//允许光源1的使用
      //  glEnable(GL_LIGHTING);//我们还需要启动总光源开关,默认的时候不开,后面的L键来控制开启和关闭
    
        glColor4f(1.0, 1.0, 1.0, 0.5);//后面的步骤都是以全亮绘制物体,并且50%的透明度
        glBlendFunc(GL_SRC_ALPHA, GL_ONE);
    }
    
    void GLWidget::paintGL()
    {
        //glClear()函数在这里就是对initializeGL()函数中设置的颜色和缓存深度等起作用
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        /*下面开始画立方体,并对其进行纹理映射*/
        glLoadIdentity();
        glTranslatef(0.0, 0.0, zoom);
        glRotatef(rotate_angle, -0.4f, 0.4f, -1.0f);
        glBindTexture(GL_TEXTURE_2D, texture[filter]);//这句代码一定要,因为在initializeGL()函数中已绑定一个固定的纹理目标了
        glBegin(GL_QUADS);
        //上顶面
        glNormal3f(0.0, 1.0, 0.0);//该函数指定是法线的方向为向量(x, y, z)方向,在使用光源时,且对空间物体进行纹理映射时,
                                  //每个面都需要指定其法线的方向,否则会出现各种意外的结果。
        glTexCoord2f(0.0, 1.0);//将2D的纹理坐标映射到3D的空间物体表面上
        glVertex3f(-1.0f, 1.0f, -1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0f, 1.0f, 1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0f, 1.0f, 1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0f, 1.0f, -1.0f);
        //下顶面
        glNormal3f(0.0, -1.0, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0f, -1.0f, -1.0f);
        //正前面
        glNormal3f(0.0, 0.0, 1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0f, 1.0f, 1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0f, 1.0f, 1.0f);
        //右侧面
        glNormal3f(1.0, 0.0, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(1.0f, 1.0f, 1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(1.0f, 1.0f, -1.0f);
        //背后面
        glNormal3f(0.0, 0.0, -1.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0f, 1.0f, -1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(1.0f, 1.0f, -1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        //左侧面
        glNormal3f(-1.0, 0.0, 0.0);
        glTexCoord2f(0.0, 1.0);
        glVertex3f(-1.0f, 1.0f, -1.0f);
        glTexCoord2f(0.0, 0.0);
        glVertex3f(-1.0f, -1.0f, -1.0f);
        glTexCoord2f(1.0, 0.0);
        glVertex3f(-1.0f, -1.0f, 1.0f);
        glTexCoord2f(1.0, 1.0);
        glVertex3f(-1.0f, 1.0f, 1.0f);
        glEnd();
        rotate_angle += rotate_speed;
    }
    
    //该程序是设置opengl场景透视图,程序中至少被执行一次(程序启动时).
    void GLWidget::resizeGL(int width, int height)
    {
        if(0 == height)
            height = 1;//防止一条边为0
        glViewport(0, 0, (GLint)width, (GLint)height);//重置当前视口,本身不是重置窗口的,只不过是这里被Qt给封装好了
        glMatrixMode(GL_PROJECTION);//选择投影矩阵
        glLoadIdentity();//重置选择好的投影矩阵
        gluPerspective(45.0, (GLfloat)width/(GLfloat)height, 0.1, 100.0);//建立透视投影矩阵
        glMatrixMode(GL_MODELVIEW);//以下2句和上面出现的解释一样
        glLoadIdentity();
    
    }
    void GLWidget::keyPressEvent(QKeyEvent *e)
    {
        switch(e->key())
        {
            /*L键位开启光照的开关*/
            case Qt::Key_L:
                light = !light;
                if(!light)
                    glDisable(GL_LIGHTING);
                else
                    glEnable(GL_LIGHTING);
                updateGL();
                break;
            /*B键位选择是否采用色彩融合*/
            case Qt::Key_B:
                blend = !blend;
                if(blend)
                    {
                        glEnable(GL_BLEND);
                        glDisable(GL_DEPTH_TEST);
                    }
                else
                    {
                        glDisable(GL_BLEND);
                        glEnable(GL_DEPTH_TEST);
                    }
                updateGL();
                break;
            /*F键位选择纹理滤波的方式*/
            case Qt::Key_F:
                filter += 1;
                if(filter > 2)
                    filter = 0;
                updateGL();
                break;
            /*PageUp键为将木箱移到屏幕内部方向*/
            case Qt::Key_PageUp:
                zoom -= 0.2;
                updateGL();
                break;
            /*PageDown键为将木箱移到屏幕外部方向*/
            case Qt::Key_PageDown:
                zoom += 0.2;
                updateGL();
                break;
            /*Up键为加快立方体旋转的速度*/
            case Qt::Key_Up:
                rotate_speed += 1.0;
                updateGL();
                break;
            /*Down键为减慢立方体旋转的速度*/
            case Qt::Key_Down:
                rotate_speed -= 1.0;
                updateGL();
                break;
            /*F1键为全屏和普通屏显示切换键*/
            case Qt::Key_F1:
                fullscreen = !fullscreen;
                if(fullscreen)
                    showFullScreen();
                else
                {
                    setGeometry(300, 150, 500, 500);
                    showNormal();
                }
                updateGL();
                break;
            /*Ese为退出程序键*/
            case Qt::Key_Escape:
                close();
        }
    }
    
    /*装载纹理*/
    void GLWidget::loadTextures()
    {
        QImage tex, buf;
        if(!buf.load("../opengl_qt_nehe_07/cat.jpg"))
       // if(!buf.load("../opengl_qt_nehe_07/crate.bmp"))
        {
            qWarning("Cannot open the image...");
            QImage dummy(128, 128, QImage::Format_RGB32);//当没找到所需打开的图片时,创建一副128*128大小,深度为32位的位图
            dummy.fill(Qt::green);
            buf = dummy;
        }
        tex = convertToGLFormat(buf);//将Qt图片的格式buf转换成opengl的图片格式tex
        glGenTextures(3, &texture[0]);//开辟3个纹理内存,索引指向texture[0]
    
        /*建立第一个纹理*/
        glBindTexture(GL_TEXTURE_2D, texture[0]);//将创建的纹理内存指向的内容绑定到纹理对象GL_TEXTURE_2D上,经过这句代码后,以后对
                                                //GL_TEXTURE_2D的操作的任何操作都同时对应与它所绑定的纹理对象
        glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());//开始真正创建纹理数据
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//当所显示的纹理比加载进来的纹理小时,采用GL_NEAREST的方法来处理
                                                                          //GL_NEAREST方式速度非常快,因为它不是真正的滤波,所以占用内存非常
                                                                          // 小,速度就快了
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//当所显示的纹理比加载进来的纹理大时,采用GL_NEAREST的方法来处理
    
        /*建立第二个纹理*/
        glBindTexture(GL_TEXTURE_2D, texture[1]);
        glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);//当所显示的纹理比加载进来的纹理小时,采用GL_LINEAR的方法来处理
                                                                        //GL_LINEAR方式速度非常稍微慢些,但它做到了线性滤波
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    
        /*建立第三个纹理*/
        glBindTexture(GL_TEXTURE_2D, texture[2]);
        gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, tex.width(), tex.height(), GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());//该函数对所加载的
                                                                                        //纹理像素没有要求是2的n次方,可以是任意的像素
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);//mipmap方式是当物体很远时,也能保留很好的细节,所以
                                                                       //它的运算量很大,速度很慢,这里GL_LINEAR_MIPMAP_NEAREST是混合使用
    }
    
    GLWidget::~GLWidget()
    {
        delete ui;
    }

     

     

      总结:

      本次实验对比了3中纹理滤波方式,学会了简单的光照的使用方法,并用键盘控制物体的某些特性。

     

      参考资料:

      http://nehe.gamedev.net/ 

      http://www.owlei.com/DancingWind/

      http://www.qiliang.net/old/nehe_qt/

      附录:

      实验工程code下载

     

     

     

     

     

     

  • 相关阅读:
    Class类
    HTML表单格式化
    HTML表单组件
    html常用标签
    Html概述
    Myeclipse2016安装Aptana
    长元音
    对比法记音标
    Java基础八--构造函数
    WPS2012交叉引用技巧,word比wps这点强更新參考文献
  • 原文地址:https://www.cnblogs.com/tornadomeet/p/2655723.html
Copyright © 2020-2023  润新知