Created
January 18, 2013 10:56
-
-
Save kbenzie/4563858 to your computer and use it in GitHub Desktop.
OpenGL instancing a model with multiple sub meshes using a single model matrix transform buffer.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 | |
struct material_data | |
{ | |
vec4 emissive; | |
vec4 ambient; | |
vec4 diffuse; | |
vec4 specular; | |
float shininess; | |
}; | |
struct light_data | |
{ | |
vec4 ambient; | |
vec4 diffuse; | |
vec4 specular; | |
vec4 direction; | |
}; | |
uniform material_data material; | |
uniform light_data light; | |
uniform vec3 camera_position; | |
in vec3 position; | |
in vec3 normal; | |
in vec3 instanced_colour; | |
out vec4 colour; | |
vec4 phong(in vec3 eye) | |
{ | |
vec4 ambient = material.ambient * light.ambient * vec4(instanced_colour, 1); | |
vec4 diffuse = max(dot(light.direction.xyz, normal), 0) * | |
(material.diffuse * light.diffuse) * vec4(instanced_colour, 1); | |
vec4 specular = pow(max(dot(reflect(-light.direction.xyz, normal), eye), 0), material.shininess) * | |
(material.specular * light.specular); | |
return vec4(ambient.xyz + diffuse.xyz + specular.xyz, 1); | |
} | |
void main() | |
{ | |
vec3 eye = normalize(camera_position - position); | |
colour = vec4(material.emissive.xyz + phong(eye).xyz, 1); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#version 330 | |
layout (location = 0) in vec3 vertex_position; | |
layout (location = 1) in vec3 vertex_normal; | |
layout (location = 2) in vec3 colour; | |
layout (location = 3) in vec4 model_matrix_0; | |
layout (location = 4) in vec4 model_matrix_1; | |
layout (location = 5) in vec4 model_matrix_2; | |
layout (location = 6) in vec4 model_matrix_3; | |
out vec3 position; | |
out vec3 normal; | |
out vec3 instanced_colour; | |
uniform mat4 view_matrix; | |
uniform mat4 projection_matrix; | |
void main() | |
{ | |
mat4 model_matrix; | |
model_matrix[0] = model_matrix_0; | |
model_matrix[1] = model_matrix_1; | |
model_matrix[2] = model_matrix_2; | |
model_matrix[3] = model_matrix_3; | |
normal = normalize(mat3(inverse(transpose(model_matrix))) * vertex_normal); | |
mat4 model_view_matrix = view_matrix * model_matrix; | |
instanced_colour = colour; | |
position = vec3(model_view_matrix * vec4(vertex_position, 1)); | |
gl_Position = (projection_matrix * model_view_matrix) * vec4(vertex_position, 1); | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "model.h" | |
#include "log.h" | |
#include <IL\il.h> | |
#include <IL\ilut.h> | |
#include <assimp\scene.h> | |
#include <assimp\Importer.hpp> | |
#include <assimp\postprocess.h> | |
namespace elixir | |
{ | |
mesh::mesh() | |
: m_vao(0) | |
, m_ibo(0) | |
, m_size(0) | |
{} | |
mesh::mesh(const std::vector<glm::vec3> &positions, | |
const std::vector<glm::vec3> &normals, | |
const std::vector<GLuint> &indices) | |
{ | |
m_vbos.resize(2); | |
if (indices.size()) m_size = indices.size(); | |
else m_size = positions.size(); | |
glGenBuffers(m_vbos.size(), &m_vbos[0]); | |
if (indices.size()) glGenBuffers(1, &m_ibo); | |
else m_ibo = 0; | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[0]); // vertex_position | |
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * positions.size(), &positions[0], GL_STATIC_DRAW); | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[1]); // vertex_normal | |
glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * normals.size(), &normals[0], GL_STATIC_DRAW); | |
if (m_ibo) { | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_ibo); // index_buffer_object | |
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * indices.size(), &indices[0], GL_STATIC_DRAW); | |
} | |
glGenVertexArrays(1, &m_vao); | |
glBindVertexArray(m_vao); | |
glEnableVertexAttribArray(0); // vertex_position | |
glEnableVertexAttribArray(1); // vertex_normal | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[0]); // vertex_position | |
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr); | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[1]); // vertex_normal | |
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, nullptr); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
} | |
model::model() | |
{} | |
model::model(const std::vector<mesh> &meshes) | |
: m_vbos(2, 0) | |
, m_meshes(meshes) | |
{ | |
glGenBuffers(2, &m_vbos[0]); | |
for (auto i=0u; i < m_meshes.size(); ++i) { | |
glBindVertexArray(m_meshes[i].m_vao); | |
glEnableVertexAttribArray(2); // colour | |
glEnableVertexAttribArray(3); // model_matrix_0 | |
glEnableVertexAttribArray(4); // model_matrix_1 | |
glEnableVertexAttribArray(5); // model_matrix_2 | |
glEnableVertexAttribArray(6); // model_matrix_3 | |
} | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[0]); // colour | |
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, sizeof(glm::vec3), (GLvoid*)0); | |
glVertexAttribDivisor(2, 1); | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[1]); | |
glVertexAttribPointer(3, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)0); | |
glVertexAttribDivisor(3, 1); // model_matrix_0 | |
glVertexAttribPointer(4, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)sizeof(glm::vec4)); | |
glVertexAttribDivisor(4, 1); // model_matrix_1 | |
glVertexAttribPointer(5, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4)*2)); | |
glVertexAttribDivisor(5, 1); // model_matrix_2 | |
glVertexAttribPointer(6, 4, GL_FLOAT, GL_FALSE, sizeof(glm::mat4), (GLvoid*)(sizeof(glm::vec4)*3)); | |
glVertexAttribDivisor(6, 1); // model_matrix_3 | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
} | |
model::~model() | |
{ | |
for (auto i=0u; i < m_meshes.size(); ++i) { | |
// Delete buffers | |
if (m_meshes[i].m_vbos.size()) | |
glDeleteBuffers(m_meshes[i].m_vbos.size(), &m_meshes[i].m_vbos[0]); | |
if (glIsBuffer(m_meshes[i].m_vao)) glDeleteBuffers(1, &m_meshes[i].m_vao); | |
if (glIsBuffer(m_meshes[i].m_ibo)) glDeleteBuffers(1, &m_meshes[i].m_ibo); | |
m_meshes[i].m_vbos.clear(); | |
m_meshes[i].m_vao = 0; | |
m_meshes[i].m_ibo = 0; | |
m_meshes[i].m_size = 0; | |
} | |
if (m_vbos.size()) glDeleteBuffers(2, &m_vbos[0]); | |
} | |
void model::add(const glm::mat4 &model_matrix, const glm::vec3 &colour) | |
{ | |
m_model_matrices.push_back(model_matrix); | |
m_colours.push_back(colour); | |
set_buffers(); | |
} | |
void model::add(const unsigned size, const glm::vec3 &colour) | |
{ | |
m_model_matrices.resize(size, glm::mat4(1)); | |
set_buffers(); | |
} | |
void model::set(const std::vector<glm::mat4> &model_matrices, const std::vector<glm::vec3> &colours) | |
{ | |
m_model_matrices = model_matrices; | |
m_colours = colours; | |
set_buffers(); | |
} | |
const GLuint & model::model_matrix_buffer() const | |
{ | |
return m_vbos[1]; | |
} | |
void model::render(glsl_program &program) | |
{ | |
for (auto i=0u; i<m_meshes.size(); ++i) { | |
program.set_uniform("material.ambient", m_meshes[i].m_material.ambient); | |
program.set_uniform("material.diffuse", m_meshes[i].m_material.diffuse); | |
program.set_uniform("material.specular", m_meshes[i].m_material.specular); | |
program.set_uniform("material.shininess", m_meshes[i].m_material.shininess); | |
glBindVertexArray(m_meshes[i].m_vao); | |
if (m_meshes[i].m_ibo) | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_meshes[i].m_ibo); | |
for (auto j=0u; j<m_model_matrices.size(); ++j) { | |
program.set_uniform("model_matrix", m_model_matrices[j]); | |
if (m_meshes[i].m_ibo) { | |
glDrawElements(GL_TRIANGLES, | |
m_meshes[i].m_size, | |
GL_UNSIGNED_INT, | |
0); | |
} else { | |
glDrawArrays(GL_TRIANGLES, | |
0, | |
m_meshes[i].m_size); | |
} | |
} | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
} | |
} | |
void model::render_instanced(glsl_program &program) | |
{ | |
for (auto i=0u; i<m_meshes.size(); ++i) { | |
program.set_uniform("material.ambient", m_meshes[i].m_material.ambient); | |
program.set_uniform("material.diffuse", m_meshes[i].m_material.diffuse); | |
program.set_uniform("material.specular", m_meshes[i].m_material.specular); | |
program.set_uniform("material.shininess", m_meshes[i].m_material.shininess); | |
glBindVertexArray(m_meshes[i].m_vao); | |
if (m_meshes[i].m_ibo) { | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_meshes[i].m_ibo); | |
glDrawElementsInstanced(GL_TRIANGLES, | |
m_meshes[i].m_size, | |
GL_UNSIGNED_INT, | |
0, | |
m_model_matrices.size()); | |
} else { | |
glDrawArraysInstanced(GL_TRIANGLES, | |
0, | |
m_meshes[i].m_size, | |
m_model_matrices.size()); | |
} | |
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); | |
glBindVertexArray(0); | |
} | |
} | |
void model::set_buffers() | |
{ | |
if (glIsBuffer(m_vbos[0])) { | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[0]); | |
glBufferData(GL_ARRAY_BUFFER, | |
sizeof(glm::vec3) * m_colours.size(), | |
&m_colours[0], | |
GL_DYNAMIC_DRAW); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
} | |
if (glIsBuffer(m_vbos[1])) { | |
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[1]); | |
glBufferData(GL_ARRAY_BUFFER, | |
sizeof(glm::mat4) * m_model_matrices.size(), | |
&m_model_matrices[0], | |
GL_DYNAMIC_DRAW); | |
glBindBuffer(GL_ARRAY_BUFFER, 0); | |
} | |
} | |
} | |
namespace | |
{ | |
elixir::material extract_material(const aiScene &scene, | |
const unsigned material_index) | |
{ | |
using namespace std; | |
using namespace glm; | |
const aiMaterial &mat = *scene.mMaterials[material_index]; | |
aiColor3D colour; float value; | |
elixir::material material; | |
if (mat.Get(AI_MATKEY_COLOR_AMBIENT, colour) == AI_SUCCESS) | |
material.ambient = vec4(colour.r, colour.g, colour.b, 1); | |
if (mat.Get(AI_MATKEY_COLOR_DIFFUSE, colour) == AI_SUCCESS) | |
material.diffuse = vec4(colour.r, colour.g, colour.b, 1); | |
if (mat.Get(AI_MATKEY_COLOR_SPECULAR, colour) == AI_SUCCESS) | |
material.specular = vec4(colour.r, colour.g, colour.b, 1); | |
if (mat.Get(AI_MATKEY_SHININESS, value) == AI_SUCCESS) | |
material.shininess = value; | |
return material; | |
} | |
std::unique_ptr<elixir::model> extract_meshes(const aiScene &scene, | |
const std::string &filename) | |
{ | |
using namespace std; | |
using namespace glm; | |
vector<elixir::mesh> meshes(scene.mNumMeshes); | |
for (auto mesh_index = 0u; mesh_index < scene.mNumMeshes; ++mesh_index) { | |
auto &mesh = *scene.mMeshes[mesh_index]; | |
vector<vec3> positions(mesh.mNumVertices); | |
vector<vec3> normals(mesh.mNumVertices); | |
vector<GLuint> indices; | |
for (auto i=0u; i < mesh.mNumVertices; ++i) { | |
positions[i] = vec3(mesh.mVertices[i].x, mesh.mVertices[i].y, mesh.mVertices[i].z); | |
normals[i] = vec3(mesh.mNormals[i].x, mesh.mNormals[i].y, mesh.mNormals[i].z); | |
} | |
for (auto i=0u; i < mesh.mNumFaces; ++i) { | |
indices.push_back(mesh.mFaces[i].mIndices[0]); | |
indices.push_back(mesh.mFaces[i].mIndices[1]); | |
indices.push_back(mesh.mFaces[i].mIndices[2]); | |
} | |
meshes[mesh_index] = elixir::mesh(positions, | |
normals, | |
indices, | |
elixir::material());//extract_material(scene, mesh.mMaterialIndex)); | |
} | |
return unique_ptr<elixir::model>(new elixir::model(meshes)); | |
} | |
} | |
namespace elixir | |
{ | |
std::unique_ptr<model> load_model(const std::string &filename) | |
{ | |
Assimp::Importer importer; | |
auto scene = importer.ReadFile(filename.c_str() | |
, aiProcess_ValidateDataStructure // perform a full validation of the loader's output | |
| aiProcess_Triangulate // triangulate polygons with more than 3 edges | |
| aiProcess_ImproveCacheLocality // improve the cache locality of the output vertices | |
| aiProcess_RemoveRedundantMaterials // remove redundant materials | |
| aiProcess_FindDegenerates // remove degenerated polygons from the import | |
| aiProcess_FindInvalidData // detect invalid model data, such as invalid normal vectors | |
| aiProcess_OptimizeMeshes // join small meshes, if possible; | |
| aiProcess_SplitLargeMeshes // split large, unrenderable meshes into submeshes | |
); | |
if (scene) return extract_meshes(*scene, filename); | |
else { | |
Log << "model_facotry::load() failed read file: " << filename << "\n"; | |
return nullptr; | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "glsl_program.h" | |
#include <vector> | |
#include <tuple> | |
#include <memory> | |
#include <GL\glew.h> | |
#include <glm\glm.hpp> | |
namespace elixir | |
{ | |
struct mesh | |
{ | |
mesh(); | |
mesh(const std::vector<glm::vec3> &positions, | |
const std::vector<glm::vec3> &normals, | |
const std::vector<GLuint> &indices); | |
std::vector<GLuint> m_vbos; | |
GLuint m_ibo, m_vao; | |
GLsizei m_size; | |
}; | |
class model | |
{ | |
public: | |
model(); | |
model(const std::vector<mesh> &meshes); | |
~model(); | |
void add(const glm::mat4 &model_matrix, const glm::vec3 &colour); | |
void add(const unsigned size, const glm::vec3 &colour); | |
void set(const std::vector<glm::mat4> &model_matrices, const std::vector<glm::vec3> &colours); | |
void set_model_matrices(const unsigned size); | |
const GLuint & model_matrix_buffer() const; | |
const GLuint & colour_buffer() const; | |
void render(glsl_program &program); | |
void render_instanced(glsl_program &program); | |
private: | |
void set_buffers(); | |
std::vector<GLuint> m_vbos; | |
std::vector<mesh> m_meshes; | |
std::vector<glm::mat4> m_model_matrices; | |
std::vector<glm::vec3> m_colours; | |
}; | |
std::unique_ptr<model> load_model(const std::string &filename); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment