【问题标题】:OpenGL VertexBuffer Class redefinition and template method errorsOpenGL VertexBuffer 类重定义和模板方法错误
【发布时间】:2019-09-08 21:59:12
【问题描述】:

我有一个简单的 OpenGL 程序,它按照以下步骤使用绘制管道绘制一个正方形:使用 OpenGL 我绑定我的着色器 > 绑定我的顶点数组 > 将我的索引缓冲区与我的 2 个三角形的点列表绑定 > 绘制三角形数据使用我绑定到我的程序的顶点和着色器数据到我的窗口。我在 NVIDIA GPU 版本 388.13 和 GLEW 版本 2.1.0 上使用 OpenGL 版本 3.3.0。

我为此绘制管道创建的抽象现在抛出了一些错误。在我介绍 .特别是在我的VertexBufferLayout 类中的模板推送方法中。

template<class T>
void VertexBufferLayout::push(unsigned int elements_per_vertex);

以及VertexBufferVertexBufferLayoutVertexBufferLayoutElement 的“类”或“结构”类型重新定义错误。我想知道这是否是我的每个构造函数的问题?

顶点缓冲区 标题

class VertexBuffer
{
private:
    unsigned int m_renderer_id;
public:
    VertexBuffer(const void* data, unsigned int size);
    ~VertexBuffer();

    void bind() const;
    void unbind() const;
};

cpp

#include "VertexBuffer.h"

VertexBuffer::VertexBuffer(const void* data, unsigned int size)
{
    MyGLCall(glGenBuffers(1, &m_renderer_id));
    MyGLCall(glBindBuffer(GL_ARRAY_BUFFER, m_renderer_id));
    MyGLCall(glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW));
}

VertexBufferLayout 标头,其中还包含我的 VertexBufferLayoutElement 结构

#include <vector>

#include <GL\glew.h>

#include "Renderer.h"

struct VertexBufferLayoutElement
{
    // nice mem alignment bro, mem size on most machines is
    unsigned int elements_per_vertex;   // 4 bytes
    unsigned int type;                  // 4 bytes
    unsigned char normalized;           // 1 byte
    static unsigned int get_size_of_type(unsigned int type)
    {
        switch (type)
        {
            case GL_FLOAT:          return 4;
            case GL_UNSIGNED_INT:   return 4;
            case GL_UNSIGNED_BYTE:  return 1;
        }
        ASSERT(false);
        return 0;
    }
};

class VertexBufferLayout
{
private:
    unsigned int m_stride; // vertex_mem_address_width
    std::vector<VertexBufferLayoutElement> m_elements;
public:
    VertexBufferLayout();
    ~VertexBufferLayout();

    template<class T>
    void push(unsigned int elements_per_vertex);
    // these 3 templates MUST use the format above
    template<>
    void push<float>(unsigned int elements_per_vertex);
    template<>
    void push<unsigned int>(unsigned int elements_per_vertex);
    template<>
    void push<unsigned char>(unsigned int elements_per_vertex);

    // need const& here?
    inline const std::vector<VertexBufferLayoutElement> get_elements() const { return m_elements; }
    inline unsigned int get_stride() const { return m_stride; }
};

cpp

#include "VertexBufferLayout.h"

VertexBufferLayout::VertexBufferLayout() 
    : m_stride(0) { }

我会将所有源 .h 和 .cpp 文件暂时保留在 Dropbox 中以帮助提供反馈。一旦我有答案,我会删除它们。

完整的 1 个警告和 8 个错误详情如下。

Warning C4067    unexpected tokens following preprocessor directive - expected a newline    BasicOOP    D:\opengl\BasicOOP\BasicOOP\VertexArray.h   1   
Error   C2062    type 'float' unexpected    BasicOOP    D:\opengl\BasicOOP\BasicOOP\Main.cpp    67  
Error   C2664    'void VertexArray::add_buffer(const VertexBuffer &,const VertexBufferLayout &)': cannot convert argument 1 from 'int' to 'const VertexBuffer &'    BasicOOP    D:\opengl\BasicOOP\BasicOOP\Main.cpp    68  
Error   C2011    'VertexBufferLayoutElement': 'struct' type redefinition    BasicOOP    D:\opengl\BasicOOP\BasicOOP\VertexBufferLayout.h    8   
Error   C2011    'VertexBufferLayout': 'class' type redefinition    BasicOOP    D:\opengl\BasicOOP\BasicOOP\VertexBufferLayout.h    27  
Error   C2079    'vertexBufferLayout' uses undefined class 'VertexBufferLayout' BasicOOP    D:\opengl\BasicOOP\BasicOOP\Main.cpp    66  
Error   C2011    'VertexBuffer': 'class' type redefinition  BasicOOP    D:\opengl\BasicOOP\BasicOOP\VertexBuffer.h  2   
Error   C2079    'vertexBuffer' uses undefined class 'VertexBuffer' BasicOOP    D:\opengl\BasicOOP\BasicOOP\Main.cpp    64  
Error   C2440    'initializing': cannot convert from 'initializer list' to 'int'    BasicOOP    D:\opengl\BasicOOP\BasicOOP\Main.cpp    64

Main.cpp

#include <iostream>

#include "Renderer.h"

#include "VertexArray.h"
#include "VertexBuffer.h"
#include "VertexBufferLayout.h"
#include "IndexBuffer.h"
#include "ShaderManager.h"

int main(void)
{
    std::cout << "Object Oriented OpenGL\n";

    GLFWwindow* window;
    if (!glfwInit()) { return -1; }
    // opengl v3.3
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    window = glfwCreateWindow(640, 480, "Object Oriented OpenGL", NULL, NULL);
    if (!window) {
        glfwTerminate();
        return -1;
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(3);

    if (GLEW_OK != glewInit()) { std::cout << "ERROR initializing OpenGL GLEW\n"; }

    std::cout << "Rendering...\nOpenGL version " << glGetString(GL_VERSION)
        << "\nGL Extension Wrangler version " << glewGetString(GLEW_VERSION);

    // big scope why?
    // both buffers are stack allocated, destructor called when scope exits
    // odd use case we have here... rare to make buffers in main function scope 
    // This creates infinite loop on glfwTerminate(), we call glfwTerminate() BEFORE exiting main scope
    // and having both Buffers destructors clean them up
    // could make these pointers and heap allocate these with new, but let's just change where scope of these are defined
    /*
        VertexBuffer vertexBuffer(tri_verticies, buffer_object_size * sizeof(float));
        IndexBuffer indexBuffer(tri_indicies, index_buffer_object_size);
    */
    {
        const int buffer_object_size = 8;
        const int index_buffer_object_size = 6;
        float tri_verticies[buffer_object_size] = {
            -0.5f, -0.5f, // 0
             0.5f, -0.5f, // 1
             0.5f,  0.5f, // 2
            -0.5f,  0.5f  // 3
        };
        unsigned int tri_indicies[index_buffer_object_size] = {
            0, 1, 2,
            2, 3, 0
        };

        // both buffers are stack allocated, destructor called when scope exits
        // odd use case we have here... rare to make buffers in main function scope 
        // could heap allocate these with new, but let's just change where scope of these are defined
        VertexArray vertexArray;                
        VertexBuffer vertexBuffer(tri_verticies, buffer_object_size * sizeof(float)); // no call vertexBuffer.bind() constructor does it    

        VertexBufferLayout vertexBufferLayout;
        vertexBufferLayout.push<float>(2);
        vertexArray.add_buffer(vertexBuffer, vertexBufferLayout);

        IndexBuffer indexBuffer(tri_indicies, index_buffer_object_size);        

        ShaderManager shaderManager;
        ShaderSource shaderSource = shaderManager.parse_shader("BasicUniform.shader");  // ensure debug working dir is relative to $(ProjectDir)
        unsigned int shader = shaderManager.create_shader(shaderSource.vertex_source, shaderSource.fragment_source);
        MyGLCall(glUseProgram(shader));

        // every uniform has a name as it's id
         MyGLCall(int location = glGetUniformLocation(shader, "u_color"));
         ASSERT(location != -1);
         MyGLCall(glUniform4f(location, 0.5f, 0.2f, 0.7f, 1.0f));

        // * * * * * * * * * * * *
        // first unbind everything
        // * * * * * * * * * * * *
        // http://docs.gl/gl4/glBindBuffer
        /*
        `   Buffer object names are unsigned integers.The value zero is reserved, but there is no default buffer object for each buffer object target.Instead, buffer set to zero effectively unbinds any buffer object previously bound, and restores client memory usage for that buffer object target(if supported for that target).
        */
        vertexArray.unbind();
        MyGLCall(glUseProgram(0)); // was shader
        MyGLCall(glBindBuffer(GL_ARRAY_BUFFER, 0)); // was buffer_object
        MyGLCall(glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); // was index_buffer_object

        float blue = 0.7f;
        float increment = 0.05f;

        while (!glfwWindowShouldClose(window))
        {
            MyGLCall(glClear(GL_COLOR_BUFFER_BIT));
            // * * * * * * * * * * * * *
            // then bind everything back
            // * * * * * * * * * * * * *
            // 1. bind shader and setup uniform
            MyGLCall(glUseProgram(shader));
            MyGLCall(glUniform4f(location, 0.5f, 0.2f, blue, 1.0f));
            // 2. bind index buffer
            // all replaced with the next line that links vertex buffer to vertex array object
            vertexArray.bind();
            indexBuffer.bind();

            // use below for index buffer
            // triple AAA games use me :) the right way to draw triple AAA mario is below
            // want to draw mario? one vertex buffer for all his vertex data, then many index buffers to draw each pieces of the model
            // marios boots will be a different material than his hat
            MyGLCall(glDrawElements(GL_TRIANGLES, index_buffer_object_size, GL_UNSIGNED_INT, nullptr)); // nullptr since we bind buffers using glGenBuffers

            if (blue > 1.0f) {
                increment = -0.05f;
            }
            else if (blue < 0.0f) {
                increment = 0.05f;
            }

            blue += increment;

            glfwSwapBuffers(window);
            glfwPollEvents();
        }

        MyGLCall(glDeleteProgram(shader));
    }

    //while (!glfwWindowShouldClose(window))
    //{
    //  /* Render here */
    //  glClear(GL_COLOR_BUFFER_BIT);

    //  /* Swap front and back buffers */
    //  glfwSwapBuffers(window);

    //  /* Poll for and process events */
    //  glfwPollEvents();
    //}

    glfwTerminate();

    return 0;
}

【问题讨论】:

    标签: c++ oop


    【解决方案1】:

    看样子是因为你没有添加

    #pragma once
    

    到头文件的开头(因此多次包含相同的头文件,重新定义您的类类型)

    /编辑

    如果你有一些头文件,例如

    // foo.h
    class Foo { };
    

    然后我们最终将它包含在同一个文件中两次,例如

    #include "foo.h"
    #include "foo.h"  //< usually due to another file including foo.h
    

    我们最终会得到:

    class Foo { };
    class Foo { };  ///< compiler error! redefined class Foo
    

    为了防止文件被第二次包含,只需执行以下操作:

    #pragma once
    
    class Foo { };
    

    现在该文件只会被包含一次。

    【讨论】:

    • 等我回到我的电脑上再试试。为什么需要这个 pragma 宏?
    • 它在技术上是一个非标准的预处理器指令(恰好在所有主要的 C++ 编译器上都支持)。我在上面添加了更多细节。
    • 谢谢,很清楚。我也完成了这个头文件保护,对吗?确保我的类定义不包含两次。
    • 你可以这样做,但 #pragma once 是一个更好的解决方案恕我直言。
    猜你喜欢
    • 2010-11-09
    • 2015-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-07-06
    • 1970-01-01
    相关资源
    最近更新 更多