OpenGL学习之创建天空盒

本文主要参考了立方体贴图的基本原理,首先回顾一下什么是立方体贴图:
将多个纹理组合起来映射到一个单一纹理,就是立方体贴图(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();

效果如下图:

OpenGL学习之创建天空盒_第1张图片

你可能感兴趣的:(OpenGL)