【Overload游戏引擎细节分析】UBO与SSBO的封装

这篇具有很好参考价值的文章主要介绍了【Overload游戏引擎细节分析】UBO与SSBO的封装。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、OpenGL的UBO

  在OpenGL Shader中,如果逻辑比较复杂,使用的uniform变量较多。通常多个着色器使用同一个uniform变量。由于uniform变量的位置是着色器链接时候产生的,因此它在应用程序中获得的索引会有变化。Uniform Buffer Object(UBO)是一种优化uniform变量访问,不同着色器直接共享unfiorm数据的方法。  

在Overload引擎中,很多Shader包含如下片段,这里就是定义了一个UBO变量。 它将MVP矩阵一起放入到UBO变量中。

layout (std140) uniform EngineUBO
{
    mat4    ubo_Model;
    mat4    ubo_View;
    mat4    ubo_Projection;
    vec3    ubo_ViewPos;
    float   ubo_Time;
};

std140是内存布局限定符,除此之外还有std430、binding、packed等限定符。

二·、Overload对UBO的封装

   Overload引擎中对UBO的封装在UniformBuffer.h、UniformBuffer.inl、UniformBuffer.cpp文件中,将其操作包装成了一个类UniformBuffer。使用的时候先调用Bind,结束后UnBind,设置值使用SetSubData。

namespace OvRendering::Buffers
{
	/**
	* OpenGL UBO的封装
	*/
	class UniformBuffer
	{
	public:
		/**
		* Create a UniformBuffer
		* @param p_size (Specify the size in bytes of the UBO data)
		* @param p_bindingPoint (Specify the binding point on which the uniform buffer should be binded)
		* @parma p_offset (The offset of the UBO, sizeof previouses UBO if the binding point is != 0)
		* @param p_accessSpecifier
		*/
		UniformBuffer(size_t p_size, uint32_t p_bindingPoint = 0, uint32_t p_offset = 0, EAccessSpecifier p_accessSpecifier = EAccessSpecifier::DYNAMIC_DRAW);

		/**
		* Destructor of the UniformBuffer
		*/
		~UniformBuffer();

		/**
		* Bind the UBO
		*/
		void Bind();

		/**
		* Unbind the UBO
		*/
		void Unbind();

		/**
		* Set the data in the UBO located at p_offset to p_data
		* @param p_data
		* @param p_offset
		*/
		template<typename T>
		void SetSubData(const T& p_data, size_t p_offset);

		/**
		* Set the data in the UBO located at p_offset to p_data
		* @param p_data
		* @param p_offsetInOut (Will keep track of the current stride of the data layout)
		*/
		template<typename T>
		void SetSubData(const T& p_data, std::reference_wrapper<size_t> p_offsetInOut);

		/**
		* Return the ID of the UBO
		*/
		uint32_t GetID() const;

		/**
		* Bind a block identified by the given ID to given shader
		* @param p_shader
		* @param p_uniformBlockLocation
		* @param p_bindingPoint
		*/
		static void BindBlockToShader(OvRendering::Resources::Shader& p_shader, uint32_t p_uniformBlockLocation, uint32_t p_bindingPoint = 0);

		/**
		* Bind a block identified by the given name to the given shader
		* @param p_shader
		* @param p_name
		* @param p_bindingPoint
		*/
		static void BindBlockToShader(OvRendering::Resources::Shader& p_shader, const std::string& p_name, uint32_t p_bindingPoint = 0);

		/**
		* Return the location of the block (ID)
		* @param p_shader
		* @param p_name
		*/
		static uint32_t GetBlockLocation(OvRendering::Resources::Shader& p_shader, const std::string& p_name);

	private:
		uint32_t m_bufferID;
	};
}

#include "OvRendering/Buffers/UniformBuffer.inl"

其具体实现在UniformBuffer.cpp中。我们先看看构造函数代码:

OvRendering::Buffers::UniformBuffer::UniformBuffer(size_t p_size, uint32_t p_bindingPoint, uint32_t p_offset, EAccessSpecifier p_accessSpecifier)
{
	// 生成buffer
	glGenBuffers(1, &m_bufferID);
	// 绑定UBO
	glBindBuffer(GL_UNIFORM_BUFFER, m_bufferID);
	// 分配内存
	glBufferData(GL_UNIFORM_BUFFER, p_size, NULL, static_cast<GLint>(p_accessSpecifier));
	glBindBuffer(GL_UNIFORM_BUFFER, 0);
	// 将缓存对象m_bufferID绑定到索引为p_bindingPoint的UBO上
	glBindBufferRange(GL_UNIFORM_BUFFER, p_bindingPoint, m_bufferID, p_offset, p_size);
}

