OpenGL 实例化 初探 之 非实例化绘制行星带

2016-12-29 18:31

http://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/10%20Instancing/

main.cpp

// 引入GLEW库 定义静态链接
#define GLEW_STATIC

#include <glew.h>

// 引入GLFW库
#include <GLFW/glfw3.h>
// 引入SOIL库
#include <SOIL/SOIL.h>
// 引入GLM库
#include <GLM/glm.hpp>
#include <GLM/gtc/matrix_transform.hpp>
#include <GLM/gtc/type_ptr.hpp>

#include <iostream>
#include <vector>
#include <cstdlib>
// 包含着色器加载库
#include "shader.h"
// 包含相机控制辅助类
#include "camera.h"
// 包含纹理加载类
#include "texture.h"
// 加载模型的类
#include "model.h"

#pragma comment(lib, "./SOIL.lib")

#pragma comment (lib, "opengl32.lib")
#pragma comment (lib, "glew32s.lib")
#pragma comment (lib, "glfw3.lib") 
#pragma comment (lib, "glfw3dll.lib") 
#pragma comment (lib, "glew32mxs.lib")
#pragma comment (lib, "assimp.lib")


// 键盘回调函数原型声明
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods);
// 鼠标移动回调函数原型声明
void mouse_move_callback(GLFWwindow* window, double xpos, double ypos);
// 鼠标滚轮回调函数原型声明
void mouse_scroll_callback(GLFWwindow* window, double xoffset, double yoffset);

// 场景中移动
void do_movement();

void prepareInstanceMatrices(std::vector<glm::mat4>& modelMatrices, const int amount);

// 定义程序常量
const int WINDOW_WIDTH = 800, WINDOW_HEIGHT = 600;
// 用于相机交互参数
GLfloat lastX = WINDOW_WIDTH / 2.0f, lastY = WINDOW_HEIGHT / 2.0f;
bool firstMouseMove = true;
bool keyPressedStatus[1024]; // 按键情况记录
GLfloat deltaTime = 0.0f; // 当前帧和上一帧的时间差
GLfloat lastFrame = 0.0f; // 上一帧时间
Camera camera(glm::vec3(0.0f, 0.0f, 55.0f));
const int INSTANCE_COUNT = 10000;

