http://learnopengl-cn.readthedocs.io/zh/latest/04%20Advanced%20OpenGL/10%20Instancing/
http://blog.csdn.net/wangdingqiaoit/article/details/52733351
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);
}
源码下载:
http://download.csdn.net/detail/yulinxx/9724853
用此方式,若将
const int INSTANCE_COUNT = 10000;
改为
const int INSTANCE_COUNT = 100000;
效率就很低了