Wavefront OBJ模型无法渲染 - OpenGL C ++

时间:2016-10-02 15:09:29

标签: c++ opengl rendering vbo

我似乎无法在C ++中使用 glDrawElements 渲染OBJ模型。

这是我的代码:

#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);
    }
};

我可以在对顶点和索引进行硬编码时渲染一个简单的三角形,因此它不能成为着色器问题。我已经彻底检查了索引顶点优化部分,即使与其他示例进行比较,也无法看到它有什么问题。

任何想法,更正,解决方案或其他什么?

编辑:立方体模型呈现但现在我遇到了一个新问题 - 在渲染三角形时,索引似乎被双重交叉加扰:

Click me for screenshot!

这是相同的cube.obj模型,但在点亮模式下:

Click to see in lit mode!

顶点着色器代码:

#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;
}

片段着色器代码:

#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());
}

可能是缓冲区偏移问题吗?谢谢你的帮助!

1 个答案:

答案 0 :(得分:0)

您发布了大量代码,因此可能会出现比下面指出的错误更多的错误。此外,即使您正确加载和渲染对象,它仍然可能由于各种原因而无法显示,例如因为坐标位于视图体积之外。

从快速浏览看,我在代码中看到了两个错误,您将几何体加载到缓冲区中,并设置顶点属性:

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);

在第二次调用中,您只需将3个索引加载到索引缓冲区中。您必须根据相应向量的大小计算所有索引的大小,就像您第一次调用时一样:

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

顶点设置中的偏移量也不正确:

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是一个向量,因此sizeof(v)是向量对象的大小,这与此无关。您需要顶点对象中字段的偏移量:

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));
相关问题