本节的目的是在已有的基础上构建出一个基础渲染架构。主要任务就是抽象。
一个简单的渲染架构包含很多模块,我之前发了许多的渲染相关的文章,大家应该都比较清楚了,总结一下大概就是:
- 渲染环境
- 各种缓冲对象
- 顶点数组对象
- 着色器材质
- 纹理
- 摄像机、灯光等部件
上面是一个最基本的渲染结构所需要的部件,为了方便使用,我们会将上面一些部分封装为底层,抽象出一个渲染器的概念,它可以调用一些渲染命令。
首先是渲染器,这里我们构建渲染器的类:
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
网友评论