简体   繁体   English

使用VAO / VBO的OpenGL模型/纹理渲染

[英]OpenGL Model/Texture rendering using VAO/VBO

I am trying to render 3D models with textures using Assimp . 我正在尝试使用Assimp渲染带有纹理的3D模型。 The conversion goes perfect, all textures positions and what not gets loaded. 转换非常完美,所有纹理位置以及未加载的位置。 I have tested the texture images by drawing them to the screen in 2D. 我已经通过将纹理图像绘制到2D屏幕上来测试它们。 For some reason it does not render the textures to the model. 由于某些原因,它不会将纹理渲染到模型。 I am a beginner in OpenGL so forgive me if i dont explain it right. 我是OpenGL的初学者,所以如果我对它的解释不正确,请原谅我。 The tutorial I have based the code on is from here , but i stripped a big part since I have my own camera/movement system. 我基于代码的教程是从这里开始的 ,但是由于我拥有自己的摄像头/运动系统,因此我省下了很大一部分。

The model renders like this: http://i.stack.imgur.com/5sK9K.png 该模型如下所示: http : //i.stack.imgur.com/5sK9K.png

whilest the texture in use looks like this: http://i.stack.imgur.com/sWGp7.jpg 使用中的纹理看起来像这样: http ://i.stack.imgur.com/sWGp7.jpg

The relevant rendering code is the following: 相关的渲染代码如下:

Generating textures from data file: 从数据文件生成纹理:

int Mesh::LoadGLTextures(const aiScene* scene){
  if (scene->HasTextures()) return -1; //yes this is correct
  /* getTexture Filenames and Numb of Textures */
  for (unsigned int m = 0; m<scene->mNumMaterials; m++){
    int texIndex = 0;

    aiReturn texFound;
    aiString path;  // filename

    while ((texFound = scene->mMaterials[m]->GetTexture(aiTextureType_DIFFUSE, texIndex, &path)) == AI_SUCCESS){
        textureIdMap[path.data] = NULL; //fill map with textures, pointers still NULL yet
        texIndex++;
    }
  }

  int numTextures = textureIdMap.size();
  /* create and fill array with GL texture ids */
  GLuint* textureIds = new GLuint[numTextures];

  /* get iterator */
  std::map<std::string, GLuint>::iterator itr = textureIdMap.begin();

  std::string basepath = getBasePath(path);

  ALLEGRO_BITMAP *image;
  for (int i = 0; i<numTextures; i++){
    std::string filename = (*itr).first;  // get filename
    (*itr).second = textureIds[i];    // save texture id for filename in map
    itr++;                                // next texture

    std::string fileloc = basepath + filename;  /* Loading of image */
    image = al_load_bitmap(fileloc.c_str());

    if (image) /* If no error occured: */{
        GLuint texId = al_get_opengl_texture(image);

        //glGenTextures(numTextures, &textureIds[i]); /* Texture name generation */
        glBindTexture(GL_TEXTURE_2D, texId); /* Binding of texture name */
        //redefine standard texture values
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* We will use linear
                                                                          interpolation for magnification filter */
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); /* We will use linear
                                                                          interpolation for minifying filter */
        textureIdMap[filename] = texId;
    } else {
        /* Error occured */
        std::cout << "Couldn't load Image: " << fileloc.c_str() << "\n";
    }
  }

  //Cleanup
  delete[] textureIds;

  //return success
  return true;
}

Generating VBO/VAO: 生成VBO / VAO:

void Mesh::genVAOsAndUniformBuffer(const aiScene *sc) {
  struct MyMesh aMesh;
  struct MyMaterial aMat;
  GLuint buffer;

  // For each mesh
  for (unsigned int n = 0; n < sc->mNumMeshes; ++n){
    const aiMesh* mesh = sc->mMeshes[n];

    // create array with faces
    // have to convert from Assimp format to array
    unsigned int *faceArray;
    faceArray = (unsigned int *)malloc(sizeof(unsigned int) * mesh->mNumFaces * 3);
    unsigned int faceIndex = 0;

    for (unsigned int t = 0; t < mesh->mNumFaces; ++t) {
        const aiFace* face = &mesh->mFaces[t];

        memcpy(&faceArray[faceIndex], face->mIndices, 3 * sizeof(unsigned int));
        faceIndex += 3;
    }
    aMesh.numFaces = sc->mMeshes[n]->mNumFaces;

    // generate Vertex Array for mesh
    glGenVertexArrays(1, &(aMesh.vao));
    glBindVertexArray(aMesh.vao);

    // buffer for faces
    glGenBuffers(1, &buffer);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * mesh->mNumFaces * 3, faceArray, GL_STATIC_DRAW);

    // buffer for vertex positions
    if (mesh->HasPositions()) {
        glGenBuffers(1, &buffer);
        glBindBuffer(GL_ARRAY_BUFFER, buffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mVertices, GL_STATIC_DRAW);
        glEnableVertexAttribArray(vertexLoc);
        glVertexAttribPointer(vertexLoc, 3, GL_FLOAT, 0, 0, 0);
    }

    // buffer for vertex normals
    if (mesh->HasNormals()) {
        glGenBuffers(1, &buffer);
        glBindBuffer(GL_ARRAY_BUFFER, buffer);
        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3 * mesh->mNumVertices, mesh->mNormals, GL_STATIC_DRAW);
        glEnableVertexAttribArray(normalLoc);
        glVertexAttribPointer(normalLoc, 3, GL_FLOAT, 0, 0, 0);
    }

    // buffer for vertex texture coordinates
    if (mesh->HasTextureCoords(0)) {
        float *texCoords = (float *)malloc(sizeof(float) * 2 * mesh->mNumVertices);
        for (unsigned int k = 0; k < mesh->mNumVertices; ++k) {
            texCoords[k * 2] = mesh->mTextureCoords[0][k].x;
            texCoords[k * 2 + 1] = mesh->mTextureCoords[0][k].y;
        }

        glGenBuffers(1, &buffer);
        glEnableVertexAttribArray(texCoordLoc);
        glBindBuffer(GL_ARRAY_BUFFER, buffer);

        glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 2 * mesh->mNumVertices, texCoords, GL_STATIC_DRAW);
        glVertexAttribPointer(texCoordLoc, 2, GL_FLOAT, GL_FALSE, 0, 0);
    }

    // unbind buffers
    glBindVertexArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);

    // create material uniform buffer
    aiMaterial *mtl = sc->mMaterials[mesh->mMaterialIndex];

    aiString texPath;   //contains filename of texture
    if (AI_SUCCESS == mtl->GetTexture(aiTextureType_DIFFUSE, 0, &texPath)){
        //bind texture
        unsigned int texId = textureIdMap[texPath.data];
        aMesh.texIndex = texId;
        aMat.texCount = 1;
    } else {
        aMat.texCount = 0;
    }

    float c[4];
    set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
    aiColor4D diffuse;
    if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
        color4_to_float4(&diffuse, c);
    memcpy(aMat.diffuse, c, sizeof(c));

    set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
    aiColor4D ambient;
    if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
        color4_to_float4(&ambient, c);
    memcpy(aMat.ambient, c, sizeof(c));

    set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
    aiColor4D specular;
    if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
        color4_to_float4(&specular, c);
    memcpy(aMat.specular, c, sizeof(c));

    set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
    aiColor4D emission;
    if (AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
        color4_to_float4(&emission, c);
    memcpy(aMat.emissive, c, sizeof(c));

    float shininess = 0.0;
    unsigned int max;
    aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
    aMat.shininess = shininess;

    glGenBuffers(1, &(aMesh.uniformBlockIndex));
    glBindBuffer(GL_UNIFORM_BUFFER, aMesh.uniformBlockIndex);
    glBufferData(GL_UNIFORM_BUFFER, sizeof(aMat), (void *)(&aMat), GL_STATIC_DRAW);

    myMeshes.push_back(aMesh);
  }
}

Rendering model: 渲染模型:

void Mesh::recursive_render(const aiScene *sc, const aiNode* nd){
  // draw all meshes assigned to this node
  for (unsigned int n = 0; n < nd->mNumMeshes; ++n){
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, myMeshes[nd->mMeshes[n]].texIndex);

    // bind VAO
    glBindVertexArray(myMeshes[nd->mMeshes[n]].vao);

    // draw
    glDrawElements(GL_TRIANGLES, myMeshes[nd->mMeshes[n]].numFaces * 3, GL_UNSIGNED_INT, 0);
  }

  // draw all children
  for (unsigned int n = 0; n < nd->mNumChildren; ++n){
      recursive_render(sc, nd->mChildren[n]);
  }
}

Any other relevant code parts can be found in my open github project https://github.com/kwek20/StrategyGame/tree/master/Strategy 其他任何相关代码部分都可以在我打开的github项目https://github.com/kwek20/StrategyGame/tree/master/Strategy中找到

Mesh.cpp is relevant, as well as main.cpp and Camera.cpp. Mesh.cpp以及main.cpp和Camera.cpp是相关的。

As far as I understaind I followed the guidelines well, created a VAO, created VBOs, added data and enabled the proper vertex array attriute tot render the scene with. 据我所知,我完全遵循指南,创建了VAO,创建了VBO,添加了数据并启用了适当的顶点数组以渲染场景。

I have checked all the data variables and everything is filled according to plan 我检查了所有数据变量,并按计划填充了所有内容

Could anyone here spot the mistake I have made and or explain it? 在座的任何人都可以发现我所犯的错误并加以解释吗? Some links are typed weird because of the limit I have :( 由于我的限制,有些链接的类型很奇怪:(

It would help if you posted your shaders also. 如果您还发布了着色器,将会有所帮助。 I can post some rendering code with textures if that helps you out: Generating the texture for opengl and loading a grayscale (UC8) image with width and height into the GPU 如果可以帮助我,我可以发布一些带有纹理的渲染代码:为opengl生成纹理并将带有宽度和高度的灰度(UC8)图像加载到GPU中

void GLRenderer::getTexture(unsigned char * image, int width, int height)
{
    glActiveTexture(GL_TEXTURE0);
    glGenTextures(1, &mTextureID);
    glBindTexture(GL_TEXTURE_2D, mTextureID);
    glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGB8, width, height);
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_BGR, GL_UNSIGNED_BYTE, image);

    if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
    glBindTexture(GL_TEXTURE_2D, 0);
}