int main(int argc, char** argv)
{

    if (!glfwInit())    // 初始化glfw库
    {
        std::cout << "Error::GLFW could not initialize GLFW!" << std::endl;
        return -1;
    }

    // 开启OpenGL 3.3 core profile
    std::cout << "Start OpenGL core profile version 3.3" << std::endl;
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    // 创建窗口
    GLFWwindow* window = glfwCreateWindow(WINDOW_WIDTH, WINDOW_HEIGHT,
        "Demo of instancing model", NULL, NULL);
    if (!window)
    {
        std::cout << "Error::GLFW could not create winddow!" << std::endl;
        glfwTerminate();
        std::system("pause");
        return -1;
    }
    // 创建的窗口的context指定为当前context
    glfwMakeContextCurrent(window);

    // 注册窗口键盘事件回调函数
    glfwSetKeyCallback(window, key_callback);
    // 注册鼠标事件回调函数
    glfwSetCursorPosCallback(window, mouse_move_callback);
    // 注册鼠标滚轮事件回调函数
    glfwSetScrollCallback(window, mouse_scroll_callback);
    // 鼠标捕获 停留在程序内
    //glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);

    // 初始化GLEW 获取OpenGL函数
    glewExperimental = GL_TRUE; // 让glew获取所有拓展函数
    GLenum status = glewInit();
    if (status != GLEW_OK)
    {
        std::cout << "Error::GLEW glew version:" << glewGetString(GLEW_VERSION)
            << " error string:" << glewGetErrorString(status) << std::endl;
        glfwTerminate();
        std::system("pause");
        return -1;
    }

    // 设置视口参数
    glViewport(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT);

    //Section1 加载模型数据 为了方便更换模型 我们从文件读取模型文件路径
    Model objModels[2];
    std::ifstream modelPath("./res/modelPath.txt");
    if (!modelPath)
    {
        std::cerr << "Error::could not read model path file." << std::endl;
        glfwTerminate();
        std::system("pause");
        return -1;
    }
    std::string modelFilePath;
    for (int i = 0; i < 2; ++i)
    {
        std::getline(modelPath, modelFilePath);
        if (modelFilePath.empty())
        {
            std::cerr << "Error::model path empty." << std::endl;
            glfwTerminate();
            std::system("pause");
            return -1;
        }
        if (!objModels[i].loadModel(modelFilePath))
        {
            glfwTerminate();
            std::system("pause");
            return -1;
        }
    }
    Model& planet = objModels[0];
    Model& rock = objModels[1];
    // Section2 准备着色器程序
    Shader shader("./Shader/vertices", "./Shader/fragement");

    // Section3 为多个实例准备模型变换矩阵
    std::vector<glm::mat4> modelMatrices;
    prepareInstanceMatrices(modelMatrices, INSTANCE_COUNT);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_CULL_FACE);
    // 开始游戏主循环
    while (!glfwWindowShouldClose(window))
    {
        GLfloat currentFrame = (GLfloat)glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;
        std::cout << " time used in this frame: " << deltaTime << " seconds." << std::endl;
        glfwPollEvents(); // 处理例如鼠标 键盘等事件
        do_movement(); // 根据用户操作情况 更新相机属性

        // 清除颜色缓冲区 重置为指定颜色
        glClearColor(0.18f, 0.04f, 0.14f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // 场景绘制
        shader.use();
        glm::mat4 projection = glm::perspective(camera.mouse_zoom,
            (GLfloat)(WINDOW_WIDTH) / WINDOW_HEIGHT, 1.0f, 10000.0f); // 投影矩阵
        glm::mat4 view = camera.getViewMatrix(); // 视变换矩阵

        glUniformMatrix4fv(glGetUniformLocation(shader.programId, "projection"),
            1, GL_FALSE, glm::value_ptr(projection));

        glUniformMatrix4fv(glGetUniformLocation(shader.programId, "view"),
            1, GL_FALSE, glm::value_ptr(view));

        glm::mat4 model;
        model = glm::translate(model, glm::vec3(0.0f, -3.0f, 0.0f));
        model = glm::scale(model, glm::vec3(4.0f, 4.0f, 4.0f));

        glUniformMatrix4fv(glGetUniformLocation(shader.programId, "model"),
            1, GL_FALSE, glm::value_ptr(model));

        planet.draw(shader); // 先绘制行星

        // 绘制多个小行星实例
        for (std::vector<glm::mat4>::size_type i = 0; i < modelMatrices.size(); ++i)
        {
            glUniformMatrix4fv(glGetUniformLocation(shader.programId, "model"),
                1, GL_FALSE, glm::value_ptr(modelMatrices[i]));
            rock.draw(shader);
        }

        glBindVertexArray(0);
        glUseProgram(0);
        glfwSwapBuffers(window); // 交换缓存
    }
    // 释放资源
    glfwTerminate();
    return 0;
}

void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods)
{
    if (key >= 0 && key < 1024)
    {
        if (action == GLFW_PRESS)
            keyPressedStatus[key] = true;
        else if (action == GLFW_RELEASE)
            keyPressedStatus[key] = false;
    }
    if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
    {
        glfwSetWindowShouldClose(window, GL_TRUE); // 关闭窗口
    }
}
void mouse_move_callback(GLFWwindow* window, double xpos, double ypos)
{
    if (firstMouseMove) // 首次鼠标移动
    {
        lastX = xpos;
        lastY = ypos;
        firstMouseMove = false;
    }

    GLfloat xoffset = xpos - lastX;
    GLfloat yoffset = lastY - ypos;

    lastX = xpos;
    lastY = ypos;

    camera.handleMouseMove(xoffset, yoffset);
}
// 由相机辅助类处理鼠标滚轮控制
void mouse_scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
    camera.handleMouseScroll(yoffset);
}
// 由相机辅助类处理键盘控制
void do_movement()
{

    if (keyPressedStatus[GLFW_KEY_W])
        camera.handleKeyPress(FORWARD, deltaTime);
    if (keyPressedStatus[GLFW_KEY_S])
        camera.handleKeyPress(BACKWARD, deltaTime);
    if (keyPressedStatus[GLFW_KEY_A])
        camera.handleKeyPress(LEFT, deltaTime);
    if (keyPressedStatus[GLFW_KEY_D])
        camera.handleKeyPress(RIGHT, deltaTime);
}
// 为实例准备模型变换矩阵
// 这里的细节可以不用深究
void prepareInstanceMatrices(std::vector<glm::mat4>& modelMatrices, const int amount)
{
    srand(glfwGetTime()); // 初始化随机数的种子
    GLfloat radius = 50.0;
    GLfloat offset = 2.5f;
    for (GLuint i = 0; i < amount; i++)
    {
        glm::mat4 model;
        // 1. 平移
        GLfloat angle = (GLfloat)i / (GLfloat)amount * 360.0f;
        GLfloat displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
        GLfloat x = sin(angle) * radius + displacement;
        displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
        GLfloat y = displacement * 0.4f;
        displacement = (rand() % (GLint)(2 * offset * 100)) / 100.0f - offset;
        GLfloat z = cos(angle) * radius + displacement;
        model = glm::translate(model, glm::vec3(x, y, z));

        // 2. 缩放 在 0.05 和 0.25f 之间
        GLfloat scale = (rand() % 20) / 100.0f + 0.05;
        model = glm::scale(model, glm::vec3(scale));

        // 3. 旋转
        GLfloat rotAngle = (rand() % 360);
        model = glm::rotate(model, rotAngle, glm::vec3(0.4f, 0.6f, 0.8f));

        // 4. 添加作为模型变换矩阵
        modelMatrices.push_back(model);
    }
}