在构造函数中直接创建了UBO的buffer,并绑定到索引是p_bindingPoint的UBO上。这里用到了OpenGL函数glBindBufferRange,如无需指定偏移量与size值可使用glBindBufferBase函数。

UniformBuffer.cpp中Bind()、UnBind()过于简单不再分析。往下接着看有个static函数BindBlockToShader。这个函数主要是显式绑定一个uniform块到p_bindingPoint索引,这样可以绑定同一个缓存。这里使用到了glUniformBlockBinding函数,这个函数主要是显示指定BUO的索引,可以保证多个不同的Shader程序之间UBO的索引是一样的,但需要在调用glLinkProgram之前调用。

void OvRendering::Buffers::UniformBuffer::BindBlockToShader(OvRendering::Resources::Shader& p_shader, uint32_t p_uniformBlockLocation, uint32_t p_bindingPoint)
{
	glUniformBlockBinding(p_shader.id, p_uniformBlockLocation, p_bindingPoint);
}

void OvRendering::Buffers::UniformBuffer::BindBlockToShader(OvRendering::Resources::Shader& p_shader, const std::string& p_name, uint32_t p_bindingPoint)
{
	glUniformBlockBinding(p_shader.id, GetBlockLocation(p_shader, p_name), p_bindingPoint);
}

// 获取UBO的索引位置
uint32_t OvRendering::Buffers::UniformBuffer::GetBlockLocation(OvRendering::Resources::Shader& p_shader, const std::string& p_name)
{
	return glGetUniformBlockIndex(p_shader.id, p_name.c_str());
}

但在Overload引擎中,调用这个方法是在调用glProgram之后调用的,而且索引值使用的是GetBlockLocation获取的,这也是UBO在Shader的默认索引值,所以这个方法应该是可以删除的。我注释这个方法使用上没有发现什么问题。

最后看一下如何给UBO设置值,其实现是在UniformBuffer.inl文件中,主要使用glBufferSubData函数,指定其偏移值与数据大小即可。

	template<typename T>
	inline void UniformBuffer::SetSubData(const T& p_data, size_t p_offsetInOut)
	{
		Bind();
		glBufferSubData(GL_UNIFORM_BUFFER, p_offsetInOut, sizeof(T), std::addressof(p_data));
		Unbind();
	}

	template<typename T>
	inline void UniformBuffer::SetSubData(const T& p_data, std::reference_wrapper<size_t> p_offsetInOut)
	{
		Bind();
		size_t dataSize = sizeof(T);
		glBufferSubData(GL_UNIFORM_BUFFER, p_offsetInOut.get(), dataSize, std::addressof(p_data));
		p_offsetInOut.get() += dataSize;
		Unbind();
	}

三、OpenGL的SSBO

Shader Storage Buffer Object(SSBO),着色器存储缓存对象,其行为类似于UBO,但其功能上更为强大。首先,着色器可以写入buffer块,修改其内容并呈现给其他Shader或应用程序本身。其次,可以在渲染之前再觉得其大小,而不是编译与链接时。在Overload中,灯光信息是用SSBO存储的,看以下Shader片段:

layout(std430, binding = 0) buffer LightSSBO
{
    mat4 ssbo_Lights[];
};

在着色器中可以使用length()获取ssbo_Lights的长度。

设置SSBO的方式与设置UBO类似,不过glBindBuffer()、glBindBufferRange()、glBindBufferBase()需要使用GL_SHADER_STORAGE_BUFFER作为目标参数。

四、Overload对SSBO的封装

Overload是将SSBO的操作封装到类ShaderStorageBuffer中,具体代码就不分析了,与UBO大同小异。文章来源地址https://www.toymoban.com/news/detail-728320.html

