opengl不正确的模型渲染

opengl incorrect model rendering

我正在尝试使用 openGL 渲染一些东西。它 "works" 但错误地渲染了我的模型。我看了几个参考资料,但无法说出哪里出了问题。也许 openGL 大师可以指出我的错误?

我正在使用tinyobjloader、GLM、GLFW、GLEW等几个库,这里只介绍与渲染相关的部分。某些代码仅开发到渲染单个模型的程度。

shader.vs

#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 norm;
layout (location = 2) in vec2 texCoord;

out vec3 normal;
out vec2 TexCoord;

void main()
{
    gl_Position = vec4(position, 2.0f);
    TexCoord = texCoord;
    normal = norm;
}

shader.frag

#version 330 core

in vec3 normal;
in vec2 TexCoord;

out vec4 color;

uniform sampler2D ourTexture1;

void main()
{
    color = texture(ourTexture1, TexCoord);
}

graphics.h

#include <string>

#include <GL/glew.h>
#include <GLFW/glfw3.h>

#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>

class Shader;

class Graphics{
    public:
        Graphics(std::string, uint, uint);
        ~Graphics();
        void render();
        void clearScreen();
        void draw();
        void loadTexture(std::string);
    private:
        GLFWwindow* window = NULL;
};

class Shader{
    public:
        Shader(std::string, std::string);
        void useShader();
        GLuint shaderprogram;
};

class Mesh{
    public:
        Mesh(std::vector<glm::vec3>, std::vector<glm::vec3>, std::vector<glm::vec2>, std::vector<uint>);
        void draw();
    private:
        GLuint VBO = 0;
        GLuint VBO_tex = 0;
        GLuint VBO_normal = 0;
        GLuint VAO = 0;
        GLuint EBO = 0;
        std::vector<glm::vec3> vertices;
        std::vector<glm::vec3> normals;
        std::vector<uint> indices;
        std::vector<glm::vec2> textures;
};

Mesh loadModel(std::string);

graphics.cpp

#include <glog/logging.h>
#include "graphics.h"

#include <SOIL/SOIL.h>

#include "tiny_obj_loader.h"

#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/classification.hpp>

Graphics::Graphics(std::string name, uint w, uint h){
    glfwInit();
    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);

    window = glfwCreateWindow(w, h, name.c_str(), NULL, NULL);
    if(window == NULL){
        LOG(ERROR) << "Failed to create GLFW window";
        glfwTerminate();
    }
    glfwMakeContextCurrent(window);
    glewExperimental = GL_TRUE;
    if(glewInit() != GLEW_OK){
        LOG(ERROR) << "Failed to initialize GLEW";
    }
    glViewport(0, 0, w, h);
};

Graphics::~Graphics(){
    glfwTerminate();
};

void Graphics::render(){
    glfwSwapBuffers(window);
};

void Graphics::clearScreen(){
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};

GLuint texture;
void Graphics::loadTexture(std::string path){
    glGenTextures(1, &texture);  
    glBindTexture(GL_TEXTURE_2D, texture);

    int width, height;
    unsigned char* image = SOIL_load_image(path.c_str(), &width, &height, 0, SOIL_LOAD_RGB);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
    glGenerateMipmap(GL_TEXTURE_2D);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //SOIL_free_image_data(image);
    //glBindTexture(GL_TEXTURE_2D, 0);
}

Shader::Shader(std::string vertexstring, std::string fragmentstring){
    GLuint vertexshaderid = glCreateShader(GL_VERTEX_SHADER);
    GLuint fragmentshaderid = glCreateShader(GL_FRAGMENT_SHADER);

    const char* vertercstring = vertexstring.c_str();
    const char* fragmentcstring = fragmentstring.c_str();

    GLint success; GLchar errordata[512];

    glShaderSource(vertexshaderid, 1, &vertercstring, NULL);
    glCompileShader(vertexshaderid);

    glGetShaderiv(vertexshaderid, GL_COMPILE_STATUS, &success);
    if(!success){
        glGetShaderInfoLog(vertexshaderid, 1024, NULL, errordata);
        LOG(ERROR) << "Vertex Shader Error: " << errordata;
    };

    glShaderSource(fragmentshaderid, 1, &fragmentcstring, NULL);
    glCompileShader(fragmentshaderid);

    glGetShaderiv(fragmentshaderid, GL_COMPILE_STATUS, &success);
    if(!success){
        glGetShaderInfoLog(fragmentshaderid, 1024, NULL, errordata);
        LOG(ERROR) << "fragment Shader Error: " << errordata;
    };

    shaderprogram = glCreateProgram();
    glAttachShader(shaderprogram, vertexshaderid);
    glAttachShader(shaderprogram, fragmentshaderid);
    glLinkProgram(shaderprogram);

    glDeleteShader(vertexshaderid);
    glDeleteShader(fragmentshaderid);
};

void Shader::useShader(){
    glUseProgram(shaderprogram);
};

Mesh::Mesh(std::vector<glm::vec3> v, std::vector<glm::vec3> n, std::vector<glm::vec2> t, std::vector<uint> i){
    vertices = v;
    textures = t;
    normals = n;
    indices = i;

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * vertices.size(), &vertices[0], GL_STATIC_DRAW);

    glGenBuffers(1, &VBO_tex);
    glBindBuffer(GL_ARRAY_BUFFER, VBO_tex);
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec2) * textures.size(), &textures[0], GL_STATIC_DRAW);

    glGenBuffers(1, &VBO_normal);
    glBindBuffer(GL_ARRAY_BUFFER, VBO_normal);
    glBufferData(GL_ARRAY_BUFFER, sizeof(glm::vec3) * normals.size(), &normals[0], GL_STATIC_DRAW);

    glGenBuffers(1, &EBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(uint) * indices.size(), &indices[0], GL_STATIC_DRAW);

    glGenVertexArrays(1, &VAO);
    glBindVertexArray(VAO);

    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, VBO_normal);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, NULL);

    glEnableVertexAttribArray(2);
    glBindBuffer(GL_ARRAY_BUFFER, VBO_tex);
    glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);
};

void Mesh::draw(){
    glBindVertexArray(VAO);
    glBindTexture(GL_TEXTURE_2D, texture);
    //glDrawArrays(GL_TRIANGLES, 0, vertices.size());
    glDrawElements(GL_TRIANGLES, indices.size(), GL_UNSIGNED_INT, 0);
};

#include <iostream>
Mesh loadModel(std::string path){

    std::vector<tinyobj::shape_t> shapes;
    std::vector<tinyobj::material_t> materials;

    std::string err;
    bool ret = tinyobj::LoadObj(shapes, materials, err, path.c_str());

    std::vector<glm::vec3> vertices;
    std::vector<glm::vec3> normals;
    std::vector<glm::vec2> textures;
    std::vector<uint> indices;

    for(auto shape : shapes){
        std::cout << shape.name << std::endl;
        for(uint i = 0; i < shape.mesh.positions.size(); i+=3){
            glm::vec3 vertex;
            vertex.x = shape.mesh.positions[i];
            vertex.y = shape.mesh.positions[i+1];
            vertex.z = shape.mesh.positions[i+2];
            vertices.push_back(vertex);
        }
        for(uint i = 0; i < shape.mesh.normals.size(); i+=3){
            glm::vec3 vertex;
            vertex.x = shape.mesh.normals[i];
            vertex.y = shape.mesh.normals[i+1];
            vertex.z = shape.mesh.normals[i+2];
            normals.push_back(vertex);
        }
        for(uint i = 0; i < shape.mesh.indices.size(); i++){
            indices.push_back(shape.mesh.indices[i]);
        }
        for(uint i = 0; i < shape.mesh.texcoords.size(); i+=2){
            glm::vec2 vertex;
            vertex.x = shape.mesh.texcoords[i];
            vertex.y = shape.mesh.texcoords[i+1];
            textures.push_back(vertex);
        }
    }

    return Mesh(vertices, normals, textures, indices);
};

主-减

#include "graphics.h"
Graphics graphics("hai", 640, 480);
Shader shader(readFile("shader.vs"), readFile("shader.frag"));
shader.useShader();
graphics.loadTexture("tempTexture.jpg");
Mesh m = loadModel("treeStump.obj");
graphics.clearScreen();
shader.useShader();
m.draw();
graphics.render();

picture of failed render

如您所见,纹理未正确绘制在模型上,但它在搅拌机和我已将其加载到的其他程序中工作。下一张图片的角度不同(在搅拌机中),但它应该是这样的。

picture of correct render

如果我需要提供更多信息请告诉我!谢谢!

您的纹理坐标有 2 个分量,这是典型的:

glm::vec2 vertex;
vertex.x = shape.mesh.texcoords[i];
vertex.y = shape.mesh.texcoords[i+1];
textures.push_back(vertex);

但这与您指定属性的方式不匹配:

glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, VBO_tex);
glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, NULL);

glVertexAttribPointer() 的第二个参数指定属性中的组件数。这样的话,应该是2来匹配数据:

glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 0, NULL);

此外,如果您完全关心性能或功耗,则应避免不必要地复制数据。我仅在您的代码中计算了大约 4 个顶点数据副本。在 OBJ 解析器中添加副本,以及由 OpenGL 驱动程序制作的副本,最终您将在渲染之前复制整个顶点数据大约 7 次。那是很多浪费的电子...