mesh.h

#ifndef _MESH_H_
#define _MESH_H_

#include <glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
#include <string> 
#include <vector>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "shader.h"

// 表示一个顶点属性
struct Vertex
{
    glm::vec3 position;
    glm::vec2 texCoords;
    glm::vec3 normal;
};

// 表示一个Texture
struct Texture
{
    GLuint id;
    aiTextureType type;
    std::string path;
};

// 表示一个用于渲染的最小实体
class Mesh
{
public:
    void draw(const Shader& shader) const// 绘制Mesh
    {
        if (VAOId == 0
            || VBOId == 0
            || EBOId == 0)
        {
            return;
        }
        glBindVertexArray(this->VAOId);
        int texUnitCnt = this->bindTextures(shader);
        glDrawElements(GL_TRIANGLES, this->indices.size(), GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
        // 一个好的习惯是 在这里移除纹理绑定
        this->unBindTextures(texUnitCnt);
    }
    int bindTextures(const Shader& shader) const
    {
        int diffuseCnt = 0, specularCnt = 0, texUnitCnt = 0;
        for (std::vector<Texture>::const_iterator it = this->textures.begin();
            this->textures.end() != it; ++it)
        {
            switch (it->type)
            {
            case aiTextureType_DIFFUSE:
            {
                glActiveTexture(GL_TEXTURE0 + texUnitCnt);
                glBindTexture(GL_TEXTURE_2D, it->id);
                std::stringstream samplerNameStr;
                samplerNameStr << "texture_diffuse" << diffuseCnt++;
                glUniform1i(glGetUniformLocation(shader.programId,
                    samplerNameStr.str().c_str()), texUnitCnt++);
            }
            break;
            case aiTextureType_SPECULAR:
            {
                glActiveTexture(GL_TEXTURE0 + texUnitCnt);
                glBindTexture(GL_TEXTURE_2D, it->id);
                std::stringstream samplerNameStr;
                samplerNameStr << "texture_specular" << specularCnt++;
                glUniform1i(glGetUniformLocation(shader.programId,
                    samplerNameStr.str().c_str()), texUnitCnt++);
            }
            break;
            default:
                std::cerr << "Warning::Mesh::draw, texture type" << it->type
                    << " current not supported." << std::endl;
                break;
            }
        }
        return texUnitCnt;
    }
    void unBindTextures(const int texUnitCnt) const
    {
        for (int i = 0; i < texUnitCnt; ++i)
        {
            glActiveTexture(GL_TEXTURE0 + i);
            glBindTexture(GL_TEXTURE_2D, 0);
        }
    }
    Mesh() :VAOId(0), VBOId(0), EBOId(0) {}
    Mesh(const std::vector<Vertex>& vertData,
        const std::vector<Texture> & textures,
        const std::vector<GLuint>& indices) :VAOId(0), VBOId(0), EBOId(0) // 构造一个Mesh
    {
        setData(vertData, textures, indices);
    }
    void setData(const std::vector<Vertex>& vertData,
        const std::vector<Texture> & textures,
        const std::vector<GLuint>& indices)
    {
        this->vertData = vertData;
        this->indices = indices;
        this->textures = textures;
        if (!vertData.empty() && !indices.empty())
        {
            this->setupMesh();
        }
    }
    void final() const
    {
        glDeleteVertexArrays(1, &this->VAOId);
        glDeleteBuffers(1, &this->VBOId);
        glDeleteBuffers(1, &this->EBOId);
    }
    ~Mesh()
    {
        // 不要再这里释放VBO等空间 因为Mesh对象传递时 临时对象销毁后这里会清理VBO等空间
    }
    GLuint getVAOId() const { return this->VAOId; }
    const std::vector<Vertex>& getVertices() const { return this->vertData; }
    const std::vector<GLuint>& getIndices() const { return this->indices; }
private:
    std::vector<Vertex> vertData;
    std::vector<GLuint> indices;
    std::vector<Texture> textures;
    GLuint VAOId, VBOId, EBOId;
    void setupMesh()  // 建立VAO,VBO等缓冲区
    {
        glGenVertexArrays(1, &this->VAOId);
        glGenBuffers(1, &this->VBOId);
        glGenBuffers(1, &this->EBOId);

        glBindVertexArray(this->VAOId);
        glBindBuffer(GL_ARRAY_BUFFER, this->VBOId);
        glBufferData(GL_ARRAY_BUFFER, sizeof(Vertex) * this->vertData.size(),
            &this->vertData[0], GL_STATIC_DRAW);
        // 顶点位置属性
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE,
            sizeof(Vertex), (GLvoid*)0);
        glEnableVertexAttribArray(0);
        // 顶点纹理坐标
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE,
            sizeof(Vertex), (GLvoid*)(3 * sizeof(GL_FLOAT)));
        glEnableVertexAttribArray(1);
        // 顶点法向量属性
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE,
            sizeof(Vertex), (GLvoid*)(5 * sizeof(GL_FLOAT)));
        glEnableVertexAttribArray(2);
        // 索引数据
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, this->EBOId);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint)* this->indices.size(),
            &this->indices[0], GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
        glBindVertexArray(0);
    }
};

#endif 

model.h

#ifndef _MODEL_H_
#define _MODEL_H_

#include <map>
#include <assimp/Importer.hpp>
#include <assimp/scene.h>
#include <assimp/postprocess.h>
#include "mesh.h"
#include "texture.h"

/*
* 代表一个模型 模型可以包含一个或多个Mesh
*/
class Model
{
public:
    void draw(const Shader& shader) const
    {
        for (std::vector<Mesh>::const_iterator it = this->meshes.begin(); this->meshes.end() != it; ++it)
        {
            it->draw(shader);
        }
    }
    bool loadModel(const std::string& filePath)
    {
        Assimp::Importer importer;
        if (filePath.empty())
        {
            std::cerr << "Error:Model::loadModel, empty model file path." << std::endl;
            return false;
        }
        const aiScene* sceneObjPtr = importer.ReadFile(filePath,
            aiProcess_Triangulate | aiProcess_FlipUVs);
        if (!sceneObjPtr
            || sceneObjPtr->mFlags == AI_SCENE_FLAGS_INCOMPLETE
            || !sceneObjPtr->mRootNode)
        {
            std::cerr << "Error:Model::loadModel, description: "
                << importer.GetErrorString() << std::endl;
            return false;
        }
        this->modelFileDir = filePath.substr(0, filePath.find_last_of('/'));
        if (!this->processNode(sceneObjPtr->mRootNode, sceneObjPtr))
        {
            std::cerr << "Error:Model::loadModel, process node failed." << std::endl;
            return false;
        }
        return true;
    }
    ~Model()
    {
        for (std::vector<Mesh>::const_iterator it = this->meshes.begin(); this->meshes.end() != it; ++it)
        {
            it->final();
        }
    }
    const std::vector<Mesh>& getMeshes() const { return this->meshes; }
private:
    /*
 * 递归处理模型的结点
 */
    bool processNode(const aiNode* node, const aiScene* sceneObjPtr)
    {
        if (!node || !sceneObjPtr)
        {
            return false;
        }
        // 先处理自身结点
        for (size_t i = 0; i < node->mNumMeshes; ++i)
        {
            // 注意node中的mesh是对sceneObject中mesh的索引
            const aiMesh* meshPtr = sceneObjPtr->mMeshes[node->mMeshes[i]];
            if (meshPtr)
            {
                Mesh meshObj;
                if (this->processMesh(meshPtr, sceneObjPtr, meshObj))
                {
                    this->meshes.push_back(meshObj);
                }
            }
        }
        // 处理孩子结点
        for (size_t i = 0; i < node->mNumChildren; ++i)
        {
            this->processNode(node->mChildren[i], sceneObjPtr);
        }
        return true;
    }
    bool processMesh(const aiMesh* meshPtr, const aiScene* sceneObjPtr, Mesh& meshObj)
    {
        if (!meshPtr || !sceneObjPtr)
        {
            return false;
        }
        std::vector<Vertex> vertData;
        std::vector<Texture> textures;
        std::vector<GLuint> indices;
        // 从Mesh得到顶点数据、法向量、纹理数据
        for (size_t i = 0; i < meshPtr->mNumVertices; ++i)
        {
            Vertex vertex;
            // 获取顶点位置
            if (meshPtr->HasPositions())
            {
                vertex.position.x = meshPtr->mVertices[i].x;
                vertex.position.y = meshPtr->mVertices[i].y;
                vertex.position.z = meshPtr->mVertices[i].z;
            }
            // 获取纹理数据 目前只处理0号纹理
            if (meshPtr->HasTextureCoords(0))
            {
                vertex.texCoords.x = meshPtr->mTextureCoords[0][i].x;
                vertex.texCoords.y = meshPtr->mTextureCoords[0][i].y;
            }
            else
            {
                vertex.texCoords = glm::vec2(0.0f, 0.0f);
            }
            // 获取法向量数据
            if (meshPtr->HasNormals())
            {
                vertex.normal.x = meshPtr->mNormals[i].x;
                vertex.normal.y = meshPtr->mNormals[i].y;
                vertex.normal.z = meshPtr->mNormals[i].z;
            }
            vertData.push_back(vertex);
        }
        // 获取索引数据
        for (size_t i = 0; i < meshPtr->mNumFaces; ++i)
        {
            aiFace face = meshPtr->mFaces[i];
            if (face.mNumIndices != 3)
            {
                std::cerr << "Error:Model::processMesh, mesh not transformed to triangle mesh." << std::endl;
                return false;
            }
            for (size_t j = 0; j < face.mNumIndices; ++j)
            {
                indices.push_back(face.mIndices[j]);
            }
        }
        // 获取纹理数据
        if (meshPtr->mMaterialIndex >= 0)
        {
            const aiMaterial* materialPtr = sceneObjPtr->mMaterials[meshPtr->mMaterialIndex];
            // 获取diffuse类型
            std::vector<Texture> diffuseTexture;
            this->processMaterial(materialPtr, sceneObjPtr, aiTextureType_DIFFUSE, diffuseTexture);
            textures.insert(textures.end(), diffuseTexture.begin(), diffuseTexture.end());
            // 获取specular类型
            std::vector<Texture> specularTexture;
            this->processMaterial(materialPtr, sceneObjPtr, aiTextureType_SPECULAR, specularTexture);
            textures.insert(textures.end(), specularTexture.begin(), specularTexture.end());
        }
        meshObj.setData(vertData, textures, indices);
        return true;
    }
    /*
 * 获取一个材质中的纹理
 */
    bool processMaterial(const aiMaterial* matPtr, const aiScene* sceneObjPtr,
        const aiTextureType textureType, std::vector<Texture>& textures)
    {
        textures.clear();

        if (!matPtr
            || !sceneObjPtr)
        {
            return false;
        }
        if (matPtr->GetTextureCount(textureType) <= 0)
        {
            return true;
        }
        for (size_t i = 0; i < matPtr->GetTextureCount(textureType); ++i)
        {
            Texture text;
            aiString textPath;
            aiReturn retStatus = matPtr->GetTexture(textureType, i, &textPath);
            if (retStatus != aiReturn_SUCCESS
                || textPath.length == 0)
            {
                std::cerr << "Warning, load texture type=" << textureType
                    << "index= " << i << " failed with return value= "
                    << retStatus << std::endl;
                continue;
            }
            std::string absolutePath = this->modelFileDir + "/" + textPath.C_Str();
            LoadedTextMapType::const_iterator it = this->loadedTextureMap.find(absolutePath);
            if (it == this->loadedTextureMap.end()) // 检查是否已经加载过了
            {
                GLuint textId = TextureHelper::load2DTexture(absolutePath.c_str());
                text.id = textId;
                text.path = absolutePath;
                text.type = textureType;
                textures.push_back(text);
                loadedTextureMap[absolutePath] = text;
            }
            else
            {
                textures.push_back(it->second);
            }
        }
        return true;
    }
private:
    std::vector<Mesh> meshes; // 保存Mesh
    std::string modelFileDir; // 保存模型文件的文件夹路径
    typedef std::map<std::string, Texture> LoadedTextMapType; // key = texture file path
    LoadedTextMapType loadedTextureMap; // 保存已经加载的纹理
};

#endif

Camera.h

#ifndef _CAMERA_H_
#define _CAMERA_H_

#include <iostream>
#include <fstream>
#include <glew.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/string_cast.hpp>
#include <iomanip> // std::setprecision
// 定义移动方向
enum Camera_Movement {
    FORWARD,
    BACKWARD,
    LEFT,
    RIGHT
};
// 定义预设常量
const GLfloat YAW = 0.0f;
const GLfloat PITCH = 0.0f;
const GLfloat SPEED = 3.0f;
const GLfloat MOUSE_SENSITIVTY = 0.05f;
const GLfloat MOUSE_ZOOM = 45.0f;
const float  MAX_PITCH_ANGLE = 89.0f; // 防止万向锁

class Camera
{
public:
    Camera(glm::vec3 pos = glm::vec3(0.0, 0.0, 2.0),
        glm::vec3 up = glm::vec3(0.0, 1.0, 0.0),
        GLfloat yaw = YAW, GLfloat pitch = PITCH)
        :position(pos), forward(0.0, 0.0, -1.0), viewUp(up),
        moveSpeed(SPEED), mouse_zoom(MOUSE_ZOOM), mouse_sensitivity(MOUSE_SENSITIVTY),
        yawAngle(yaw), pitchAngle(pitch)
    {
        this->updateCameraVectors();
    }
public:
    // 获取视变换矩阵
    glm::mat4 getViewMatrix()
    {
        return glm::lookAt(this->position, this->position + this->forward, this->viewUp);
    }
    // 处理键盘按键后方向移动
    void handleKeyPress(Camera_Movement direction, GLfloat deltaTime)
    {
        GLfloat velocity = this->moveSpeed * deltaTime;
        switch (direction)
        {
        case FORWARD:
            this->position += this->forward * velocity;
            break;
        case BACKWARD:
            this->position -= this->forward * velocity;
            break;
        case LEFT:
            this->position -= this->side * velocity;
            break;
        case RIGHT:
            this->position += this->side * velocity;
            break;
        default:
            break;
        }
    }
    // 处理鼠标移动
    void handleMouseMove(GLfloat xoffset, GLfloat yoffset)
    {

        xoffset *= this->mouse_sensitivity; // 用鼠标灵敏度调节角度变换
        yoffset *= this->mouse_sensitivity;

        this->pitchAngle += yoffset;
        this->yawAngle += xoffset;

        this->normalizeAngle();
        this->updateCameraVectors();
    }
    // 处理鼠标滚轮缩放 保持在[1.0, MOUSE_ZOOM]之间
    void handleMouseScroll(GLfloat yoffset)
    {
        if (this->mouse_zoom >= 1.0f && this->mouse_zoom <= MOUSE_ZOOM)
            this->mouse_zoom -= this->mouse_sensitivity * yoffset;
        if (this->mouse_zoom <= 1.0f)
            this->mouse_zoom = 1.0f;
        if (this->mouse_zoom >= 45.0f)
            this->mouse_zoom = 45.0f;
    }
    // 使pitch yaw角度保持在合理范围内
    void normalizeAngle()
    {
        if (this->pitchAngle > MAX_PITCH_ANGLE)
            this->pitchAngle = MAX_PITCH_ANGLE;
        if (this->pitchAngle < -MAX_PITCH_ANGLE)
            this->pitchAngle = -MAX_PITCH_ANGLE;
        if (this->yawAngle < 0.0f)
            this->yawAngle += 360.0f;
    }
    // 计算forward side向量
    void updateCameraVectors()
    {
        glm::vec3 forward;
        forward.x = -sin(glm::radians(this->yawAngle)) * cos(glm::radians(this->pitchAngle));
        forward.y = sin(glm::radians(this->pitchAngle));
        forward.z = -cos(glm::radians(this->yawAngle)) * cos(glm::radians(this->pitchAngle));
        this->forward = glm::normalize(forward);

        glm::vec3 side;
        side.x = cos(glm::radians(this->yawAngle));
        side.y = 0;
        side.z = -sin(glm::radians(this->yawAngle));
        this->side = glm::normalize(side);
    }
public:
    glm::vec3 forward, up, side, viewUp, position; // 相机属性
    GLfloat yawAngle, pitchAngle; // 欧拉角
    GLfloat moveSpeed, mouse_sensitivity, mouse_zoom; // 相机选项
};

#endif

texture.h

#ifndef _TEXTURE_H_
#define _TEXTURE_H_

#include <glew.h>
#include <iostream>
#include <fstream>

class TextureHelper
{
public:
    /*
 /* 成功加载2D纹理则返回纹理对象Id 否则返回0
 */
    static  GLuint load2DTexture(const char* filename, GLint internalFormat = GL_RGBA,
        GLenum picFormat = GL_RGBA, int loadChannels = SOIL_LOAD_RGBA)
    {
        // Step1 创建并绑定纹理对象
        GLuint textureId = 0;
        glGenTextures(1, &textureId);
        glBindTexture(GL_TEXTURE_2D, textureId);
        // Step2 设定wrap参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
        // Step3 设定filter参数
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
            GL_LINEAR_MIPMAP_LINEAR); // 为MipMap设定filter方法
                                      // Step4 加载纹理
        GLubyte *imageData = NULL;
        int picWidth, picHeight;
        int channels = 0;
        imageData = SOIL_load_image(filename, &picWidth, &picHeight, &channels, loadChannels);
        if (imageData == NULL)
        {
            std::cerr << "Error::Texture could not load texture file:" << filename << std::endl;
            return 0;
        }
        glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, picWidth, picHeight,
            0, picFormat, GL_UNSIGNED_BYTE, imageData);
        glGenerateMipmap(GL_TEXTURE_2D);
        // Step5 释放纹理图片资源
        SOIL_free_image_data(imageData);
        glBindTexture(GL_TEXTURE_2D, 0);
        return textureId;
    }
