一、前置知识
经过前面的学习,我们已经知道了立方体怎么创建了,接下来学习怎么给立方体贴图;
为了将纹理正确的映射到四边形上,您必须:
- 将纹理的
右上角映射到四边形的右上角; - 纹理的
左上角映射到四边形的左上角; - 纹理的
右下角映射到四边形的右下角; - 纹理的
左下角映射到四边形的左下角;
如果映射错误的话,图像显示时可能上下 颠倒,侧向一边或者什么都不是
glTexCoord2f(x, y):
- 第一个参数是X坐标;0.0 是纹理的左侧、0.5 是纹理的中点、 1.0 是纹理的右侧;
- 第二个参数是Y坐标;0.0 是纹理的底部、 0.5 是纹理的中点、1.0 是纹理的顶部;
二、效果展示、


三、完整代码
#ifndef GL_TEST_H
#define GL_TEST_H
#include <qgl.h> //因为QGLWidget类被包含在qgl.h头文件中
#include <glut.h> //使用glut库中的API
#include <QKeyEvent>
#include <QTimer>
//继承QGLWidget得到OPenGL窗口部件类
class GL_Test : public QGLWidget
{
public:
GL_Test(QWidget* parent = 0, bool fs = false);
~GL_Test();
protected:
/*************************************************************************************************
QGLWidget 类已经内置了对 OpenGL 的处理,就是通过对 initializeGL()、 paintGL()和 resizeGL()这个三个函数实现
*************************************************************************************************/
void initializeGL() override; //用来初始化OPenGL窗口,可以在里面设定一些有关选项
void paintGL() override; //用来绘制OPenGL的窗口,只要有更新发生,这个函数就会被调用
void resizeGL(int w, int h) override; //用来处理窗口大小变换这一事件,resizeGL()在处理完后会自动刷新屏幕
void keyPressEvent(QKeyEvent* e) override; //Qt键盘事件处理函数
void loadGLTextures(); //载入指定的图片并生成相应的纹理
protected:
bool fullscreen; //用来保存窗口是否处于全屏状态的变量
GLfloat xRot, yRot, zRot; //用来处理立方体的旋转
GLuint texture[1]; //用来存储纹理(长度为1的数组)
};
#endif // GL_TEST_H
#include "gl_test.h"
GL_Test::GL_Test(QWidget* parent, bool fs)
: QGLWidget(parent)
{
fullscreen = fs;
xRot = 0.0;
yRot = 0.0;
zRot = 0.0;
setGeometry(500,500,640,480); //设置窗口大小、位置
setWindowTitle("The first OpenGL Window"); //设置窗口标题
if(fullscreen) {
showFullScreen();
}
QTimer* timer = new QTimer(this);
connect(timer,&QTimer::timeout,[=]{
updateGL();
});
timer->start(50);
}
GL_Test::~GL_Test()
{
}
void GL_Test::initializeGL()
{
loadGLTextures(); //载入纹理
glEnable(GL_TEXTURE_2D); //启用纹理
glShadeModel(GL_SMOOTH); //启用smooth shading(阴影平滑)
glClearColor(0.0, 0.0, 0.0, 0.5); //清除屏幕时所用的颜色,rgba【0.0(最黑)~1.0(最亮)】
glClearDepth(1.0); //设置深度缓存
glEnable(GL_DEPTH_TEST); //启动深度测试
glDepthFunc(GL_LEQUAL); //所作深度测试的类型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); //真正精细的透视修正,告诉OPenGL我们希望进行最好的透视修正,这会十分轻微的影响性能,但使得透视图看起来好一点
}
void GL_Test::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
//-----------------------------------------
glLoadIdentity(); //重置当前的模型观察矩阵
glTranslatef(0.0, 0.0, -5.0);
glRotatef(xRot, 1.0, 0.0, 0.0); //绕X轴旋转xRot度
glRotatef(yRot, 0.0, 1.0, 0.0); //绕X轴旋转yRot度
glRotatef(zRot, 0.0, 0.0, 1.0); //绕X轴旋转zRot度
//选择我们使用的纹理
glBindTexture(GL_TEXTURE_2D, texture[0]);
//如果您在您的场景中使用多个纹理,您应该使用来 glBindTexture(GL_TEXTURE_2D, texture[所使用纹理对应的数字]) 选择要绑定的纹理;
//当您想改变纹理时,应该绑定新的纹理。并且您不能在glBegin()和glEnd()之间绑定纹理,必须在glBegin()之前或glEnd()之后绑定;
//绘制正方形开始
glBegin(GL_QUADS);
//前面
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 );
//后面
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 );
//顶面
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 );
//底面
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 );
//右面
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 );
//左面
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 += 2;
yRot += 3;
zRot += 4;
}
void GL_Test::resizeGL(int w, int h)
{
if(h == 0) { //防止h为0
h = 1;
}
glViewport(0, 0, (GLint)w, (GLint)h); //重置当前的视口(Viewport)
glMatrixMode(GL_PROJECTION); //选择投影矩阵
glLoadIdentity(); //重置投影矩阵
gluPerspective( 45.0, (GLfloat)w/(GLfloat)h, 0.1, 100.0 ); //建立透视投影矩阵
glMatrixMode(GL_MODELVIEW); //选择模型观察矩阵
glLoadIdentity(); //重置模型观察矩阵
}
void GL_Test::keyPressEvent(QKeyEvent* e)
{
switch (e->key()) {
case Qt::Key_Q: {
fullscreen = !fullscreen;
if(fullscreen) {
showFullScreen();
}else {
showNormal();
setGeometry(500,500,640,480);
}
updateGL();
break;
}//case Qt::Key_Q
case Qt::Key_Escape: {
close();
}//Qt::Key_Escape
}//switch (e->key())
}
void GL_Test::loadGLTextures()
{
QImage tex,buf;
if(!buf.load(":/Images/1591561503-gB5rD.jpg")) { //用QImage类载入纹理图片
QImage dummy(128,128,QImage::Format_RGB32); //如果载入不成功,生成一个128*128的32位色的绿色图片
dummy.fill(Qt::green);
buf = dummy;
}
tex = QGLWidget::convertToGLFormat(buf); //QGLWidget的静态函数,专门用来转换图片
glGenTextures(1,&texture[0]); //创建一个纹理
glBindTexture(GL_TEXTURE_2D,texture[0]); //使用来自位图数据生成的典型纹理
/*告诉OPenGL将纹理名字texture[0]绑定到纹理目标上;2D纹理只有高度(在Y轴上)和宽度(在X轴上)*/
//真正的创建纹理
//GL_TEXTURE_2D:告诉 OpenGL 此纹理是一个 2D 纹理;
//数字0:代表图像的详细程度;
//数字3:是数据的成分数;
//tex.width():是纹理的宽度
//tex.height():是纹理的高度;
//GL_RGBA:告诉 OpenGL 图像数据由红、绿、蓝三色数据以及 alpha 通道数据组成;
//GL_UNSIGNED_BYTE:意味着组成图像的数据是无符号字节类型的;
//tex.bits():告诉 OpenGL 纹理数据的来源;
glTexImage2D(GL_TEXTURE_2D, 0, 3, tex.width(), tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
//告诉 OpenGL 在显示图像时,当它比放大得原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小得比原始得纹理小(GL_TEXTURE_MIN_FILTER)时,
//OpenGL 采用的滤波方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//我们都采用 GL_LINEAR,这使得纹理从很远处到离屏幕很近时都平滑显示;
//使用 GL_LINEAR 需要 CPU 和显卡做更多的运算,如果您的机器很慢,您也许应该采用 GL_NEAREST;
//过滤的纹理在放大的时候,看起来斑驳的很,您也可以结合这两种滤波方式: 在近处时使用 GL_LINEAR,远处时 GL_NEAREST;
}
附、给立方体每个面渲染不同的纹理

1、创建多个纹理
void GL_Test::loadGLTextures()
{
QStringList Images;
Images << ":/Images/1591561503-gB5rD.jpg";
Images << ":/Images/AI.png";
Images << ":/Images/wallhaven-283q5m.png";
Images << ":/Images/wallhaven-q2oxx7.png";
Images << ":/Images/wallhaven-y85ojk.png";
Images << ":/Images/wallhaven-z8pm7y.png";
for(int i=0; i<6; ++i) {
QImage tex,buf;
if(!buf.load(Images.at(i))) { //用QImage类载入纹理图片
QImage dummy(128,128,QImage::Format_RGB32); //如果载入不成功,生成一个128*128的32位色的绿色图片
dummy.fill(Qt::green);
buf = dummy;
}
tex = QGLWidget::convertToGLFormat(buf); //QGLWidget的静态函数,专门用来转换图片
glGenTextures(1,&texture[i]); //创建1个纹理,存储在texture[i]中
//---------------------------------------------------------------------------------------
glBindTexture(GL_TEXTURE_2D,texture[i]); //使用来自位图数据生成的典型纹理
/*告诉OPenGL将纹理名字texture[0]绑定到纹理目标上;2D纹理只有高度(在Y轴上)和宽度(在X轴上)*/
//真正的创建纹理
//GL_TEXTURE_2D:告诉 OpenGL 此纹理是一个 2D 纹理;
//数字0:代表图像的详细程度;
//数字3:是数据的成分数;
//tex.width():是纹理的宽度
//tex.height():是纹理的高度;
//GL_RGBA:告诉 OpenGL 图像数据由红、绿、蓝三色数据以及 alpha 通道数据组成;
//GL_UNSIGNED_BYTE:意味着组成图像的数据是无符号字节类型的;
//tex.bits():告诉 OpenGL 纹理数据的来源;
glTexImage2D(GL_TEXTURE_2D,
0,
3,
tex.width(),
tex.height(),
0, GL_RGBA,
GL_UNSIGNED_BYTE,
tex.bits());
//告诉 OpenGL 在显示图像时,当它比放大得原始的纹理大(GL_TEXTURE_MAG_FILTER)或缩小得比原始得纹理小(GL_TEXTURE_MIN_FILTER)时,
//OpenGL 采用的滤波方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//我们都采用 GL_LINEAR,这使得纹理从很远处到离屏幕很近时都平滑显示;
//使 用 GL_LINEAR 需要 CPU 和显卡做更多的运算,如果您的机器很慢,您也许应该采用 GL_NEAREST;
//过滤的纹理在放大的时候,看起来斑驳的很,您也可以结合这两种滤波方式: 在近处时使用 GL_LINEAR,远处时 GL_NEAREST;
}
}
2、不同面使用不同纹理
void GL_Test::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕和深度缓存
glLoadIdentity(); //重置当前的模型观察矩阵
glTranslatef(0.0, 0.0, zoom);
glRotatef(xRot, 1.0, 0.0, 0.0); //绕X轴旋转xRot度
glRotatef(yRot, 0.0, 1.0, 0.0); //绕X轴旋转yRot度
// //选择我们使用的纹理
// glBindTexture(GL_TEXTURE_2D, texture[filter]);
// //如果您在您的场景中使用多个纹理,您应该使用来 glBindTexture(GL_TEXTURE_2D, texture[所使用纹理对应的数字]) 选择要绑定的纹理;
// //当您想改变纹理时,应该绑定新的纹理。并且您不能在glBegin()和glEnd()之间绑定纹理,必须在glBegin()之前或glEnd()之后绑定;
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[0]);
//绘制正方形开始
glBegin(GL_QUADS);
//前面
glNormal3f(0.0, 0.0, 1.0); //x、y、z,垂直于面的法线向量
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();
//绘制正方形结束
//-----------------------------------------
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[1]);
//绘制正方形开始
glBegin(GL_QUADS);
//后面
glNormal3f(0.0, 0.0, -1.0); //x、y、z,垂直于面的法线向量
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 );
glEnd();
//绘制正方形结束
//-----------------------------------------
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[2]);
//绘制正方形开始
glBegin(GL_QUADS);
//顶面
glNormal3f(0.0, 1.0, 0.0); //x、y、z,垂直于面的法线向量
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 );
glEnd();
//绘制正方形结束
//-----------------------------------------
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[3]);
//绘制正方形开始
glBegin(GL_QUADS);
//底面
glNormal3f(0.0, -1.0, 0.0); //x、y、z,垂直于面的法线向量
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 );
glEnd();
//绘制正方形结束
//-----------------------------------------
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[4]);
//绘制正方形开始
glBegin(GL_QUADS);
//右面
glNormal3f(1.0, 0.0, 0.0); //x、y、z,垂直于面的法线向量
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 );
glEnd();
//绘制正方形结束
//-----------------------------------------
//-----------------------------------------
glBindTexture(GL_TEXTURE_2D, texture[5]);
//绘制正方形开始
glBegin(GL_QUADS);
//左面
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 += xSpeed;
yRot += ySpeed;
}
本文介绍了如何在OpenGL中正确映射纹理到立方体的各个面,演示了如何为每个面加载不同纹理,并通过实例展示了如何在绘制立方体时切换纹理。前置知识包括纹理坐标系统的理解,以及关键的glTexCoord2f函数用法。
1214

被折叠的 条评论
为什么被折叠?