到了这里,关于【Overload游戏引擎细节分析】UBO与SSBO的封装的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Overload游戏引擎细节分析】PBR材质Shader---完结篇

    PBR基于物理的渲染可以实现更加真实的效果,其Shader值得分析一下。但PBR需要较多的基础知识,不适合不会OpenGL的朋友。 一、PBR理论 PBR指基于物理的渲染,其理论较多,需要的基础知识也较多,我在这就不再写一遍了,具体可以参看: LearnOpenGL PBR理论-英文 或者 LearnOpenGL

    2024年02月08日
    浏览(44)
  • 【Overload游戏引擎细节分析】编辑器对象鼠标拾取原理

          Overload的场景视图区有拾取鼠标功能,单击拾取物体后会显示在Inspector面板中。本文来分析鼠标拾取这个功能背后的原理。 一、OpenGL的FrameBuffer 实现鼠标拾取常用的方式有两种:渲染id到纹理、光线投射求交。Overload使用的是渲染id到纹理,其实现需借助OpenGL的帧缓冲

    2024年02月04日
    浏览(50)
  • 【Overload游戏引擎细节分析】视图投影矩阵计算与摄像机

    本文只罗列公式,不做具体的推导。 OpenGL本身没有摄像机(Camera)的概念,但我们为了产品上的需求与编程上的方便,一般会抽象一个摄像机组件。摄像机类似于人眼,可以建立一个本地坐标系。相机的位置是坐标原点,摄像机的朝向Forward是摄像机看的方向,再给定向上的Up轴

    2024年02月07日
    浏览(42)
  • 安卓游戏开发之物理引擎优劣分析

            在安卓游戏开发中,物理引擎是模拟现实世界中物理现象和技术的核心组件,它能够使得游戏中的物体和行为更加真实。物理引擎通常能够处理碰撞检测、动力学模拟、刚体、软体、关节、碰撞响应、摩擦力和更多物理效应。         不同的物理引擎有不同的

    2024年02月21日
    浏览(43)
  • Redis底层封装细节

    日常我们程序员在使用redis做缓存的时候,很少会直接使用到RedisTemplate直接操作k-v键值对,而是通过对RedisTemplate原生代码的封装,来构建我们日常便于使用习惯的代码来操作数据,这里我分享一下日常基本的对RedisTemplate底层的封装原理和使用方法 以上是我日常使用过程中对

    2024年02月16日
    浏览(35)
  • 数据从发出到接收的细节介绍{封装与解封装}

    目录 前言 一,数据封装的全过程 1.1,应用层的封装形式 1.2,传输层的封装形式 理解: 1.3,网络层的封装形式 理解: 1.4,数据链路层的封装形式 理解: 1.5,物理层 1.6,总结 二,网络数据传输 三,解封装 3.1,物理层 3.2,数据链路层 3.3,网络层 3.4,传输层 3.5,应用层

    2024年02月16日
    浏览(28)
  • Easy Rules规则引擎(2-细节篇)

    在 Easy Rules规则引擎(1-基础篇) 中我们已经简单介绍了 Easy Rules 规则引擎的使用示例,这节我们详解介绍一下规则引擎的相关参数配置实例还有组合规则。 Easy Rules 规则引擎支持下面参数配置: 参数名称 参数类型 必选 默认值 rulePriorityThreshold int 否 Integer.MAX_VALUE skipOnFirst

    2024年02月11日
    浏览(44)
  • 搜索引擎排名因素有哪些具体的细节?

    搜索引擎排名因素有很多,以下是一些常见的因素: 密度和位置:搜索引擎会考虑在网页上的出现频率和位置。密度指的是在网页内容中出现的频率与整个文本的比例。的位置也很重要,例如,如果出现在页面的顶部或标题标签中,则

    2024年02月07日
    浏览(49)
  • 回炉与剖析C++封装特性 - 重新认识C++,完满呈现全部内部细节

    💛前情提要💛 本章节是 C++ 的 深度剖析封装细节特性 的相关知识~ 接下来我们即将进入一个全新的空间,对代码有一个全新的视角~ 以下的内容一定会让你对 C++ 有一个颠覆性的认识哦!!! 以下内容干货满满,跟上步伐吧~ 作者介绍: 🎓 作者: 热爱编程不起眼的小人物🐐

    2023年04月13日
    浏览(35)
  • UE4/5C++多线程插件制作(十九、异步资源读取封装,细节修改)

    目录 MTPResourceLoadManage MTPThreadInterface MTPManage.h MTPManage.cpp RTPAgendy RTPAgendy.h  RTPAgendy.cpp

    2024年02月14日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包