Loading the vertices from assimp onto the gpu 将顶点从assimp加载到GPU

    //** buffer a obj file-style model, initialize the VAO
void GLRenderer::bufferModel(float* aVertexArray, int aNumberOfVertices, float* aNormalArray, int aNumberOfNormals, float* aUVList, int aNumberOfUVs, unsigned int* aIndexList, int aNumberOfIndices)
{
    //** just to be sure we are current
    glfwMakeContextCurrent(mWin);

    //** Buffer all data in VBOs
    glGenBuffers(1, &mVertex_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfVertices * 3, aVertexArray, GL_STATIC_DRAW);
    glGenBuffers(1, &mNormal_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfNormals * 3, aNormalArray, GL_STATIC_DRAW);
    glGenBuffers(1, &mUV_buffer_object);
    glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
    glBufferData(GL_ARRAY_BUFFER, sizeof(float) * aNumberOfUVs * 2, aUVList, GL_STATIC_DRAW);
    glGenBuffers(1, &mIndex_buffer_object);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * aNumberOfIndices, aIndexList, GL_STATIC_DRAW);

    if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);

    //** VAO tells our shaders how to match up data from buffer to shader input variables
    glGenVertexArrays(1, &mVertex_array_object);
    glBindVertexArray(mVertex_array_object);

    //** vertices first
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, mVertex_buffer_object);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    //** normals next
    if (aNumberOfNormals > 0){
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, mNormal_buffer_object);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);
    }
    //** UVs last
    if (aNumberOfUVs > 0){
    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, mUV_buffer_object);
    glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);
    }
    //** indexing for reusing vertices in triangle-meshes
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIndex_buffer_object);


    //** check errors and store the number of vertices
    if (aux::checkGlErrors(__LINE__, __FILE__))assert(false);
    mNumVert = aNumberOfVertices;
    mNumNormals = aNumberOfNormals;
    mNumUVs = aNumberOfUVs;
    mNumIndices = aNumberOfIndices;

}

The code above is called like: 上面的代码称为:

//read vertices from file
std::vector<float> vertex, normal, uv;
std::vector<unsigned int> index;
//assimp-wrapping function to load obj to vectors
aux::loadObjToVectors("Resources\\vertices\\model.obj", vertex, normal, index, uv);
mPtr->bufferModel(&vertex[0], static_cast<int>(vertex.size()) / 3, &normal[0], static_cast<int>(normal.size()) / 3, &uv[0], static_cast<int>(uv.size()) / 2, &index[0], static_cast<int>(index.size()));

Then comes the shader-part: In the vertex shader you just hand-through the UV-coordinate layer 然后是着色器部分:在顶点着色器中,您只需穿过UV坐标层

#version 400 core
layout (location = 0) in vec3 vertexPosition_modelspace;
layout (location = 1) in vec3 vertexNormal_modelspace;
layout (location = 2) in vec2 vertexUV;

out vec2 UV;

[... in main then ...]
UV = vertexUV;

While in the fragment shader you assign the value to the pixel: #version 400 core in vec2 UV; 在片段着色器中,您可以将值分配给像素:vec2 UV中的#version 400 core; uniform sampler2D textureSampler; 均匀采样器2D textureSampler; layout(location = 0) out vec4 outColor; 布局(位置= 0)输出vec4 outColor; [... in main then ...] // you probably want to calculate lighting here then too, so its just the simplest way to get the texture inside outColor = vec4(texture2D(textureSampler, UV).rgb, cosAngle); [... in main then ...] //您可能也想在这里计算光照,因此这是在outColor = vec4(texture2D(textureSampler,UV).rgb,cosAngle)内获取纹理的最简单方法。 //you can also check whether the UV coords are correctly bound by using: outColor = vec4(UV.x, UV.y,1,1); //也可以使用以下方法检查UV坐标是否正确绑定:outColor = vec4(UV.x,UV.y,1,1); //and then checking the pixel-values in the resulting image (eg render it to a PBO and then download it onto the CPU for) //然后检查结果图像中的像素值(例如,将其渲染到PBO,然后将其下载到CPU上)

In the rendering loop also make sure that all the uniforms are correctly bound (especially texture related ones) and that the texture is active and bound 在渲染循环中,还请确保正确地绑定了所有制服(尤其是与纹理相关的制服),并且纹理已激活并绑定

if (mTextureID != -1) {
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, mTextureID); 
}

GLint textureLocation = glGetUniformLocation(mShaderProgram, "textureSampler");
glUniform1i(textureLocation, 0);
//**set the poligon mode
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//**drawElements because of indexing
glDrawElements(GL_TRIANGLES, mNumIndices, GL_UNSIGNED_INT, 0);

I hope I could help you! 希望能对您有所帮助! Kind regards, VdoP 亲切的问候,VdoP

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM