OpenGL中的大型模型在更高的窗口分辨率下性能会大幅下降



我目前正在用OpenGL渲染大约10个背包对象和一个巨大的sponza模型(它有大约400个网格和200万个三角形(,并在我的场景中渲染7个点光源。当窗口的分辨率为800x600时,性能还可以(60fps(,但当我将窗口设置为屏幕大小(2560x1600(时,性能会大幅下降到15-20fps左右,尤其是在浏览sponza模型时(我使用的是Assimp(。代码结构如下(简化(:

Mesh.cpp:

class Mesh {
// class that contains all vertex info (pos, normal, UV)
void draw(const Shader& shader) const {
// bind the textures + activate
// bind the vao
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
}
};

Model.cpp(唯一的工作是加载网格(:

class Model {
std::vector<Mesh> meshes;
void draw(const Shader& shader) const {
for (const auto& mesh: meshes) {
mesh.draw(shader);
}
}
};

问题是,由于sponza模型有400个网格,这相当于400个绘制调用+10个背包绘制调用,大致相当于每帧410个绘制调用。我想,为每个网格调用draw可能会导致性能下降。然而,我不确定如何解决它。也许我可以把所有的顶点数据放在一个VBO中,并进行一次draw调用,但我不完全确定如何解决这个问题。此外,sponza模型中有25种材料。

附言:我使用的是learnopeng.com的Mesh和Model实现,下面是完整的类:Model.cpp

class Model {
public:
explicit Model(const std::string &path);
~Model();
void draw(const Shader &shader);
private:
void load_model(const std::string &path);
void process_node(aiNode *node, const aiScene *scene);
Mesh process_mesh(aiMesh *mesh, const aiScene *scene);
std::vector<Texture> load_material_textures(aiMaterial *mat, aiTextureType type,
Texture::TextureType tex_type);
std::vector<Mesh> meshes;
std::vector<Texture> textures_loaded;
std::string directory;
};
#include <string>
Model::Model(const std::string &path) {
load_model(path);
}
Model::~Model() {
for (const auto &mesh : meshes) {
mesh.destroy();
}
}
void Model::draw(const Shader &shader) {
for (const auto &mesh : meshes)
mesh.draw(shader);
}
void Model::load_model(const std::string &path) {
Assimp::Importer importer;
const aiScene *scene = importer.ReadFile(path.c_str(), aiProcess_Triangulate | aiProcess_FlipUVs | aiProcess_GenSmoothNormals | aiProcess_JoinIdenticalVertices);
if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || !scene->mRootNode) {
std::cout << "ERROR::ASSIMP::" << importer.GetErrorString() << std::endl;
return;
}
directory = path.substr(0, path.find_last_of("/"));
std::cout << "load_model(" << path << ")" << std::endl;
process_node(scene->mRootNode, scene);
std::cout << meshes.size() << std::endl;
}
void Model::process_node(aiNode *node, const aiScene *scene) {
// process each mesh located at the current node
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
// the node object only contains indices to index the actual objects in the scene.
// the scene contains all the data, node is just to keep stuff organized (like relations between nodes).
aiMesh *mesh = scene->mMeshes[node->mMeshes[i]];
meshes.push_back(process_mesh(mesh, scene));
}
// after we've processed all of the meshes (if any) we then recursively process each of the children nodes
for (unsigned int i = 0; i < node->mNumChildren; i++) {
process_node(node->mChildren[i], scene);
}
}
Mesh Model::process_mesh(aiMesh *mesh, const aiScene *scene) {
std::vector<Vertex> vertices;
std::vector<Texture> textures;
std::vector<ui32> indices;
// fill in vertex data form the model info
for (int i = 0; i < mesh->mNumVertices; i++) {
Vertex vertex;
glm::vec3 temp_vec;
// get the position data
temp_vec.x      = mesh->mVertices[i].x;
temp_vec.y      = mesh->mVertices[i].y;
temp_vec.z      = mesh->mVertices[i].z;
vertex.position = temp_vec;
// get the normal data
temp_vec.x     = mesh->mNormals[i].x;
temp_vec.y     = mesh->mNormals[i].y;
temp_vec.z     = mesh->mNormals[i].z;
vertex.normals = temp_vec;
// get the uv data
if (mesh->mTextureCoords[0]) {
glm::vec2 uv_data;
uv_data.x = mesh->mTextureCoords[0][i].x;
uv_data.y = mesh->mTextureCoords[0][i].y;
vertex.uv = uv_data;
} else
vertex.uv = glm::vec2(0.0f, 0.0f);
vertices.push_back(vertex);
}
for (int i = 0; i < mesh->mNumFaces; i++) {
aiFace face = mesh->mFaces[i];
for (int j = 0; j < face.mNumIndices; j++)
indices.push_back(face.mIndices[j]);
}
if (mesh->mMaterialIndex >= 0) {
aiMaterial *mat                   = scene->mMaterials[mesh->mMaterialIndex];
std::vector<Texture> diffuse_maps = load_material_textures(mat, aiTextureType_DIFFUSE, Texture::TextureType::DIFFUSE);
textures.insert(textures.end(), diffuse_maps.begin(), diffuse_maps.end());
std::vector<Texture> specular_maps = load_material_textures(mat, aiTextureType_SPECULAR, Texture::TextureType::SPECULAR);
textures.insert(textures.end(), specular_maps.begin(), specular_maps.end());
std::vector<Texture> emission_maps = load_material_textures(mat, aiTextureType_EMISSIVE, Texture::TextureType::EMISSION);
textures.insert(textures.end(), emission_maps.begin(), emission_maps.end());
}
return Mesh(vertices, indices, textures);
}
std::vector<Texture> Model::load_material_textures(aiMaterial *mat, aiTextureType type, Texture::TextureType tex_type) {
std::vector<Texture> textures;
for (int i = 0; i < mat->GetTextureCount(type); i++) {
aiString str;
mat->GetTexture(type, i, &str);
bool skip = false;
for (int j = 0; j < textures_loaded.size(); j++) {
if (std::strcmp(textures_loaded[j].path.data(), str.C_Str()) == 0) {
textures.push_back(textures_loaded[j]);
skip = true;
break;
}
}
if (!skip) {
Texture tex = ResourceManager::load_ogl_texture_from_path(directory + "/" + str.C_Str(), tex_type);
tex.path    = str.C_Str();
textures.push_back(tex);
textures_loaded.push_back(tex);
}
}
return textures;
}

Mesh.cpp:

struct Vertex {
glm::vec3 position;
glm::vec3 normals;
glm::vec2 uv;
};
class Mesh {
public:
std::vector<Vertex> vertices;
std::vector<ui32> indices;
std::vector<Texture> textures;
Mesh(const std::vector<Vertex> &vertices, const std::vector<ui32> &indices, const std::vector<Texture> &textures);
~Mesh();
void draw(const Shader &shader) const;
void destroy() const;
private:
ui32 vao, vbo, ebo;
void setup_mesh();
};
Mesh::Mesh(const std::vector<Vertex> &vertices, const std::vector<ui32> &indices, const std::vector<Texture> &textures) : vertices(vertices), indices(indices), textures(textures) {
setup_mesh();
}
Mesh::~Mesh() {}
void Mesh::setup_mesh() {
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), &vertices[0], GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int),
&indices[0], GL_STATIC_DRAW);
// vertex positions
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), nullptr);
// vertex normals
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, normals));
// vertex texture coords
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void *) offsetof(Vertex, uv));
glBindVertexArray(0);
}
void Mesh::draw(const Shader &shader) const {
ui32 no_diffuse  = 1;
ui32 no_specular = 1;
ui32 no_emission = 1;
for (int i = 0; i < textures.size(); i++) {
glActiveTexture(GL_TEXTURE0 + i);
std::string n, base_name;
base_name = textures[i].type;
if (std::strcmp(base_name.c_str(), "texture_diffuse") == 0)
n = std::to_string(no_diffuse++);
else if (std::strcmp(base_name.c_str(), "texture_specular") == 0)
n = std::to_string(no_specular++);// transfer unsigned int to string
else if (std::strcmp(base_name.c_str(), "texture_emission") == 0)
n = std::to_string(no_emission++);// transfer unsigned int to string
shader.setInt("material." + base_name + n, i);
glBindTexture(GL_TEXTURE_2D, textures[i].id);
}
glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
glBindVertexArray(0);
glActiveTexture(GL_TEXTURE0);
}
void Mesh::destroy() const {
glDeleteVertexArrays(1, &vao);
glDeleteBuffers(1, &vbo);
glDeleteBuffers(1, &ebo);
for (const auto &texture : textures)
glDeleteTextures(1, &texture.id);
}

更多信息:渲染是简单的香草正向渲染。

400个网格和200万个三角形

400不同的网格?如果是,那就简化你的模型,这太荒谬了。如果没有,则使用实例化。400次抽签同样荒谬。

然而,当我将窗口设置为屏幕大小(2560x1600(时,的性能会大幅下降至15-20fps左右

虽然你的程序的整体架构几乎无法解决,但这句话表明,至少部分问题是你遇到了显卡的填充率限制(至少忽略了它无所事事的大量时间,因为你忙于循环浏览随机对象,一个接一个地绘制它们(。

最新更新