本文主要参考了立方体贴图的基本原理,首先回顾一下什么是立方体贴图:
将多个纹理组合起来映射到一个单一纹理,就是立方体贴图(Cube Map)。基本上说立方体贴图它包含6个2D纹理,这每个2D纹理是一个立方体(cube)的一个面,也就是说它是一个有贴图的立方体。你可能会奇怪这样的立方体有什么用?为什么费事地把6个独立纹理结合为一个单独的纹理,只使用6个各自独立的不行吗?这是因为立方体贴图有自己特有的属性,可以使用方向向量对它们索引和采样。想象一下,我们有一个1×1×1的单位立方体,有个以原点为起点的方向向量在它的中心。
注意:方向向量的大小无关紧要。一旦提供了方向,OpenGL就会获取方向向量触碰到立方体表面上的相应的纹理像素(texel),这样就返回了正确的纹理采样值。
下面贴出天空盒类
skyBox.h
#ifndef _SKYBOX_H_
#define _SKYBOX_H_
#include
#include
#include
class CCamera;
class CShader;
class CSkyBox
{
public:
CSkyBox(CCamera* pCamera);
~CSkyBox();
void initSkyBox();
bool loadCubeMap(const std::vector& images);
void drawSkyBox();
private:
GLuint m_cubeTextureId;//CubeMap textureid
CCamera* m_pCamera;
CShader* m_pShader;
GLuint m_skyboxVAO;
GLuint m_uniformBlockSkyBox;
};
#endif //_SKYBOX_H_
skyBox.cpp
#include "SkyBox.h"
#include
#include
#include
#include
#include
#include
#define STB_IMAGE_IMPLEMENTATION
#include
#include "Camera.h"
#include "Shader.h"
CSkyBox::CSkyBox(CCamera* pCamera):m_pCamera(pCamera),
m_cubeTextureId(0),
m_skyboxVAO(0),
m_uniformBlockSkyBox(0),
m_pShader(nullptr)
{
m_pShader = new CShader;
m_pShader->loadShader("../res/shader/skybox.vs", "../res/shader/skybox.ps");
m_uniformBlockSkyBox = glGetUniformBlockIndex(m_pShader->getProgram(), "Matrices");
}
CSkyBox::~CSkyBox()
{
if (m_pShader)
delete m_pShader;
m_pShader = nullptr;
if (m_pCamera)
delete m_pCamera;
m_pCamera = nullptr;
}
void CSkyBox::initSkyBox()
{
//准备数据
float 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
};
// Setup skybox VAO
GLuint skyboxVBO;
glGenVertexArrays(1, &m_skyboxVAO);
glGenBuffers(1, &skyboxVBO);
glBindVertexArray(m_skyboxVAO);
glBindBuffer(GL_ARRAY_BUFFER, skyboxVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (GLvoid*)0);
glBindVertexArray(0);
}
bool CSkyBox::loadCubeMap(const std::vector& images)
{
int imageWidth, imageHeight, imageChinnel;
GLubyte* imagedata;
if (images.empty())
return false;
glGenTextures(1, &m_cubeTextureId); //生成材质id
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubeTextureId);//绑定材质ID 与立方体贴图对象进行关联
for (int i = 0; i < images.size(); i++)
{
imagedata = stbi_load(images[i], &imageWidth, &imageHeight, &imageChinnel, 0);
if (!imagedata)
{
return false;
}
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
GL_RGB, imageWidth, imageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, imagedata
);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
//开启mipmap贴图模式
glGenerateMipmap(GL_TEXTURE_CUBE_MAP);
return true;
}
void CSkyBox::drawSkyBox()
{
m_pShader->ProgramUse();
#if 1 //只关心移动
//第二种写法,只关心移动(原理是摄像机移动的时候天空盒也随着移动,相对地天空盒就在无限远处。)
glm::mat4 transMat = glm::translate(m_pCamera->getCameraPos());
glm::mat4 model = transMat;
m_pShader->setShaderUniform("model", model, false);
glm::mat4 viewMat = m_pCamera->getCameraViewMatrix();
#else
//第一种写法 只关心旋转 将视图矩阵的位移规避只保留旋转
glm::mat4 viewMat = glm::mat4(glm::mat3(pCamera->getCameraViewMatrix()));
glm::mat4 viewMat = m_pCamera->getCameraViewMatrix();
#endif
m_pShader->setShaderUniform("view", viewMat, false);
glm::mat4 projectMat = m_pCamera->getCameraProjectMatrix();
m_pShader->setShaderUniform("projection", projectMat, false);
// skybox cube
glBindVertexArray(m_skyboxVAO);
glActiveTexture(GL_TEXTURE0);
m_pShader->setShaderUniform("skybox",(GLuint) 0);
// m_pShader->setShaderUniform("num", 0.5f);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubeTextureId);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
m_pShader->stopProgrameUse();
}
天空盒着色器代码
skybox.vs
#version 330 core
layout (location = 0) in vec3 position;
out vec3 TexCoords;
uniform float scale;
layout (std140) uniform Matrices
{
mat4 projection;
mat4 view;
};
void main()
{
vec4 pos = projection * view * vec4(vec3(position.x*scale,position.y*scale,position.z*scale), 1.0);
gl_Position = pos.xyww; //透视除法 TexCoords = position;
}
skybox.ps
#version 330 core
in vec3 TexCoords;
out vec4 color;
uniform samplerCube skybox;
void main()
{
color = texture(skybox, TexCoords);
}
调用:
初始化天空盒数据
//图片的顺序跟定义纹理的顺序有关,不能错乱否则效果不对。
//初始化天空盒
std::vector images;
images.push_back("../res/ame_siege/siege_lf.tga");//左
images.push_back("../res/ame_siege/siege_rt.tga");//右
images.push_back("../res/ame_siege/siege_up.tga");//上
images.push_back("../res/ame_siege/siege_dn.tga");//下
images.push_back("../res/ame_siege/siege_bk.tga");//后
images.push_back("../res/ame_siege/siege_ft.tga");//前
m_pSkyBox->initSkyBox();
if (!m_pSkyBox->loadCubeMap(images))
在更新出调用天空盒绘制函数和一些必要参数设置。
//绘制天空盒必须保证为第一个渲染天空盒,设置深度测试函数
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Clear all attached buffers
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glDepthFunc(GL_LEQUAL); // 更改天空盒的深度写入
//绘制天空盒
m_pSkyBox->drawSkyBox();
效果如下图: