OpenGL 3.1 使用索引绘图渲染纹理时出现问题

OpenGL 3.1 Issue rendering textures with indexed drawing

本文关键字:纹理 问题 绘图 索引 OpenGL      更新时间:2023-10-16

我正在尝试使用 glDrawElements 绘制从 assimp 加载的模型,几何图形显示良好,但纹理没有显示在模型上,我只是得到了我加载的模型的黑色版本。

荷载模型函数

ModelInfo LoadModel(const std::string& modelPath){
printf( "Loading model: %sn", modelPath.c_str());
//verify that file exists first
std::ifstream fin(modelPath.c_str());
if(!fin.fail()){
    fin.close();
}else{
    throw std::runtime_error("could not open file" + modelPath);
}
Assimp::Importer importer;
const aiScene* scene = importer.ReadFile( modelPath,
        aiProcessPreset_TargetRealtime_Fast |
        //aiProcess_CalcTangentSpace      |
        aiProcess_Triangulate           |
        aiProcess_GenSmoothNormals      |
        aiProcess_FlipUVs
    //aiProcess_JoinIdenticalVertices |
    //aiProcess_SortByPType);
);
if(!scene){
    throw std::runtime_error(importer.GetErrorString());
}
printf("imported %sn",modelPath.c_str());
fflush(stdout);
std::vector<unsigned int> indices;
std::vector<float> vertices;
std::vector<float> uvs;
std::vector<float> normals;
aiMesh* mesh = scene->mMeshes[0];
int numOfFaces = mesh->mNumFaces;
int numOfIndices = numOfFaces * 3;
indices.resize(numOfIndices);
for (unsigned int i =0; i < mesh->mNumFaces; ++i){
    const aiFace &face = mesh->mFaces[i];
    assert(face.mNumIndices == 3);
    indices[i * 3 + 0] = face.mIndices[0];
    indices[i * 3 + 1] = face.mIndices[1];
    indices[i * 3 + 2] = face.mIndices[2];
}
int numOfVertices = mesh->mNumVertices;
vertices.resize(numOfVertices * 3);
normals.resize(numOfVertices * 3);
uvs.resize(numOfVertices * 2);
for( unsigned int i = 0; i < mesh->mNumVertices; ++i){
    if(mesh->HasPositions()){
        vertices[i * 3 + 0] = mesh->mVertices[i].x;
        vertices[i * 3 + 1] = mesh->mVertices[i].y;
        vertices[i * 3 + 2] = mesh->mVertices[i].z;
        //printf("[ %f, %f, %f]n",vertices[i*3+0],vertices[i*3+1],vertices[i*3+2]);
    }

    if( mesh->HasNormals()){
        normals[i * 3 + 0] = mesh->mNormals[i].x;
        normals[i * 3 + 1] = mesh->mNormals[i].x;
        normals[i * 3 + 2] = mesh->mNormals[i].x;
    }
    if(mesh->HasTextureCoords(0)){
        uvs[i * 2 + 0] = mesh->mTextureCoords[0][i].x;
        uvs[i * 2 + 1] = mesh->mTextureCoords[0][i].y;
        printf("[ %f, %f]n",uvs[i*2+0],uvs[i*2+1]);
    }
}
//create voa
GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
//create element buffer
GLuint elementBuffer;
glGenBuffers(1, &elementBuffer);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, elementBuffer);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(unsigned int), &indices[0], GL_STATIC_DRAW);
//create vertex buffer
GLuint vertexBuffer;
glGenBuffers(1, &vertexBuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(aiVector3D), &vertices[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
printf("vertices.size is %lun", vertices.size());
printf("uvs.size is %lun", uvs.size());
GLuint uvBuffer;
glGenBuffers(1, &uvBuffer);
glBindBuffer(GL_ARRAY_BUFFER, uvBuffer);
glBufferData(GL_ARRAY_BUFFER, uvs.size() * sizeof(aiVector2D), &uvs[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 2*sizeof(float), (void*)0);
GLuint normalBuffer;
glGenBuffers(1, &normalBuffer);
glBindBuffer(GL_ARRAY_BUFFER, normalBuffer);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(aiVector3D), &normals[0], GL_STATIC_DRAW);
glEnableVertexAttribArray(2);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
ModelInfo retval;
retval.vao    = vao;
retval.index  = elementBuffer;
retval.vertex = vertexBuffer;
retval.uv     = uvBuffer;
retval.normal = normalBuffer;
retval.count  = numOfIndices;
return retval;

}

加载模型的简短概述,使用 assimp 加载模型,然后将来自 assimp 场景中第一个网格的数据加载到向量中,然后为几何体、纹理坐标、法线和索引创建 vao、vbos。然后将数据从矢量加载到缓冲区中,并设置顶点属性。加载完所有内容后,将创建一个包含多个 GLuint 的结构来返回数据。

渲染模型功能

void render_model(ModelInfo info){
cleanup();
//set program
glUseProgram(modelprogram);
//set uniforms
glm::mat4 view = camera.matrix();
glUniformMatrix4fv(modelcamera, 1, GL_FALSE, &view[0][0]);
glm::mat4 model = glm::mat4();
model = glm::rotate(model, degree, glm::vec3(0,1,1));
glUniformMatrix4fv(modelmodel, 1, GL_FALSE, &model[0][0]);
glBindVertexArray(info.vao);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(modeltext, texture);
glDrawElements(GL_TRIANGLES, info.count, GL_UNSIGNED_INT, (void*)0);
}

渲染模型的简要概述。设置着色器程序以处理模型的渲染,获取相机的矩阵,将其传递给着色器,为模型生成矩阵,使其旋转,将其传递给着色器。绑定要渲染的模型的 vao。绑定以前加载的文本,该文本也用于程序中显示没有问题的其他几何体,将其传递给着色器。全部设置好,从当前 vao 索引缓冲区中的第一个位置开始调用 glDrawElements。绘制完成后,进行清理以取消绑定任何缓冲区和数组

我为此使用的着色器是非常基本的顶点着色器

#version 140
uniform mat4 camera;
uniform mat4 model;
in vec3 position;
in vec2 uv;
out vec2 fragUV;
void main(){
    //pass variables on to fragment shader
    fragUV   = uv;
    //vertex to draw
    gl_Position = camera * model * vec4(position,1);
}

片段着色器 #version 140

uniform sampler2D modeltext;
in vec2 fragUV;
out vec4 finalColor;
void main(){
    finalColor = texture(modeltext, fragUV);
}

所有制服都正确加载了一个功能,该功能可以验证某些东西是否实际加载,纹理被使用并在程序中的其他位置工作。模型具有纹理坐标。几何体正在加载和渲染没有问题

您将错误的值传递给采样器制服。

glBindTexture (GL_TEXTURE_2D, texture);
glUniform1i   (modeltext,     texture);

采样器制服不采用纹理对象的名称 (ID),它们采用将纹理绑定到的纹理图像单元的索引。在此示例中,您似乎使用的是默认的纹理图像单元:GL_TEXTURE0 ,因此采样器制服应使用的值为 0

由于sampler2D实际上需要纹理图像单元而不是纹理的名称,因此在更改绑定纹理时,永远不必更改此制服。GLSL 将始终对绑定到相应纹理图像单元的任何纹理进行采样(假设它们具有兼容的类型/状态)。

这意味着通常在初始化 GLSL 程序时设置一次采样器制服,并且永远不会再触摸它。在较新版本的 GLSL (4.20) 中,您甚至可以对采样器在着色器本身中使用的纹理图像单元进行硬编码,但由于您使用的是 GLSL 1.40,因此除非支持扩展名:GL_ARB_shading_language_420pack,否则您没有此选项。


请考虑以下代码,该代码正确演示了如何使用采样器制服:

glActiveTexture (GL_TEXTURE0);
glBindTexture   (GL_TEXTURE_2D, texture);
glUniform1i     (modeltext,     0);

我包含了对glActiveTexture (...)的冗余调用,只是为了演示均匀采样器和活动纹理单元之间的关系......您不会将常量GL_TEXTURE0传递给采样器,而是使用整数索引。 GL_TEXTURE0 是默认的活动纹理单位。