在Learn_OpenGL
中,加载纹理使用了std_image库,这个库,额~~~~,挺(nan)好(yi)用(yan)的(biao)。
在Qt中,加载纹理可以使用内置库:
QOpenGLTexture
来进行纹理操作。QImage
来进行纹理载入。QImage image=QImage(path).mirrored(false,true);//加载单一纹理代码
或
QImage data(directory.filePath(str.C_Str()));//加载模型纹理代码
QImage::mirrored
: Returns a mirror of the image, mirrored in the horizontal and/or the vertical direction depending on whetherhorizontal
andvertical
are set to true or false
QOpenGLTexture *texture = new QOpenGLTexture(image, QOpenGLTexture::GenerateMipMaps);
//直接生成绑定一个2d纹理, 并生成多级纹理MipMaps
与QOpenGLTexture *texture = new QOpenGLTexture(image);
等同
QOpenGLTexture::QOpenGLTexture(const QImage &image, QOpenGLTexture::MipMapGeneration genMipMaps = GenerateMipMaps)
——————————————————————————————————
Creates a QOpenGLTexture object that can later be bound to the 2D texture target and contains the pixel data contained in image. If you wish to have a chain of mipmaps generated then set genMipMaps to true (this is the default).
或
QOpenGLTexture * texture = new Texture;
texture.setData(data);//data为Qimage类
void
QOpenGLTexture::setData
(constQImage
&image,QOpenGLTexture::MipMapGeneration
genMipMaps = GenerateMipMaps)
————————————————————————————————————
This overload of setData() will allocate storage for you. The pixel data is contained in image. Mipmaps are generated by default. Set genMipMaps toDontGenerateMipMaps
to turn off mipmap generation.
// set the texture wrapping parameters
texture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat);
// 等于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
texture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
// set texture filtering parameters
texture->setMinificationFilter(QOpenGLTexture::Linear);
//等价于glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
texture->setMagnificationFilter(QOpenGLTexture::Linear);
texture.setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
glActiveTexture(GL_TEXTURE0);
texture1->bind();
glActiveTexture(GL_TEXTURE1);
texture2->bind();
shaderProgram.setUniformValue("texture1",0);
shaderProgram.setUniformValue("texture2",1);
别忘啦delete texture
,因为texture
是new
出来的。
QOpenGLTexture *pTexture=new QOpenGLTexture(QOpenGLTexture::TargetCubeMap);
默认读取的纹理为32位RGB,不符合CubeMap的要求,必须转为24位RGB。
QImage posX = QImage(paths[0]).convertToFormat(QImage::Format_RGB888); //Right
QImage negX = QImage(paths[1]).convertToFormat(QImage::Format_RGB888); //Left
QImage posY = QImage(paths[2]).convertToFormat(QImage::Format_RGB888); //Top
QImage negY = QImage(paths[3]).convertToFormat(QImage::Format_RGB888); //Bottom
QImage posZ = QImage(paths[4]).convertToFormat(QImage::Format_RGB888); //Front
QImage negZ = QImage(paths[5]).convertToFormat(QImage::Format_RGB888); //Back
仔细分析QOpenGLTexture的帮助文档,文档清楚的说明除了void setData(const QImage &image, MipMapGeneration genMipMaps = GenerateMipMaps)
函数在生成2D纹理时会默认自动分配纹理物理内存,其余的setData()重载函数都必须手动分配内存空间。
所以,如果我们需要生成纹理目标为CubeMap的纹理,必须提前手动分配内存空间。
在手动分配内存空间之前,必须确定纹理的尺寸,类型等样式。
因此,对于 QOpenGLTexture
,您必须在 allocateStorage
之前设置 Size
, Format
. 最后一步是 setData
.
pTexture->setSize(posX.width(),posX.height(),posX.depth());
posX.depth()
图像深度是用于存储单个像素的位数,也称为每像素位(bpp)。
支撑深度为1、8、16、24、32、64。
要求立方体贴图的六张纹理尺寸必须相同。
将纹理格式设置为RGB格式
pTexture->setFormat(QOpenGLTexture::RGBFormat);
为这个纹理对象分配服务器端存储,考虑到格式、尺寸、mipmap级别、数组层和立方体贴图面。
一旦分配了存储,就不可能再更改这些属性。
如果受支持,QOpenGLTexture将使用不可变的纹理存储。
一旦为纹理分配了存储,那么像素数据就可以通过setData()重载上传
allocateStorage(pixelFormat,pixelType)
//设置像素格式和像素类型
pTexture->allocateStorage(QOpenGLTexture::RGB,QOpenGLTexture::UInt8);
void QOpenGLTexture::setData( int mipLevel,
int layer,
QOpenGLTexture::CubeMapFace cubeFace,
QOpenGLTexture::PixelFormat sourceFormat,
QOpenGLTexture::PixelType sourceType,
const void *data,
const QOpenGLPixelTransferOptions *const options = nullptr)
上传纹理对象mipLevel
、数组层(layer
)和立方体面的像素数据。上传像素数据前必须分配存储空间。setData()的一些重载将设置适当的维度、mipmap级别和数组层,然后如果它们有足够的信息为您分配存储空间。这将在函数文档中注明。
data所指向的像素数据的结构由sourceFormat和sourceType指定。像素数据上传可以通过选项进行控制。
如果使用了compressed format(),那么应该使用setCompressedData()
setData(mipLevel, 数组层, 立方体贴图位置, 纹理格式, 纹理数据类型, (const void*)数据);
如下:
pTexture->setData(0, 0, QOpenGLTexture::CubeMapPositiveX, QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void*)posX.bits());
之后设置纹理的过滤方式,环绕方式。
pTexture->setMinificationFilter(QOpenGLTexture::Linear);
pTexture->setMagnificationFilter(QOpenGLTexture::Linear);
pTexture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge);
pTexture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
这样就成功得到了立方体贴图的QOpenGLTexture
类数据。
GLfloat skyboxVertices[] = {
// Positions
-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,
-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,
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,
-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,
1.0f, -1.0f, -1.0f,
1.0f, -1.0f, -1.0f,
-1.0f, -1.0f, 1.0f,
1.0f, -1.0f, 1.0f
};
QOpenGLVertexArrayObject::Binder skyboxVAOBind(&skyboxVAO);
//创建并绑定,资源获取即初始化,离开作用域自动析构
skyboxVBO.create();
skyboxVBO.bind();
skyboxVBO.allocate(skyboxVertices,sizeof(skyboxVertices));
glEnableVertexAttribArray(0);
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,3*sizeof(float), (void*)0);
skyboxVBO.release();
#version 450 core
layout (location = 0) in vec3 aPos;
out vec3 TexCoords;
uniform mat4 projection;
uniform mat4 view;
void main()
{
TexCoords = aPos;
vec4 pos = projection * view * vec4(aPos, 1.0);
gl_Position = pos.xyww;
}
注意:这里gl_Position = pos.xyww;
将z分量始终设为最大值1,即永远在所有物体之后。
#version 450 core
out vec4 FragColor;
in vec3 TexCoords;
uniform samplerCube skybox;
void main()
{
FragColor = texture(skybox, TexCoords);
}
注意这里是samplerCube
skyboxShader.addShaderFromSourceFile(QOpenGLShader::Vertex, ":/skybox.vert");
skyboxShader.addShaderFromSourceFile(QOpenGLShader::Fragment, ":/skybox.frag");
skyboxShader.link();
skyboxShader.bind();
QMatrix4x4 view=camera->getViewMatrix();
view.setColumn(3,QVector4D(0,0,0,1.0f));//将第三列(最后一列)设置为(0,0,0,1)
//作用,使摄像机位移不会改变立方体位置
skyboxShader.setUniformValue("view",view);
skyboxShader.setUniformValue("projection",projection);
首先设置深度通过方式为小于等于,因为GL_DEPTH_BUFFER_BIT
后的深度都为1,为了让天空图替换背景,需要让当深度为1时替换背景颜色。
glDepthFunc(GL_LEQUAL);
QOpenGLVertexArrayObject::Binder skyboxVAOBind(&skyboxVAO);
glActiveTexture(GL_TEXTURE0);
skyboxTexture->bind();
skyboxShader.setUniformValue("skybox",0);
glDrawArrays(GL_TRIANGLES, 0, 36);