一、提要
今天在网上看到一个消息,芬兰IT服务公司Digia Oyj称,公司将会出资400万欧元购买诺基亚旗下QT软件业务
芬兰IT服务公司Digia Oyj称,公司将会出资400万欧元购买诺基亚旗下QT软件业务。
据我所了解,Qt算是一个非常优秀的团队,希望Qt离开Nokia之后能够发展得更好吧。
经过了上一篇的教程,我们对3D世界已经有了初步的认识,今天我们要学习的内容是:光照和纹理滤波。
二、光照和纹理滤波
OpenGL有四种类型的光照:环境光,漫射光,镜面光,发射光。这四种成分都可以单独进行计算,并且可以叠加。
。环境光来自于四面八方。所有场景中的对象都处于环境光的照射中。漫射光由特定的光源产生,并在您的场景中的对象表面上产生反射。处于漫射光直接照射下的任何对象表面都变得很亮,
而几乎未被照射到的区域就显得要暗一些。镜面光来自一个特定的方向,类似一组平行光。
发射光用于模拟发光物体,不受任何光源影响。
关于光的颜色和材料的颜色有必要来说明一下。
对于光线而言,颜色成分的数量对应于每种颜色的完全
强度百分比。如果一种光线颜色的R,G和B的值都为1.0,
这种光线就是可以实现的最量的白色。如果都是0.5,那么这种光线还是白色,但强度只有一般,因此看山区像是灰色。
如果R=G=1并且B=0(完全的红和绿,没有蓝),这种光线看上去就是黄色。
对于材料而言,它的RGB值和它对这些颜色的翻色比例相对应。因此,如果一种材料的R=1,
G=0.5、B=0,那么它将反射所有的入射光,但是不反射任何入射蓝光。
这篇教程我们暂讨论环境光和漫射光。
首先在nehewidget.h中加入几个变量。
//纹理存储数组
GLuint texture[3];
//场景深入屏幕的距离
GLfloat zoom;
//立方体在X轴和Y轴上旋转的速度
GLfloat xSpeed, ySpeed;
//表明使用哪个纹理
GLuint filter;
//是否开启灯光
bool isLighting;
然后在neheWidget.cpp中加入光源的信息数组:
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 };
创建光源的过程和颜色的创建完全一致。前三个参数分别是RGB三色分量,最后一个是alpha通道参数。
因此,第一行有关lightAmbient的代码使我们得到的是半亮(0.5)的白色环境光。如果没有环境光,未被漫射光照到的地方会变得十分黑暗。
第二行有关lightDiffuse的代码使我们生成最亮的漫射光。所有的参数值都取成最大值1.0。它将照在我们木板箱的前面,看起来挺好。
第三行有关lightPosition的代码使我们保存光源的位置。前三个参数和glTranslate中的一样。依次分别是XYZ轴上的位移。
在构造函数中对变量进行初始化:
zoom = -5.0;
xSpeed = ySpeed = 0.0;
filter = 0;
isLighting = false;
装载纹理,这个在上一篇教程中已经实现了,不过这次要换成木箱的纹理。
void NeHeWidget::loadGLTextures()
{
QImage tex, buf;
if ( !buf.load( ":/data/Crate.bmp" ) )
{
//如果载入不成功,自动生成一个128*128的32位色的绿色图片。
qWarning("Could not read image file!");
QImage dummy( 128, 128,QImage::Format_RGB32 );
dummy.fill( Qt::green );
buf = dummy;
}
//转换成纹理类型
tex = QGLWidget::convertToGLFormat( buf );
//创建纹理
glGenTextures( 3, &texture[0] );
//使用来自位图数据生成的典型纹理,将纹理名字texture[0]绑定到纹理目标上
glBindTexture( GL_TEXTURE_2D, texture[0] );
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 );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST );
//绑定第二个纹理
glBindTexture( GL_TEXTURE_2D, texture[1] );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexImage2D( GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
//绑定第三个纹理
glBindTexture( GL_TEXTURE_2D, texture[2] );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST );
gluBuild2DMipmaps( GL_TEXTURE_2D, GL_RGB, tex.width(), tex.height(), GL_RGBA, GL_UNSIGNED_BYTE, tex.bits() );
}
装载好bmp图片之后,我们对纹理进行了三次绑定,采用了不同的滤波方式。
上次我们采用的是线性滤波方式,这需要机器有相当高的处理能力,但它们看起来很不错。这一课中,我们接着要创建的第一种纹理使用GL_NEAREST方式。从原理上讲,这种方式没有真正进行滤波。它只占用很小的处理能力,看起来也很差。唯一的好处是这样我们的工程在很快和很慢的机器上都可以正常运行。您会注意到我们在MIN和MAG时都采用了GL_NEAREST,你可以混合使用GL_NEAREST和GL_LINEAR。纹理看起来效果会好些,但我们更关心速度,所以全采用低质量贴图。MIN_FILTER在图像绘制时小于贴图的原始尺寸时采用。MAG_FILTER在图像绘制时大于贴图的原始尺寸时采用。
这其实在游戏编程中有用到,比如在大的场景中,有很多很多的模型,但远处的模型并不是很重要,所以用GL_NEAREST方式就可以节约非常多的资源。
最后一个纹理用到了新的纹理创建方法:Mipmapping!您可能会注意到当图像在屏幕上变得很小的时候,很多细节将会丢失。刚才还很不错的图案变得很难看。当您告诉OpenGL创建一个 mipmapped的纹理后,OpenGL将尝试创建不同尺寸的高质量纹理。当您向屏幕绘制一个mipmapped纹理的时候,OpenGL将选择它已经创建的外观最佳的纹理(带有更多细节)来绘制,而不仅仅是缩放原先的图像(这将导致细节丢失)。
gluBuild2DMipmaps可以使用任意的位图来创建纹理。OpenGL将自动将它缩放到正常的大小。
修改paintGL().
void NeHeWidget::paintGL()
{
// 清除屏幕和深度缓存
glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
glLoadIdentity();
//移到屏幕的左半部分,并且将视图推入屏幕背后足够的距离以便我们可以看见全部的场景
glTranslatef(0.0f,0.0f,zoom);
glRotatef( xRot, 1.0, 0.0, 0.0 );
glRotatef( yRot, 0.0, 1.0, 0.0 );
//选择使用的纹理
glBindTexture( GL_TEXTURE_2D, texture[filter] );
glBegin( GL_QUADS );
glNormal3f( 0.0, 0.0, 1.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, 1.0 );
glNormal3f( 0.0, 0.0, -1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0, 1.0, -1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( 1.0, 1.0, -1.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( 1.0, -1.0, -1.0 );
glNormal3f( 0.0, 1.0, 0.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, -1.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, 1.0, 1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, -1.0 );
glNormal3f( 0.0, -1.0, 0.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0, -1.0, -1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( 1.0, -1.0, -1.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 );
glNormal3f( 1.0, 0.0, 0.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( 1.0, -1.0, -1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( 1.0, 1.0, -1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( 1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( 1.0, -1.0, 1.0 );
glNormal3f( -1.0, 0.0, 0.0 );
glTexCoord2f( 0.0, 0.0 ); glVertex3f( -1.0, -1.0, -1.0 );
glTexCoord2f( 1.0, 0.0 ); glVertex3f( -1.0, -1.0, 1.0 );
glTexCoord2f( 1.0, 1.0 ); glVertex3f( -1.0, 1.0, 1.0 );
glTexCoord2f( 0.0, 1.0 ); glVertex3f( -1.0, 1.0, -1.0 );
glEnd();
xRot += 0.3;
yRot += 0.2;
}
代码和之前的类似,就不解释了。
接着在initializeGL()中添加关于光源的代码。
glLightfv( GL_LIGHT1, GL_AMBIENT, lightAmbient );
glLightfv( GL_LIGHT1, GL_DIFFUSE, lightDiffuse );
glLightfv( GL_LIGHT1, GL_POSITION, lightPosition );
glEnable( GL_LIGHT1 );
glEnable(GL_LIGHTING);
第一行设置环境光的发光量,光源GL_LIGHT1开始发光。这一课的开始处我们我们将环境光的发光量存放在lightAmbient数组中。现在我们就使用此数组(半亮度环境光)。
接下来我们设置漫射光的发光量。它存放在lightDiffuse数组中(全亮度白光)。
然后设置光源的位置。位置存放在lightPosition 数组中(正好位于木箱前面的中心,X-0.0,Y-0.0,Z方向移向观察者2个单位,位于屏幕外面)。
最后,我们启用一号光源。我们还没有启用GL_LIGHTING,所以您看不见任何光线。记住:只对光源进行设置、定位、甚至启用,光源都不会工作。除非我们启用GL_LIGHTING。
接下来我们要用键盘上的L键来控制灯光的亮暗,选择纹理,控制物理距离。思路是在nehewidget类
加入关于控制的共有接口,然后manwindow接收键盘事件,再通过接口来控制灯光。
首先在nehewidget.h中添加声明:
public:
//返回灯光是否开启的状态
bool isLighting();
//开启灯光
void turnOnLight();
//关闭灯光
void turnOffLight();
然后在nehewidget.cpp里实现方法:
bool NeHeWidget::isLighting()
{
return this->isLight;
}
void NeHeWidget::turnOffLight()
{
this->isLight=false;
glDisable(GL_LIGHT1 );
qWarning("Off");
}
void NeHeWidget::turnOnLight()
{
this->isLight=true;
glEnable( GL_LIGHT1 );
qWarning("ON");
}
在manwindow.cpp的按键事件函数里,添加以下代码:
case Qt::Key_L:
if ( neheWidget->isLighting()) neheWidget->turnOffLight();
else neheWidget->turnOnLight();
neheWidget->updateGL();
qWarning("LLLL");
break;
case Qt::Key_F:
neheWidget->changeFilter();
break;
case Qt::Key_PageUp:
neheWidget->zoomOut();
break;
case Qt::Key_PageDown:
neheWidget->zoomIn();
break;
编译运行,就可以看到下面的效果:
这是灯打开的状态
灯关闭的状态。
下面是不同纹理滤波
四.参考资料
1.《OpenGL Reference Manual》,OpenGL参考手册
2.《OpenGL编程指南》(《OpenGL Programming Guide》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis著,徐波译,机械工业出版社
3. 《win32 OpenGL编程 》 一个大牛的博客 http://blog.csdn.net/vagrxie/article/category/628716/3
4. 《OpenGL函数思考 》 里面有很多OpenGL函数的通俗解释 http://blog.csdn.net/shuaihj
5. 《nehe的OpenGL教程》 比较通俗易懂的OpenGL教程 http://nehe.gamedev.net/
分享到:
相关推荐
这是Qt结合openGL的小程序,功能比较简单,仅供新手学习。本程序开发环境:Qt Creator4.0.2+Qt5.7 注:如果程序运行出错,那是因为缺少glut库。可以参考下面链接解决:...
7:纹理滤波,光源和 键盘控制 8:融合 9:在三维空间中移动位图 10:载入一个三维世界并在其中移动 11:旗的效果(波动纹理) 12:显示列表 13:位图字体 14:轮廓字体 15:使用纹理映射的轮廓字体 16:看起来很棒...
qt打包提示确实Qt5OpenGL.dll,可以用这个,或者下载威龙触摸屏程序安装后也可以得到
本人原创,浅显易懂,QT+OPENGL多线程,采用2D纹理贴图的方式,多线程进行纹理拷贝和渲染。
Qt中OpenGL实现立方体的纹理贴图和旋转
Qt+opengl加载各种类型的3D模型,.glb .obj 等;Qt+opengl加载各种类型的3D模型,.glb .obj 等;Qt+opengl加载各种类型的3D模型,.glb .obj 等;Qt+opengl加载各种类型的3D模型,.glb .obj 等;Qt+opengl加载各种...
运行其中的程序,可以理解OpenGL中光照部分的工作方式。 很可惜的是,UP主并电脑烂爆了,无法运行OpenGL2.0及以上规范,所以无法运用Shader制作更加漂亮的效果了。不过这个演示程序看起来也不赖,大家说是么。
OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿OpenGL+qt抗锯齿...
Linux下用QT开发OpenGL教程,浅显易懂!
一个学习qt opengl中的mipmap的示例,其中包含了和普通纹理的对比。通过对比加深对mipmap纹理的学习。
用QT的opengl显示图片,一个类的定义文件。
qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl实现帧缓冲qt+opengl...
实现了QT下OpenGL的光源照射编程,模拟现实中的光源实现三维图形的绘制,在Vs2017+QT插件平台下运行
Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl实现爆破物体Qt+opengl...
Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl实现分屏效果Qt+opengl...
基于Qt+OpenGL 实现的3D模型obj文件加载以及纹理贴图,未使用第三方库,根据obj文件的格式,逐行解析并读取,加载到顶点缓冲区中,适合学习OBJ模型加载的同学参考。
这是我的毕设题目中写的软件,基本功能是输入数据,就会在opengl框中绘制图像,用到了matlab,opengl,是基于qt开发的(vs2013中的qt插件),里面都是调用到的操作opengl的方法,也是比较新的,网上有很多老的方法...
基于QT 实现了大部分LearnOpenGL的例子,可以作为学习QT、OpenGL的参考
Qt+OpenGL实现三维地形显示,数字地图使用图片形式存储
初学者适用,qt4.8.4环境下,在指定ui界面显示opengl绘制内容,同时多纹理加载,把当前opengl画面保存为bmp图片。