一、OpenGL的UBO
? 在OpenGL Shader中,如果邏輯比較復(fù)雜,使用的uniform變量較多。通常多個(gè)著色器使用同一個(gè)uniform變量。由于uniform變量的位置是著色器鏈接時(shí)候產(chǎn)生的,因此它在應(yīng)用程序中獲得的索引會(huì)有變化。Uniform Buffer Object(UBO)是一種優(yōu)化uniform變量訪問(wèn),不同著色器直接共享unfiorm數(shù)據(jù)的方法。??
在Overload引擎中,很多Shader包含如下片段,這里就是定義了一個(gè)UBO變量。 它將MVP矩陣一起放入到UBO變量中。
layout (std140) uniform EngineUBO
{
mat4 ubo_Model;
mat4 ubo_View;
mat4 ubo_Projection;
vec3 ubo_ViewPos;
float ubo_Time;
};
std140是內(nèi)存布局限定符,除此之外還有std430、binding、packed等限定符。
二·、Overload對(duì)UBO的封裝
? ?Overload引擎中對(duì)UBO的封裝在UniformBuffer.h、UniformBuffer.inl、UniformBuffer.cpp文件中,將其操作包裝成了一個(gè)類(lèi)UniformBuffer。使用的時(shí)候先調(diào)用Bind,結(jié)束后UnBind,設(shè)置值使用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"
其具體實(shí)現(xiàn)在UniformBuffer.cpp中。我們先看看構(gòu)造函數(shù)代碼:
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);
// 分配內(nèi)存
glBufferData(GL_UNIFORM_BUFFER, p_size, NULL, static_cast<GLint>(p_accessSpecifier));
glBindBuffer(GL_UNIFORM_BUFFER, 0);
// 將緩存對(duì)象m_bufferID綁定到索引為p_bindingPoint的UBO上
glBindBufferRange(GL_UNIFORM_BUFFER, p_bindingPoint, m_bufferID, p_offset, p_size);
}
在構(gòu)造函數(shù)中直接創(chuàng)建了UBO的buffer,并綁定到索引是p_bindingPoint的UBO上。這里用到了OpenGL函數(shù)glBindBufferRange,如無(wú)需指定偏移量與size值可使用glBindBufferBase函數(shù)。
UniformBuffer.cpp中Bind()、UnBind()過(guò)于簡(jiǎn)單不再分析。往下接著看有個(gè)static函數(shù)BindBlockToShader。這個(gè)函數(shù)主要是顯式綁定一個(gè)uniform塊到p_bindingPoint索引,這樣可以綁定同一個(gè)緩存。這里使用到了glUniformBlockBinding函數(shù),這個(gè)函數(shù)主要是顯示指定BUO的索引,可以保證多個(gè)不同的Shader程序之間UBO的索引是一樣的,但需要在調(diào)用glLinkProgram之前調(diào)用。
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引擎中,調(diào)用這個(gè)方法是在調(diào)用glProgram之后調(diào)用的,而且索引值使用的是GetBlockLocation獲取的,這也是UBO在Shader的默認(rèn)索引值,所以這個(gè)方法應(yīng)該是可以刪除的。我注釋這個(gè)方法使用上沒(méi)有發(fā)現(xiàn)什么問(wèn)題。
最后看一下如何給UBO設(shè)置值,其實(shí)現(xiàn)是在UniformBuffer.inl文件中,主要使用glBufferSubData函數(shù),指定其偏移值與數(shù)據(jù)大小即可。
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),著色器存儲(chǔ)緩存對(duì)象,其行為類(lèi)似于UBO,但其功能上更為強(qiáng)大。首先,著色器可以寫(xiě)入buffer塊,修改其內(nèi)容并呈現(xiàn)給其他Shader或應(yīng)用程序本身。其次,可以在渲染之前再覺(jué)得其大小,而不是編譯與鏈接時(shí)。在Overload中,燈光信息是用SSBO存儲(chǔ)的,看以下Shader片段:
layout(std430, binding = 0) buffer LightSSBO
{
mat4 ssbo_Lights[];
};
在著色器中可以使用length()獲取ssbo_Lights的長(zhǎng)度。
設(shè)置SSBO的方式與設(shè)置UBO類(lèi)似,不過(guò)glBindBuffer()、glBindBufferRange()、glBindBufferBase()需要使用GL_SHADER_STORAGE_BUFFER作為目標(biāo)參數(shù)。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-728320.html
四、Overload對(duì)SSBO的封裝
Overload是將SSBO的操作封裝到類(lèi)ShaderStorageBuffer中,具體代碼就不分析了,與UBO大同小異。文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-728320.html
到了這里,關(guān)于【Overload游戲引擎細(xì)節(jié)分析】UBO與SSBO的封裝的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!