美文网首页
Dragon Engine:OpenGL渲染架构

Dragon Engine:OpenGL渲染架构

作者: Dragon_boy | 来源:发表于2020-08-17 23:47 被阅读0次

    这一节具体介绍OpenGL的渲染架构实现:

    渲染环境类:

    namespace Dragon
    {
        class OpenGLContext : public GraphicsContext
        {
        public:
            OpenGLContext(GLFWwindow* windowHandle);
    
            virtual void Init() override;
            virtual void SwapBuffers() override;
        private:
            GLFWwindow* m_WindowHandle;
        };
    }
    

    实现:

    namespace Dragon
    {
        OpenGLContext::OpenGLContext(GLFWwindow* windowHandle)
            :m_WindowHandle(windowHandle)
        {
            DG_CORE_ASSERT(windowHandle, "Window handle is null");
        }
        void OpenGLContext::Init()
        {
            glfwMakeContextCurrent(m_WindowHandle);
            int status = gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
            DG_CORE_ASSERT(status, "Failed to initailize GLAD!");
    
            DG_CORE_INFO("OpenGL info:");
            DG_CORE_INFO("  Vendor: {0}", glGetString(GL_VENDOR));
            DG_CORE_INFO("  Renderer: {0}", glGetString(GL_RENDERER));
            DG_CORE_INFO("  Version: {0}", glGetString(GL_VERSION));
    
        }
        void OpenGLContext::SwapBuffers()
        {
            glfwSwapBuffers(m_WindowHandle);
        }
    }
    

    构造方法会检测窗口是否为空。Init函数调用GLFW的方法创建当前窗口的环境,接着加载glad,并打印OpenGL的版本相关信息。SwapBuffers是一个交换缓冲的方法。

    OpenGL的RendererAPI类:

    namespace Dragon
    {
        class OpenGLRendererAPI : public RendererAPI
        {
        public:
            virtual void Init() override;
    
            virtual void SetClearColor(const glm::vec4& color) override;
            virtual void Clear() override;
    
            virtual void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray) override;
        };
    }
    

    方法的实现:

    namespace Dragon
    {
        void OpenGLRendererAPI::Init()
        {
            glEnable(GL_BLEND);
            glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        }
    
        void OpenGLRendererAPI::SetClearColor(const glm::vec4& color)
        {
            glClearColor(color.r, color.g, color.b, color.a);
        }
        void OpenGLRendererAPI::Clear()
        {
            glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        }
        void OpenGLRendererAPI::DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray)
        {
            glDrawElements(GL_TRIANGLES, vertexArray->GetIndexBuffer()->GetCount(), GL_UNSIGNED_INT, nullptr);
        }
    }
    

    各种方法是OpenGL的一些方法的封装。

    OpenGL的缓冲类:

    namespace Dragon
    {
        class OpenGLVertexBuffer : public VertexBuffer
        {
        public:
            OpenGLVertexBuffer(const void* vertices, uint32_t size);
            virtual ~OpenGLVertexBuffer();
    
            virtual void Bind() const override;
            virtual void Unbind() const override;
    
            virtual const BufferLayout& GetLayout() const override { return m_Layout; }
            virtual void SetLayout(const BufferLayout& layout) override { m_Layout = layout; }
        private:
            uint32_t m_RendererID;
            BufferLayout m_Layout;
        };
    
        class OpenGLIndexBuffer : public IndexBuffer
        {
        public:
            OpenGLIndexBuffer(const void* indices, uint32_t count);
            virtual ~OpenGLIndexBuffer();
    
            virtual void Bind() const;
            virtual void Unbind() const;
    
            virtual uint32_t GetCount() const;
        private:
            uint32_t m_RendererID;
            uint32_t m_Count;
        };
    }
    

    这里定义了顶点缓冲和索引缓冲。

    相关方法实现:

    namespace Dragon
    {
        ////////////////////////////////////////////////////////////////////////
        //VertexBuffer///////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////
    
        OpenGLVertexBuffer::OpenGLVertexBuffer(const void* vertices, uint32_t size)
        {
            glCreateBuffers(1, &m_RendererID);
            glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
            glBufferData(GL_ARRAY_BUFFER, size, vertices, GL_STATIC_DRAW);
        }
    
        OpenGLVertexBuffer::~OpenGLVertexBuffer()
        {
            glDeleteBuffers(1, &m_RendererID);
        }
    
        void OpenGLVertexBuffer::Bind() const
        {
            glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
        }
    
        void OpenGLVertexBuffer::Unbind() const
        {
            glBindBuffer(GL_ARRAY_BUFFER, 0);
        }
    
        ////////////////////////////////////////////////////////////////////////
        //IndexBuffer///////////////////////////////////////////////////////////
        ///////////////////////////////////////////////////////////////////////
    
        OpenGLIndexBuffer::OpenGLIndexBuffer(const void* indices, uint32_t count)
            :m_Count(count)
        {
            glCreateBuffers(1, &m_RendererID);
            glBindBuffer(GL_ARRAY_BUFFER, m_RendererID);
            glBufferData(GL_ARRAY_BUFFER, count * sizeof(uint32_t), indices, GL_STATIC_DRAW);
        }
    
        OpenGLIndexBuffer::~OpenGLIndexBuffer()
        {
            glDeleteBuffers(1, &m_RendererID);
        }
        void OpenGLIndexBuffer::Bind() const
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_RendererID);
        }
        void OpenGLIndexBuffer::Unbind() const
        {
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        uint32_t OpenGLIndexBuffer::GetCount() const
        {
            return m_Count;
        }
    }
    

    两个重点都在于构造方法,这里使用较新OpenGL API的的glCreateBuffers函数创建缓冲对象,接着绑定缓冲对象到特定目标,接着传递缓冲数据(这里可以使用更新的glNamedBufferStorage方法)。析构函数删除缓冲对象,Bind和Unbind绑定和解绑。

    接着是顶点数组对象类:

    namespace Dragon
    {
        class OpenGLVertexArray : public VertexArray
        {
        public:
            OpenGLVertexArray();
            virtual ~OpenGLVertexArray();
    
            virtual void Bind() const override;
            virtual void Unbind() const override;
    
            virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer) override;
            virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer) override;
    
            virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const { return m_VertexBuffers; };
            virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const { return m_IndexBuffer; };
        private:
            uint32_t m_RendererID;
            //uint32_t m_VertexBufferIndex = 0;
            std::vector<std::shared_ptr<VertexBuffer>> m_VertexBuffers;
            std::shared_ptr<IndexBuffer> m_IndexBuffer;
        };
    }
    

    成员函数基本继承自顶点数组类。

    相关方法实现:

    namespace Dragon
    {
        static GLenum ShaderDataTypeToOpenGLBaseType(ShaderDataType type)
        {
            switch (type)
            {
            case ShaderDataType::Float:     return GL_FLOAT;
            case ShaderDataType::Float2:    return GL_FLOAT;
            case ShaderDataType::Float3:    return GL_FLOAT;
            case ShaderDataType::Float4:    return GL_FLOAT;
            case ShaderDataType::Mat3:      return GL_FLOAT;
            case ShaderDataType::Mat4:      return GL_FLOAT;
            case ShaderDataType::Int:       return GL_INT;
            case ShaderDataType::Int2:      return GL_INT;
            case ShaderDataType::Int3:      return GL_INT;
            case ShaderDataType::Int4:      return GL_INT;
            case ShaderDataType::Bool:      return GL_BOOL;
            }
            DG_CORE_ASSERT(false, "Unknown Type");
            return 0;
        }
    
        OpenGLVertexArray::OpenGLVertexArray()
        {
            glCreateVertexArrays(1, &m_RendererID);
        }
        OpenGLVertexArray::~OpenGLVertexArray()
        {
            glDeleteVertexArrays(1, &m_RendererID);
        }
        void OpenGLVertexArray::Bind() const
        {
            glBindVertexArray(m_RendererID);
        }
        void OpenGLVertexArray::Unbind() const
        {
            glBindVertexArray(0);
        }
    

    这里定义了一个静态方法,它的功能是将我们之前定义的ShaderDataType转换为OpenGL的数据类型。

    void OpenGLVertexArray::AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer)
        {
            DG_CORE_ASSERT(vertexBuffer->GetLayout().GetElements().size(), "Vertex Buffer has no layout!");
    
            glBindVertexArray(m_RendererID);
            vertexBuffer->Bind();
    
            uint32_t index = 0;
            const auto& layout = vertexBuffer->GetLayout();
            for (const auto& element : layout)
            {
                glEnableVertexAttribArray(index);
                glVertexAttribPointer(index,
                    element.GetComponentCount(),
                    ShaderDataTypeToOpenGLBaseType(element.Type),
                    element.Normalized ? GL_TRUE : GL_FALSE,
                    layout.GetStride(),
                    (const void*)(intptr_t)element.Offset);
                index++;
            }
    
            m_VertexBuffers.push_back(vertexBuffer);
        }
        void OpenGLVertexArray::SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer)
        {
            glBindVertexArray(m_RendererID);
            indexBuffer->Bind();
    
            m_IndexBuffer = indexBuffer;
        }
    

    添加顶点缓冲的方法,逻辑是:先判断要添加的顶点缓冲对象是否有布局,接着绑定顶点数组对象,绑定顶点缓冲,接着遍历布局中的元素,设置所有的顶点属性,所有属性方法之前都介绍过,应该不用完整介绍相关逻辑。

    设置索引缓冲的方法很简单,绑定顶点数组对象后,绑定索引缓冲后设置成员属性即可。

    到此为止,一个基本的渲染架构就以完成,大家可以绘制一个简单的三角形来实验一下(不用考虑着色器,大部分GPU会设置一个默认的顶点着色器和片元着色器(白色))

    在客户端,我们就可以这么使用。

    比如,创建一个层对象,然后,在其构造函数中设置顶点的坐标,然后设置顶点缓冲和顶点数组:

    float vertices = {...};  //三角形坐标
    
    // 已在层类的中定义了顶点数组对象
    m_VertexArray.reset(VertexArray::Create());
    
    // 创建一个顶点缓冲对象
    std::shared_ptr<VertexBuffer> vertexBuffer;
    
    // 根据顶点数据创建布局
    BufferLayout layout = {
        {ShaderDataType::Float3, "a_Position"}
        };
    
    // 使用相应数据创建OpenGL顶点缓冲对象
    vertexBuffer.reset(VertexBuffer::Create(vertices, sizeof(vertices)));
    //设置布局
    vertexBuffer->SetLayout(layout);
    //添加顶点缓冲
    m_VertexArray->AddVertexBuffer(vertexBuffer);
    

    然后在层类的update()成员函数中:

    //设置背景颜色
    Dragon::RenderCommand::SetColor({...});
    //清除缓冲
    Dragon::RenderCommand::Clear();
    //开始场景
    Dragon::Renderer::BeginScene();
    //提交渲染数据
    Dragon::Renderer::Submit(...);
    //结束场景
    Dragon::Renderer::EndScene();
    

    可以看到抽象极大的简化了命令的调用,这也是我们的目的。不出意外地话,可以看到一个白色三角形。

    下一节介绍摄像机的实现。

    项目github地址:https://github.com/Dragon-Baby/Dragon

    相关文章

      网友评论

          本文标题:Dragon Engine:OpenGL渲染架构

          本文链接:https://www.haomeiwen.com/subject/rlwcjktx.html