QT之OpenGL立方体贴图
1. 概述
立方体贴图(Cube Map)
是将多个纹理组合起来映射到一张纹理上的一种纹理类型。立方体贴图就是包含6个2D纹理的纹理,它有一个很方便的特性,就是可以通过一个方向向量来对纹理进行索引/采样。大小为1x1x1
的立方体如下所示:
2. 绘制天空盒
利用立方体绘制天空盒的步骤主要如下:
- 准备6个2D纹理
- 设置立方体贴图纹理
// 创建立方体纹理 m_cubeMap=new QOpenGLTexture(QOpenGLTexture::TargetCubeMap); // 加载纹理图片 QImage _right = QImage(":/images/images/skybox/right.jpg").convertToFormat(QImage::Format_RGB888); QImage _left = QImage(":/images/images/skybox/left.jpg").convertToFormat(QImage::Format_RGB888); QImage _top = QImage(":/images/images/skybox/top.jpg").convertToFormat(QImage::Format_RGB888); QImage _bottom = QImage(":/images/images/skybox/bottom.jpg").convertToFormat(QImage::Format_RGB888); QImage _front = QImage(":/images/images/skybox/front.jpg").convertToFormat(QImage::Format_RGB888); QImage _back = QImage(":/images/images/skybox/back.jpg").convertToFormat(QImage::Format_RGB888); // 设置宽高 m_cubeMap->setSize(_right.width(), _right.height()); // 设置颜色模式 m_cubeMap->setFormat(QOpenGLTexture::RGBFormat); // 分配存储区域 m_cubeMap->allocateStorage(QOpenGLTexture::RGB, QOpenGLTexture::UInt8); // 分别设置六个面的纹理数据 m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapPositiveX,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_right.bits()); m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapNegativeX,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_left.bits()); m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapPositiveY,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_top.bits()); m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapNegativeY,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_bottom.bits()); m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapPositiveZ,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_front.bits()); m_cubeMap->setData(0, 0, QOpenGLTexture::CubeMapNegativeZ,QOpenGLTexture::RGB, QOpenGLTexture::UInt8, (const void *)_back.bits()); //纹理放大或缩小时,像素的取值方法 ,线性或就近抉择 m_cubeMap->setMinificationFilter(QOpenGLTexture::Linear); m_cubeMap->setMagnificationFilter(QOpenGLTexture::Linear); //设置纹理边缘的扩展方法 m_cubeMap->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::ClampToEdge); m_cubeMap->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::ClampToEdge);
- 绘制立方体贴图
// 深度测试函数确保天空盒等于1也能通过测试 glDepthFunc(GL_LEQUAL); m_skyBoxShaderProgram.bind(); QMatrix4x4 skyboxView=m_view; // 移除skyboxView的位移属性 skyboxView.setColumn(3,QVector4D(0.0f,0.0f,0.0f,1.0f)); m_skyBoxShaderProgram.setUniformValue("projection", m_projection); m_skyBoxShaderProgram.setUniformValue("view", skyboxView); // 指定纹理单元索引 m_skyBoxShaderProgram.setUniformValue("skybox", 0); // 绑定立方体纹理 m_cubeMap->bind(0); // 激活VAO glBindVertexArray(m_skyboxVAO); glDrawArrays(GL_TRIANGLES, 0, 36); glBindVertexArray(0); // 深度测试函数设置为默认行为 glDepthFunc(GL_LESS); m_skyBoxShaderProgram.release();
2.1 demo
Demo输出如下:
3. 环境映射
通过使用环境立方体贴图,可以给物体使用反射(Reflection)
和折射(Refraction)
属性,这样使用的技术被称为环境映射(Environment Mapping)
。
3.1 反射
反射这个属性表现为物体(或物体的一部分)反射它周围环境,原理图如下:
3.1.1 demo
Demo输出如下:
相关代码主要与m_reflectionShaderProgram
变量相关。
3.1.2
通常物体不会产生全反射的效果,此时使用反射贴图会更合理些
3.2 折射
折射是通过斯涅尔定律(Snell’s Law)
来描述的,其原理图如下:
折射率:
使用这些折射率可以计算光在两种不同材质间传播的比值,如从空气到水1.00/1.52=0.658
3.2.1 demo
Demo输出如下:
相关代码主要与m_refractionShaderProgram
变量相关。
4. 反射纹理
修改内容如下:
- 片段着色器
// 反射纹理颜色 vec3 reflectionTextureColor = vec3(texture(material.reflection,TextureCoords)); // 计算反射纹理 vec3 I = normalize(FragmentPosition - viewPosition); vec3 R = reflect(I, normalize(Normal)); vec3 reflectionSkyBox = textureCube(skybox, R).rgb; vec3 reflection=reflectionTextureColor * reflectionSkyBox; FragColor = vec4((ambient + diffuse + specular + reflection), 1.0);
- 代码
/*myopenglwidget.cpp*/
// 指定纹理单元索引
m_shaderProgram.setUniformValue("skybox", 4);
// 绑定立方体纹理
m_cubeMap->bind(4);
/*model.cpp*/
/*
OpenGL官网说的是用aiTextureType_AMBIENT类型接收反射纹理,
但是我看Assimp中有单独的aiTextureType_REFLECTION类型,
且使用起来与aiTextureType_AMBIENT没有什么差别
*/
QVector<Texture> reflectionMaps = loadMaterialTextures(material, aiTextureType_REFLECTION, "texture_reflection");
textures.insert(textures.end(), specularMaps.begin(), specularMaps.end());
/*mesh.cpp*/
else if(name == "texture_specular")
{
// 设置反射纹理通道编号
shader.setUniformValue("material.reflection", index);
}
4.1 demo
demo效果如下: