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

Dragon Engine:基础渲染架构

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

本节的目的是在已有的基础上构建出一个基础渲染架构。主要任务就是抽象。

一个简单的渲染架构包含很多模块,我之前发了许多的渲染相关的文章,大家应该都比较清楚了,总结一下大概就是:

  • 渲染环境
  • 各种缓冲对象
  • 顶点数组对象
  • 着色器材质
  • 纹理
  • 摄像机、灯光等部件

上面是一个最基本的渲染结构所需要的部件,为了方便使用,我们会将上面一些部分封装为底层,抽象出一个渲染器的概念,它可以调用一些渲染命令。

首先是渲染器,这里我们构建渲染器的类:
Renderer.h:

namespace Dragon
{
    class Renderer
    {
    public:
        static void Init();

        static void BeginScene();
        
        static void EndScene();

        static void Submit(const std::shared_ptr<VertexArray>& vertexArray);
        inline static RendererAPI::API GetAPI() { return RendererAPI::GetAPI(); }
        
    private:
        struct SceneData
        {
            glm::mat4 ViewProjectionMatrix;
        };

        static SceneData* m_SceneData;
    };

}

设计思路是将场景数据保存起来,这里暂时只保存一些简单的数据。然后包含初始化渲染器,开始场景,结束场景,提交渲染数据等方法,然后可以获得当前渲染所需的API,以应对不同的常见图形API。

渲染器可以调用一些渲染命令,RenderCommand.h:

namespace Dragon
{
    class RenderCommand
    {
    public:
        inline static void Init()
        {
            s_RendererAPI->Init();
        }

        inline static void SetClearColor(const glm::vec4& color)
        {
            s_RendererAPI->SetClearColor(color);
        }
        inline static void Clear()
        {
            s_RendererAPI->Clear();
        }
        inline static void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray)
        {
            s_RendererAPI->DrawIndexed(vertexArray);
        }

    private:
        static RendererAPI* s_RendererAPI;
    };
}

分别是初始化,设置颜色,清空缓冲,绘制命令,与API相匹配。

Renderer类实现:

namespace Dragon
{
    Renderer::SceneData* Renderer::m_SceneData = new Renderer::SceneData;

    void Renderer::Init()
    {
        RenderCommand::Init();
    }


    void Renderer::BeginScene()
    {
        m_SceneData->ViewProjectionMatrix = glm::mat4(0.0f);
    }
    void Renderer::EndScene()
    {
    }
    void Renderer::Submit(const std::shared_ptr<VertexArray>& vertexArray)
    {
        vertexArray->Bind();
        RenderCommand::DrawIndexed(vertexArray);
        vertexArray->Unbind();
    }
}

封住了一些渲染相关命令。

RenderCommand类实现:

namespace Dragon
{
    RendererAPI* RenderCommand::s_RendererAPI = new OpenGLRendererAPI();
}

初始化静态API变量。

针对多种API的考虑,设计一个RendererAPI类:

namespace Dragon
{
    class RendererAPI
    {
    public:
        enum class API
        {
            None = 0, OpenGL = 1
        };
    public:
        virtual void Init() = 0;
        virtual void SetClearColor(const glm::vec4& color) = 0;
        virtual void Clear() = 0;

        virtual void DrawIndexed(const std::shared_ptr<VertexArray>& vertexArray) = 0;

        inline static API GetAPI() { return s_API; }
    private:
        static API s_API;
    };
}

目前只考虑OpenGL。

它的实现即初始化静态变量:

namespace Dragon
{
    RendererAPI::API RendererAPI::s_API = RendererAPI::API::OpenGL;
}

定义渲染环境类,GraphicContext.h:

namespace Dragon
{
    class GraphicsContext
    {
    public:
        virtual void Init() = 0;
        virtual void SwapBuffers() = 0;
    };
}

定义缓冲对象类:Buffer.h:

namespace Dragon
{
    enum class ShaderDataType
    {
        None = 0, Float, Float2, Float3, Float4, Mat3, Mat4, Int, Int2, Int3, Int4, Bool
    };

    static uint32_t ShaderDataTypeSize(ShaderDataType type)
    {
        switch (type)
        {
        case ShaderDataType::Float :    return 4;
        case ShaderDataType::Float2 :   return 4*2;
        case ShaderDataType::Float3 :   return 4*3;
        case ShaderDataType::Float4 :   return 4*4;
        case ShaderDataType::Mat3 :     return 4*3*3;
        case ShaderDataType::Mat4 :     return 4*4*4;
        case ShaderDataType::Int :      return 4;
        case ShaderDataType::Int2 :     return 4*2;
        case ShaderDataType::Int3 :     return 4*3;
        case ShaderDataType::Int4 :     return 4*4;
        case ShaderDataType::Bool:      return 1;
        }
        DG_CORE_ASSERT(false, "Unknown ShaderDataType!");
        return 0;
    }

首先我们定义一些着色器的数据结构,ShaderDataTypeSize可以返回某一数据结构的长度。

定义BufferElement结构体:

struct BufferElement
    {
        std::string Name;
        uint32_t Offset;
        uint32_t Size;
        ShaderDataType Type;
        bool Normalized;

        BufferElement() {}

        BufferElement(ShaderDataType type, const std::string& name, bool normalized = false)
            :Name(name), Type(type), Size(ShaderDataTypeSize(type)), Offset(0), Normalized(normalized)
        {

        }

        uint32_t GetComponentCount() const
        {
            switch (Type)
            {
            case ShaderDataType::Float:     return 1;
            case ShaderDataType::Float2:    return 2;
            case ShaderDataType::Float3:    return 3;
            case ShaderDataType::Float4:    return 4;
            case ShaderDataType::Mat3:      return 3*3;
            case ShaderDataType::Mat4:      return 4 * 4;
            case ShaderDataType::Int:       return 1;
            case ShaderDataType::Int2:      return 2;
            case ShaderDataType::Int3:      return 3;
            case ShaderDataType::Int4:      return 4;
            case ShaderDataType::Bool:      return 1;
            }

            DG_CORE_ASSERT(false, "Unknown ShaderDataType!");
            return 0;
        }
    };

该结构体可以方便进行顶点属性设置。结构体也可以和类类似,构建构造函数,包含的属性为缓冲名称,数据结构类型,数据长度,偏移量以及是否正交化。

接着构建缓冲布局类:

class BufferLayout
    {
    public:
        BufferLayout() {}
        BufferLayout(const std::initializer_list<BufferElement>& elements)
            :m_Elements(elements)
        {
            CalculateOffsetAndStride();
        }

        inline uint32_t GetStride() const{ return m_Stride; }
        inline const std::vector<BufferElement>& GetElements() const { return m_Elements; }

        std::vector<BufferElement>::iterator begin() { return m_Elements.begin(); }
        std::vector<BufferElement>::iterator end() { return m_Elements.end(); }
        std::vector<BufferElement>::const_iterator begin() const{ return m_Elements.begin(); }
        std::vector<BufferElement>::const_iterator end() const{ return m_Elements.end(); }
    private:
        void CalculateOffsetAndStride()
        {
            uint32_t offset = 0;
            m_Stride = 0;
            for (auto& element : m_Elements)
            {
                element.Offset = offset;
                offset += element.Size;
                m_Stride += element.Size;
            }
        }
    private:
        std::vector<BufferElement> m_Elements;
        uint32_t m_Stride = 0;
    };

可以存储不同的缓冲对象,定义了一些vector迭代器用于方便缓冲对象结构vector容器的遍历。注意构造方法中的initializer_list方便我们用列表作为参数。私有的计算偏移和步长的方法来自于缓冲结构体中的设置。

进入正题,构建顶点缓冲类:

class VertexBuffer
    {
    public:
        virtual ~VertexBuffer() {}

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;

        virtual const BufferLayout& GetLayout() const = 0;
        virtual void SetLayout(const BufferLayout& layout) = 0;

        static VertexBuffer* Create(const void* vertices, uint32_t size);
    };

成员函数很简单,绑定,解绑,获取缓冲布局以及设置缓冲布局。并设置一个类方法用于创建顶点缓冲。

同样,索引缓冲:

class IndexBuffer
    {
    public:
        virtual ~IndexBuffer() {}

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;

        virtual uint32_t GetCount() const = 0;

        static IndexBuffer* Create(const void* indices, uint32_t count);
    };

没什么太大的不同,改为一个获取索引数的方法。

两个缓冲类的创建方法实现为:

namespace Dragon
{
    VertexBuffer* VertexBuffer::Create(const void* vertices, uint32_t size)
    {
        switch (Renderer::GetAPI())
        {
        case RendererAPI::API::None:
            DG_CORE_ASSERT(false, "RenderAPI::None is currently not supported!");
            return nullptr;
        case RendererAPI::API::OpenGL:
            return new OpenGLVertexBuffer(vertices, size);
        }

        DG_CORE_ASSERT(false, "Unknown RendererAPI");
        return nullptr;
    }

    IndexBuffer* IndexBuffer::Create(const void* indices, uint32_t count)
    {
        switch (Renderer::GetAPI())
        {
        case RendererAPI::API::None:
            DG_CORE_ASSERT(false, "RenderAPI::None is currently not supported!");
            return nullptr;
        case RendererAPI::API::OpenGL:
            return new OpenGLIndexBuffer(indices, count);
        }

        DG_CORE_ASSERT(false, "Unknown RendererAPI");
        return nullptr;
    }
}

两个方法实现类似,都是根据当前的API创建对应API的缓冲对象,如果不存在相应API则中断程序,返回空指针。

接着是顶点数组对象的类:

namespace Dragon
{
    class VertexArray
    {
    public:
        virtual ~VertexArray() {}

        virtual void Bind() const = 0;
        virtual void Unbind() const = 0;

        virtual void AddVertexBuffer(const std::shared_ptr<VertexBuffer>& vertexBuffer)  = 0;
        virtual void SetIndexBuffer(const std::shared_ptr<IndexBuffer>& indexBuffer)  = 0;
        
        virtual const std::vector<std::shared_ptr<VertexBuffer>>& GetVertexBuffers() const = 0;
        virtual const std::shared_ptr<IndexBuffer>& GetIndexBuffer() const = 0;

        static VertexArray* Create();
    };
}

同样,绑定和解绑方法,同时添加顶点缓冲和设置索引缓冲,也设定了一个顶点数组对象创建函数。

相关实现:

namespace Dragon
{
    
    VertexArray* VertexArray::Create()
    {
        switch (Renderer::GetAPI())
        {
        case RendererAPI::API::None:    DG_CORE_ASSERT(false, "RendererAPI::None is currently not supported!"); return nullptr;
        case RendererAPI::API::OpenGL:  return new OpenGLVertexArray();
        }

        DG_CORE_ASSERT(false, "Unknown RendererAPI!");
        return nullptr;
    }
}

和缓冲的创建函数类似。

接下来就是每种API的具体实现了,这里只考虑的OpenGL(我也只会这个),这个下一节介绍。

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

相关文章

  • Dragon Engine:基础渲染架构

    本节的目的是在已有的基础上构建出一个基础渲染架构。主要任务就是抽象。 一个简单的渲染架构包含很多模块,我之前发了许...

  • Dragon Engine:OpenGL渲染架构

    这一节具体介绍OpenGL的渲染架构实现: 渲染环境类: 实现: 构造方法会检测窗口是否为空。Init函数调用GL...

  • Dragon Engine:Debug

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io Debug,或者说日志系统,...

  • Dragon Engine:窗口

    本节介绍窗口的实现。这里使用开源库GLFW,之前也介绍过,只不过这里的主要任务就是将GLFW的API抽象出来,方便...

  • Dragon Engine:GUI

    这一节介绍GUI的实现。我们这里使用开源库ImGui。 首先我们将GUI分离为一个单独的层,创建ImGuiLaye...

  • Dragon Engine:层

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io/ 层的概念在许多软件中都存在...

  • Dragon Engine:事件系统

    本文同时发布在我的个人博客上:https://dragon_boy.gitee.io 事件系统的范围很广,这里我暂...

  • Dragon Engine:编译项目

    编写大项目的时候,大部分文件可以复用,但构建项目的软件,使用的平台,可使用的外部资源因各种各不相同,所以大部分时候...

  • Dragon Engine:材质系统

    这一节介绍材质系统,主要是进行着色器类的抽象: 这里设计了两个类,一个着色器类,一个着色器库类。着色器类的成员函数...

  • Dragon Engine:纹理模块

    这一节介绍纹理模块的实现。首先是纹理类的抽象: 纹理基类的构造不用多说,这里衍生出了两个纹理类,2D和立方体贴图。...

网友评论

      本文标题:Dragon Engine:基础渲染架构

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