#define FOURCC_DXT1 0x31545844 // Equivalent to "DXT1" in ASCII
#define FOURCC_DXT3 0x33545844 // Equivalent to "DXT3" in ASCII
#define FOURCC_DXT5 0x35545844 // Equivalent to "DXT5" in ASCII

    static GLuint loadDDS(const char * filename) {


        /* try to open the file */
        std::ifstream file(filename, std::ios::in | std::ios::binary);
        if (!file) {
            std::cout << "Error::loadDDs, could not open:"
                << filename << "for read." << std::endl;
            return 0;
        }

        /* verify the type of file */
        char filecode[4];
        file.read(filecode, 4);
        if (strncmp(filecode, "DDS ", 4) != 0) {
            std::cout << "Error::loadDDs, format is not dds :"
                << filename << std::endl;
            file.close();
            return 0;
        }

        /* get the surface desc */
        char header[124];
        file.read(header, 124);

        unsigned int height = *(unsigned int*)&(header[8]);
        unsigned int width = *(unsigned int*)&(header[12]);
        unsigned int linearSize = *(unsigned int*)&(header[16]);
        unsigned int mipMapCount = *(unsigned int*)&(header[24]);
        unsigned int fourCC = *(unsigned int*)&(header[80]);


        char * buffer = NULL;
        unsigned int bufsize;
        /* how big is it going to be including all mipmaps? */
        bufsize = mipMapCount > 1 ? linearSize * 2 : linearSize;
        buffer = new char[bufsize];
        file.read(buffer, bufsize);
        /* close the file pointer */
        file.close();

        unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
        unsigned int format;
        switch (fourCC)
        {
        case FOURCC_DXT1:
            format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
            break;
        case FOURCC_DXT3:
            format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
            break;
        case FOURCC_DXT5:
            format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
            break;
        default:
            delete[] buffer;
            return 0;
        }

        // Create one OpenGL texture
        GLuint textureID;
        glGenTextures(1, &textureID);

        // "Bind" the newly created texture : all future texture functions will modify this texture
        glBindTexture(GL_TEXTURE_2D, textureID);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
        unsigned int offset = 0;

        /* load the mipmaps */
        for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
        {
            unsigned int size = ((width + 3) / 4)*((height + 3) / 4)*blockSize;
            glCompressedTexImage2D(GL_TEXTURE_2D, level, format, width, height,
                0, size, buffer + offset);

            offset += size;
            width /= 2;
            height /= 2;

            // Deal with Non-Power-Of-Two textures. This code is not included in the webpage to reduce clutter.
            if (width < 1) width = 1;
            if (height < 1) height = 1;

        }

        delete[] buffer;

        return textureID;
    }
};

#endif

Shader.h

#ifndef _SHADER_H_
#define _SHADER_H_

#include <glew.h>
#include <iterator> // std::istreambuf_iterator
#include <string> 
#include <vector>
#include <iostream>
#include <fstream>

struct ShaderFile
{
    GLenum shaderType;
    const char* filePath;
    ShaderFile(GLenum type, const char* path)
        :shaderType(type), filePath(path) {}
};

