opengl + glfw + glad 实现天空盒

天空盒基础:

天空盒技术,说到底就是就是多纹理贴图技术。是一种将多个纹理贴到一个立方体表面的技术。即立方体贴图

简单来说,立方体贴图就是一个包含了6个2D纹理的纹理,每个2D纹理都组成了立方体的一个面:一个有纹理的立方体。你可能会奇怪,这样一个立方体有什么用途呢?为什么要把6张纹理合并到一张纹理中,而不是直接使用6个单独的纹理呢?立方体贴图有一个非常有用的特性,它可以通过一个方向向量来进行索引/采样。假设我们有一个1x1x1的单位立方体,方向向量的原点位于它的中心。使用一个橘黄色的方向向量来从立方体贴图上采样一个纹理值会像是这样:

opengl + glfw + glad 实现天空盒

一个提供天空盒的网站:http://www.custommapmakers.org/skyboxes.php

opengl + glfw + glad 实现天空盒

如果你将这六个面折成一个立方体,你就会得到一个完全贴图的立方体,模拟一个巨大的场景。一些资源可能会提供了这样格式的天空盒,你必须手动提取六个面的图像,但在大部分情况下它们都是6张单独的纹理图像。

下面的网址是,LearnOpengl上针对立方体贴图的文档

https://learnopengl-cn.github.io/04%20Advanced%20OpenGL/06%20Cubemaps/ 

我在这个文档的额基础上进行了简单的封装:

skyBox.h

#ifndef _SKYBOX_H_
#define _SKYBOX_H_

#include <vector>
#include <string>
#include <memory>

class Camera;
class Shader;
class SkyBox
{
public:
    /**
     * *structure
     */ 
	SkyBox(std::weak_ptr<Camera> pCamera,
            const float &width = 0,const float &height = 0);
    /**
     * *destruct
     */ 
	~SkyBox();
    /**
     * *init
     * @brief init skybox cube
     * @param null
     * @return null
     */ 
    void init();
    /**
     * *loadCubeMap
     * @brief load textures for skybox
     * @param images,skybox images list,includes top\bottom\left\right\front\back,total six images.
     * @return bool,true or false.ture is load sucess,false is load failed.      
     */ 
    bool loadCubeMap(const std::vector<std::string>& images);
    /**
     * *draw
     * @brief draw skybox
     * @param null
     * @return null
     */ 
    void draw();
private:
    // ?CubeMap textureid  
    unsigned int      m_cubeTextureId; 
    // ?camera object     
    std::shared_ptr<Camera> m_pCamera;
    // ?shader
    std::shared_ptr<Shader> m_pShader;
    // ?顶点数组对象
    unsigned int      m_skyboxVAO;
    // ?顶点缓冲对象
    unsigned int      m_skyboxVBO;

    float m_width;
    float m_height;
};

#endif //_SKYBOX_H_

 

skyBox.cpp

#include "skyBox.h"
#include <iostream>

#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "core/stb_image.h"

#include "core/shader.h"
#include "core/camera.h"


SkyBox::SkyBox(std::weak_ptr<Camera> pCamera,
        const float &width,const float &height)
    :m_cubeTextureId(0),m_pCamera(nullptr),m_pShader(nullptr),
    m_skyboxVAO(0),m_skyboxVBO(0),
    m_width(width),m_height(height)
{
    if(!m_pCamera.use_count())
        m_pCamera = pCamera.lock();
    if(!m_pShader.use_count())
        m_pShader = std::make_shared<Shader>("../shader/skybox.vs",
                                  "../shader/skybox.fs");    
    init();
}


SkyBox::~SkyBox()
{
    // glDeleteVertexArrays(1,&m_skyboxVAO);
    // glDeleteBuffers(1,&m_skyboxVBO);
}

void SkyBox::init()
{
    //准备数据
    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
    };

    glGenVertexArrays(1, &m_skyboxVAO);
    glGenBuffers(1, &m_skyboxVBO);
    glBindVertexArray(m_skyboxVAO);
    glBindBuffer(GL_ARRAY_BUFFER, m_skyboxVBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), &skyboxVertices, GL_STATIC_DRAW);
    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
}

bool SkyBox::loadCubeMap(const std::vector<std::string>& images)
{
    glGenTextures(1, &m_cubeTextureId);
    glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubeTextureId);

    int width, height, nrChannels;
    for (unsigned int i = 0; i < images.size(); i++)
    {
        auto data = stbi_load(images[i].c_str(), &width, &height, &nrChannels, 0);
        if (data)
        {
            glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
            stbi_image_free(data);
        }
        else
        {
            std::cout << "Cubemap texture failed to load at path: " << images[i] << std::endl;
            stbi_image_free(data);
            return false;
        }
    }
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_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);

    m_pShader->use();
    m_pShader->setInt("skybox", 0);

    return true;
}

void SkyBox::draw()
{
    // todo
    m_pShader->use();
    // remove translation from the view matrix
    glm::mat4 view = glm::mat4(glm::mat3(m_pCamera->GetViewMatrix()));
    glm::mat4 projection = glm::perspective(glm::radians(m_pCamera->Zoom), 
                                m_width / m_height, 0.1f, 100.0f);
    
    m_pShader->setMat4("view", view);
    m_pShader->setMat4("projection", projection);

    glBindVertexArray(m_skyboxVAO);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_CUBE_MAP, m_cubeTextureId);
    glDrawArrays(GL_TRIANGLES, 0, 36);
    glBindVertexArray(0);
    glDepthFunc(GL_LESS); // set depth function back to default
}

 

skybox.vs

#version 330 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;
}  

skybox.fs

#version 330 core
out vec4 FragColor;

in vec3 TexCoords;

uniform samplerCube skybox;

void main()
{    
    FragColor = texture(skybox, TexCoords);
}

调用

std::shared_ptr<Camera> pCamera = std::make_shared<Camera>(glm::vec3(0.0f, 0.0f, 0.0f));
    SkyBox skyBox(pCamera,(float)SCR_WIDTH,(float)SCR_HEIGHT);

    // load textures

    std::vector<std::string> images
    {
        "../resources/Texture/mp_ac/ac_rt.tga",
        "../resources/Texture/mp_ac/ac_lf.tga",
        "../resources/Texture/mp_ac/ac_up.tga",
        "../resources/Texture/mp_ac/ac_dn.tga",
        "../resources/Texture/mp_ac/ac_ft.tga",
        "../resources/Texture/mp_ac/ac_bk.tga"
    };

    skyBox.loadCubeMap(images);
glDepthFunc(GL_LEQUAL);

        skyBox.draw();

运行结果:

opengl + glfw + glad 实现天空盒