简体   繁体   English

波前 OBJ 模型未渲染 - OpenGL C++

[英]Wavefront OBJ Model Not Rendering - OpenGL C++

I can't seem to render an OBJ model using glDrawElements in C++.我似乎无法在 C++ 中使用glDrawElements渲染 OBJ 模型。

Here is my code:这是我的代码:

#include <iostream>
#include <stdio.h>
#include <vector>
#include <fstream>
#include "Object.h"

#define BUFFER_OFFSET(i) ((char *)NULL + (i))


class Model : public object
{
private:
    struct  Vertex
    {
        CBfloat v1, v2, v3;
        CBfloat vn1, vn2, vn3;
        CBfloat vt1, vt2;

        Vertex(vec3 _position, vec3 _normal, vec2 _texCoord)
        {
            v1 = _position[0];
            v2 = _position[1];
            v3 = _position[2];
            vn1 = _normal[0];
            vn2 = _normal[1];
            vn3 = _normal[2];
            vt1 = _texCoord[0];
            vt2 = _texCoord[1];
        }
    };

    struct  Face
    {
        CBuint v[3];
        CBuint vt[3];
        CBuint vn[3];

        CBuint mat_id;

        Face(CBuint v1, CBuint vt1, CBuint vn1, CBuint v2, CBuint vt2, CBuint vn2, CBuint v3, CBuint vt3, CBuint vn3) : mat_id(-1)
        {
            v[0] = v1;
            vt[0] = vt1;
            vn[0] = vn1;
            v[1] = v2;
            vt[1] = vt2;
            vn[1] = vn2;
            v[2] = v3;
            vt[2] = vt3;
            vn[2] = vn3;
        }
    };

    struct  Mesh
    {
        CBuint vbo;
        CBuint ebo;

        Mesh() {}
        Mesh(std::vector <Vertex>& v, std::vector <CBuint>& i)
        {
            // Generate
            glGenBuffers(1, &vbo);
            glGenBuffers(1, &ebo);

            // Bind
            glBindBuffer(GL_ARRAY_BUFFER, vbo);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);

            // Buffer
            glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(Vertex), &v[0], GL_STATIC_DRAW);
            glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(CBuint) * 3, &i[0], GL_STATIC_DRAW);

            // Set attribs
            glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 3));
            glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 5));

            // Enable attribs
            glEnableVertexAttribArray(0);
            glEnableVertexAttribArray(1);
            glEnableVertexAttribArray(2);

            // Clean up
            glBindVertexArray(0);
        }
        ~Mesh()
        {
            glDeleteBuffers(1, &vbo);
            glDeleteBuffers(1, &ebo);
        }
    };

    // Local variables
    CBuint                      _vao;
    bool                        _materialised;
    std::string                 _obj_url;
    std::string                 _mtl_url;
    std::vector <std::string>   _material_element;
    std::vector <Vertex>        _vertices;
    std::vector <CBuint>        _indices;
    std::vector <Mesh*>         _meshes;
    std::vector <Material*>     _materials;

public:
    Model(Shader* shader) : _materialised(false) {}
    Model(Shader* shader, const char* obj_file) : _materialised(false)
    {
        // Load OBJ file
        if (loadOBJ(obj_file) != 1)
            std::cout << "Error: Failed to load OBJ!" << std::endl;

        // Load MTL file
        if (_materialised)
        {
            if (loadMTL(shader, _mtl_url.c_str()) != 1)
                std::cout << "Error: Failed to load MTL file!" << std::endl;
        }

        // Apply material if none were applied
        if (!_materialised)
        {
            _materials.push_back(new Material(shader, "default_m",false, false, 1.0f, vec3(1.0f, 1.0f, 1.0f), vec3(0.25f, 0.25f, 0.25f), 1.0f, 8.0f, 1.0f));
            _materialised = true;
        }

        // Initialise matrix
        initialiseMatrix(shader);
    }

    ~Model()
    {
        if (!_indices.empty())
            clear();

        glDeleteVertexArrays(1, &_vao);
    }

    inline CBvoid clear()
    {
        _vertices.clear();
        _indices.clear();
        _materials.clear();
        _meshes.clear();
    }

    inline CBint loadOBJ(const char* file)
    {
        this->_obj_url = file;

        // Check for valid file
        std::ifstream _in(file);
        if (!_in)
        {
            std::cout << "Error: OBJ file is invalid!" << std::endl;
            return -1;
        }
        else if (!_in.is_open())
        {
            std::cout << "Error: Cannot open OBJ file!" << std::endl;
            return -1;
        }

        // Get data
        char data[256];
        std::vector <std::string> line;
        while (_in.getline(data, 256))
            line.push_back(data);

        // Assign vertex data
        std::vector <vec3>  _v;
        std::vector <vec3>  _vn;
        std::vector <vec2>  _vt;
        std::vector <Face>  _f;
        for (unsigned int i = 0; i < line.size(); i++)
        {
            switch ((line[i])[0])
            {
            case '#':
                continue;
            case '\0':
                continue;
            case 'm':
                char mtl_url[128];
                sscanf_s((&line[i])[0].c_str(), "mtllib %s", mtl_url, sizeof(mtl_url));
                _mtl_url = mtl_url;
                _materialised = true;
                break;
            case 'v':
                float x, y, z;
                if ((line[i])[1] == 'n')
                {
                    sscanf_s((&line[i])[0].c_str(), "vn %f %f %f", &x, &y, &z);
                    _vn.push_back(vec3(x, y, z));
                }
                if ((line[i])[1] == 't')
                {
                    sscanf_s((&line[i])[0].c_str(), "vt %f %f", &x, &y);
                    _vt.push_back(vec2(x, y));
                }
                else if ((line[i])[1] == ' ')
                {
                    sscanf_s((&line[i])[0].c_str(), "v %f %f %f", &x, &y, &z);
                    _v.push_back(vec3(x, y, z));
                }
                break;
            case 'u':
                char material_element[128];
                sscanf_s((&line[i])[0].c_str(), "usemtl %s", &material_element, sizeof(material_element));

                // Assign new material to element
                if (_material_element.size() > 1)
                    _f[_f.size() - 1].mat_id = _material_element.size();

                _material_element.push_back(material_element);
                break;
            case 'f':
                CBuint v_i[3];
                CBuint vn_i[3];
                CBuint vt_i[3];

                sscanf_s((&line[i])[0].c_str(), "f %d/%d/%d %d/%d/%d %d/%d/%d", &v_i[0], &vt_i[0], &vn_i[0], &v_i[1], &vt_i[1], &vn_i[1], &v_i[2], &vt_i[2], &vn_i[2]);

                // Faces
                _f.push_back(Face(v_i[0], vt_i[0], vn_i[0], v_i[1], vt_i[1], vn_i[1], v_i[2], vt_i[2], vn_i[2]));

                // Indices
                _indices.push_back(v_i[0] - 1);
                _indices.push_back(vt_i[0] - 1);
                _indices.push_back(vn_i[0] - 1);

                _indices.push_back(v_i[1] - 1);
                _indices.push_back(vt_i[1] - 1);
                _indices.push_back(vn_i[1] - 1);

                _indices.push_back(v_i[2] - 1);
                _indices.push_back(vt_i[2] - 1);
                _indices.push_back(vn_i[2] - 1);

                break;
            }
        }


        // Optimise vertices
        for (CBuint i = 0; i < _v.size(); i++)
            _vertices.push_back(Vertex(_v[i], vec3(0.0f, 0.0f, 0.0f), vec2(1.0f, 1.0f)));

        // Optimise buffers
        CBuint next_element = 0;
        glGenVertexArrays(1, &_vao);
        glBindVertexArray(_vao);
        if (_materials.size() > 1)
        {
            for (CBuint i = 0; i < _f.size(); i++)
            {
                if (_f[i].mat_id == next_element)
                {
                    _meshes.push_back(new Mesh(_vertices, _indices));
                    next_element++;
                }
            }
        }
        else
            _meshes.push_back(new Mesh(_vertices, _indices));
        glBindVertexArray(0);


        // Output
        std::cout << "v: " << _v.size() << std::endl;
        std::cout << "vn: " << _vn.size() << std::endl;
        std::cout << "vt: " << _vt.size() << std::endl;
        std::cout << "f: " << _f.size() << std::endl;
        std::cout << "vertices: " << _vertices.size() << std::endl;
        std::cout << "indices: " << _indices.size() << std::endl;
        std::cout << "meshes: " << _meshes.size() << std::endl;
        std::cout << "materials: " << _materials.size() << std::endl;

        // Close file and return
        _in.close();
        return 1;
    }

    inline CBint loadMTL(Shader* shader, const char* file)
    {
        // ----------------- MTL pipeline -----------------
        // ----------------------------------------------------
        // Ns - specular exponent multiplied by the texture value
        // d  - dissolve multiplied by the texture value
        // Ka - ambient colour multiplied by the texture value
        // Kd - diffuse colour multiplied by the texture value
        // Ks - specular colour multiplied by the texture value
        // Ke - emissive colour multiplied by the texture value
        // map_* - texture map multiplied by the texture value
        // bump - normal map multiplied by the texture value


        // Check file
        std::ifstream in_file(file);
        if (!in_file)
        {
            std::cout << "Error: MTL file is invalid!" << std::endl;
            return -1;
        }
        else if (!in_file.is_open())
        {
            std::cout << "Error: MTL file failed to open!" << std::endl;
            return -1;
        }

        // Push_back materials from elements in obj
        for (unsigned int i = 0; i < _material_element.size(); i++)
            _materials.push_back(new Material(shader, _material_element[i], false, false, 1.0f, vec3(1.0f, 1.0f, 1.0f), vec3(0.5f, 0.5f, 0.5f), 0.75f, 8.0f, 1.0f));

        // Get data
        char data[256];
        std::vector <std::string> line;
        while (in_file.getline(data, 256))
            line.push_back(data);

        // Assign data
        CBint  current_material_element = -1;
        for (unsigned int i = 0; i < line.size(); i++)
        {
            switch ((line[i])[0])
            {
            case '#':
                continue;
            case '\0':
                continue;
            case 'n':
                current_material_element++;
                break;
            case '\t':
                if ((line[i])[1] == 'N' && (line[i])[2] == 's')
                {
                    float _Ns = 0.0f;
                    sscanf_s((&line[i])[0].c_str(), "\tNs %f", &_Ns);
                    _materials[current_material_element]->_specular_damper = _Ns;
                }
                else if ((line[i])[1] == 'd')
                {
                    float _d = 0.0f;
                    sscanf_s((&line[i])[0].c_str(), "\td %f", &_d);
                    _materials[current_material_element]->_opacity = _d;
                }
                else if ((line[i])[1] == 'K' && (line[i])[2] == 'd')
                {
                    vec3 _Kd = vec3(0.0f);
                    sscanf_s((&line[i])[0].c_str(), "\tKd %f %f %f", &_Kd.data[0], &_Kd.data[1], &_Kd.data[2]);
                    _materials[current_material_element]->_diffuse = _Kd;
                }
                else if ((line[i])[1] == 'K' && (line[i])[2] == 's')
                {
                    vec3 _Ks = vec3(0.0f);
                    sscanf_s((&line[i])[0].c_str(), "\tKs %f %f %f", &_Ks.data[0], &_Ks.data[1], &_Ks.data[2]);
                    _materials[current_material_element]->_specular = _Ks;
                }
                break;
            }
        }

        return 1;
    }

    virtual void update(double delta)
    {
        tickMatrix();

        // TEMP!
        _rotation = vec4(20.0f, 0.0f, 1.0f, 0.0f);
    }

    virtual void render()
    {
        glUniformMatrix4fv(_u_model, 1, GL_FALSE, _model);

        _materials[0]->bind();

        glBindVertexArray(_vao);
        glDrawElements(GL_TRIANGLES, _indices.size(), GL_UNSIGNED_INT, 0);
        glBindVertexArray(0);
    }
};

I can render a simple triangle when hard-coding the vertices and indices, so it can't be a shader problem.在对顶点和索引进行硬编码时,我可以渲染一个简单的三角形,因此它不可能是着色器问题。 I've thoroughly checked through the index and vertex optimisation part and can't see anything wrong with it, even after comparing with other examples.我已经彻底检查了索引顶点优化部分,即使与其他示例进行了比较,也看不出有什么问题。

Any ideas, corrections, solutions or whatnot?任何想法,更正,解决方案或诸如此类的东西?

Edit: The cube model renders but now I've come across a new problem - the indices seem to be scrambled with double cross-overs when rendering the triangles:编辑:立方体模型渲染,但现在我遇到了一个新问题 - 渲染三角形时,索引似乎被双重交叉打乱:

Click me for screenshot!点我截图!

And here is the same cube.obj model but in lit mode:这是相同的 cube.obj 模型,但处于点亮模式:

Click to see in lit mode!单击以在点亮模式下查看!

Vertex shader code:顶点着色器代码:

#version 330 core

// Vertex attributes
layout(location = 0) in vec3 position;
layout(location = 1) in vec3 normal;
layout(location = 2) in vec2 texCoord;

// Node parses
out vec2 TexCoord;
out vec3 Normal;
out vec3 Fragpos;

// Uniforms
uniform mat4 model;
uniform mat4 projection;
uniform mat4 view;

// Vertex loop
void main()
{
    gl_Position = projection * view * model * vec4(position, 1.0f);   
    Normal =  mat3(transpose(inverse(model))) * normal;
    Fragpos = vec3(model * vec4(position, 1.0f));
    TexCoord = texCoord;
}

Fragment shader code:片段着色器代码:

#version 330 core


// Node parses
out vec4 color;
in vec2 TexCoord;
in vec3 Normal;
in vec3 Fragpos;

// Camera data
uniform vec3    view_loc;
vec3            view_dir;

// Model data
vec3    normal;

// Phong data
vec3    _ambient;
vec3    _diffuse;
vec3    _specular;
vec3    _emissive;
vec3    _result;

// Material uniforms
uniform struct MATERIAL
{
    bool    masked;
    bool    additive;
    float   opacity;
    float   specular_damper;
    float   specular_intensity;
    float   emissive_intensity;
    vec3    diffuse;
    vec3    specular;   
} _mat;
uniform sampler2D texture[3];

// Global uniforms
uniform struct GLOBAL
{
    float   ambient_coefficient;
    vec3    ambient_colour;
} _global;

// Directional uniforms
uniform struct DIRECTIONAL
{
    float   intensity;
    vec3    position;
    vec3    colour;
} _dir;
vec3 d_view_dir;

// Directional light
vec3 directional()
{
    vec3 r;

    d_view_dir = normalize(-_dir.position);
    float src = max(dot(normal, d_view_dir),0.0);

    r = (src * _dir.colour) * _dir.intensity;

    return r;
}

// Final gather
vec4 finalGather()
{
    normal = normalize(Normal);
    view_dir = normalize(view_loc - Fragpos);

    // Ambient
    _ambient = _global.ambient_coefficient * _mat.diffuse * _dir.intensity;

    // Diffuse
    _diffuse = _mat.diffuse * directional();

    // Specular 
    vec3 reflectDir = reflect(-d_view_dir, normal);
    float power = pow(max(dot(view_dir, reflectDir), 0.0), _mat.specular_damper);
    _specular = (_mat.specular) * power * _dir.colour * _dir.intensity;

    // Emissive


    // Final result
    _result = (_ambient + _diffuse + _specular);

    //_diffuse;
    return  vec4(_result, _mat.opacity);
}

// Main loop
void main(void)
{
    color = vec4(finalGather());
}

Could it be a buffer offset problem?可能是缓冲区偏移问题吗? Thanks for the help guys!感谢您的帮助!

You posted a lot of code, so there could potentially be more errors than the ones pointed out below.您发布了大量代码,因此可能存在比下面指出的错误更多的错误。 Also, even once you load and render your object correctly, it can still not show up for various reasons, eg because the coordinates are outside the view volume.此外,即使您正确加载和渲染您的对象,由于各种原因,它仍然无法显示,例如因为坐标在视图体积之外。

From a quick look, I saw two errors in the code where you load the geometry into buffers, and set up the vertex attributes:快速浏览一下,我在将几何图形加载到缓冲区并设置顶点属性的代码中看到了两个错误:

glBufferData(GL_ARRAY_BUFFER, v.size() * sizeof(Vertex), &v[0], GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(CBuint) * 3, &i[0], GL_STATIC_DRAW);

In the second call, you're only loading 3 indices into the index buffer.在第二次调用中,您只将 3 个索引加载到索引缓冲区中。 You have to calculate the size of all indices, based on the size of the corresponding vector, just like you did for the first call:您必须根据相应向量的大小计算所有索引的大小,就像您在第一次调用时所做的那样:

glBufferData(GL_ELEMENT_ARRAY_BUFFER, i.size() * sizeof(CBuint), &i[0], GL_STATIC_DRAW);

The offsets in the vertex setup are also incorrect:顶点设置中的偏移量也不正确:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(0));
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 3));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(v) * 5));

v is a vector, so sizeof(v) is the size of the vector object, which is irrelevant here. v是一个向量,所以sizeof(v)是向量对象的大小,这里无关紧要。 You need the offset of the fields within the vertex object:您需要顶点对象中字段的偏移量:

glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 3));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), BUFFER_OFFSET(sizeof(CBfloat) * 6));

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

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