class Shader
{
public:
    Shader(const char* vertexPath, const char* fragPath) :programId(0)
    {
        std::vector<ShaderFile> fileVec;
        fileVec.push_back(ShaderFile(GL_VERTEX_SHADER, vertexPath));
        fileVec.push_back(ShaderFile(GL_FRAGMENT_SHADER, fragPath));
        loadFromFile(fileVec);
    }
    Shader(const char* vertexPath, const char* fragPath, const char* geometryPath) :programId(0)
    {
        std::vector<ShaderFile> fileVec;
        fileVec.push_back(ShaderFile(GL_VERTEX_SHADER, vertexPath));
        fileVec.push_back(ShaderFile(GL_FRAGMENT_SHADER, fragPath));
        fileVec.push_back(ShaderFile(GL_GEOMETRY_SHADER, geometryPath));
        loadFromFile(fileVec);
    }
    void use()
    {
        glUseProgram(this->programId);
    }
    ~Shader()
    {
        if (this->programId)
        {
            glDeleteProgram(this->programId);
        }
    }
public:
    GLuint programId;
private:
    /*
 * 从文件加载顶点和片元着色器
 * 传递参数为 [(着色器文件类型,着色器文件路径)+]
 */
    void loadFromFile(std::vector<ShaderFile>& shaderFileVec)
    {
        std::vector<GLuint> shaderObjectIdVec;
        std::string vertexSource, fragSource;
        std::vector<std::string> sourceVec;
        size_t shaderCount = shaderFileVec.size();
        // 读取文件源代码
        for (size_t i = 0; i < shaderCount; ++i)
        {
            std::string shaderSource;
            if (!loadShaderSource(shaderFileVec[i].filePath, shaderSource))
            {
                std::cout << "Error::Shader could not load file:" << shaderFileVec[i].filePath << std::endl;
                return;
            }
            sourceVec.push_back(shaderSource);
        }
        bool bSuccess = true;
        // 编译shader object
        for (size_t i = 0; i < shaderCount; ++i)
        {
            GLuint shaderId = glCreateShader(shaderFileVec[i].shaderType);
            const char *c_str = sourceVec[i].c_str();
            glShaderSource(shaderId, 1, &c_str, NULL);
            glCompileShader(shaderId);
            GLint compileStatus = 0;
            glGetShaderiv(shaderId, GL_COMPILE_STATUS, &compileStatus); // 检查编译状态
            if (compileStatus == GL_FALSE) // 获取错误报告
            {
                GLint maxLength = 0;
                glGetShaderiv(shaderId, GL_INFO_LOG_LENGTH, &maxLength);
                std::vector<GLchar> errLog(maxLength);
                glGetShaderInfoLog(shaderId, maxLength, &maxLength, &errLog[0]);
                std::cout << "Error::Shader file [" << shaderFileVec[i].filePath << " ] compiled failed,"
                    << &errLog[0] << std::endl;
                bSuccess = false;
            }
            shaderObjectIdVec.push_back(shaderId);
        }
        // 链接shader program
        if (bSuccess)
        {
            this->programId = glCreateProgram();
            for (size_t i = 0; i < shaderCount; ++i)
            {
                glAttachShader(this->programId, shaderObjectIdVec[i]);
            }
            glLinkProgram(this->programId);
            GLint linkStatus;
            glGetProgramiv(this->programId, GL_LINK_STATUS, &linkStatus);
            if (linkStatus == GL_FALSE)
            {
                GLint maxLength = 0;
                glGetProgramiv(this->programId, GL_INFO_LOG_LENGTH, &maxLength);
                std::vector<GLchar> errLog(maxLength);
                glGetProgramInfoLog(this->programId, maxLength, &maxLength, &errLog[0]);
                std::cout << "Error::shader link failed," << &errLog[0] << std::endl;
            }
        }
        // 链接完成后detach 并释放shader object
        for (size_t i = 0; i < shaderCount; ++i)
        {
            if (this->programId != 0)
            {
                glDetachShader(this->programId, shaderObjectIdVec[i]);
            }
            glDeleteShader(shaderObjectIdVec[i]);
        }
    }
    /*
 * 读取着色器程序源码
 */
    bool loadShaderSource(const char* filePath, std::string& source)
    {
        source.clear();
        std::ifstream in_stream(filePath);
        if (!in_stream)
        {
            return false;
        }
        source.assign(std::istreambuf_iterator<char>(in_stream),
            std::istreambuf_iterator<char>()); // 文件流迭代器构造字符串
        return true;
    }
};
#endif

GLSL Shader 部分

vertices

#version 330 core

layout(location = 0) in vec3 position;
layout(location = 1) in vec2 textCoord;
layout(location = 2) in vec3 normal;


uniform mat4 projection;
uniform mat4 view;
uniform mat4 model;

out vec2 TextCoord;

void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0);
    TextCoord = textCoord;
}

fragement

#version 330 core

in vec2 TextCoord;
uniform sampler2D texture_diffuse0;
uniform sampler2D texture_diffuse1;
uniform sampler2D texture_specular0;
uniform sampler2D texture_specular1;

out vec4 color;


void main()
{
    color = texture(texture_diffuse0, TextCoord);
}

这里写图片描述

这里写图片描述