4-使用SSBO存储动画信息
在看完GAMES104动画系统那一部分后,我也想写一个简单的动画系统,又要开一个坑了!本文将简要介绍如何用SSBO存储更多的动画信息。
SSBO
概念
SSBO,即着色器存储缓冲对象,在OpenGL4.3和Vulkan1.0中被支持。一个SSBO可以看成是一个UBO和一个纹理的混合。和UBO相比,SSBO有如下优势:
- SSBO的存储范围更大,允许存储的最低内存是128MB,而UBO是16KB;
- SSBO是可写的,而UBO是只读的;
- SSBO可以存储不定长数组,而UBO则拥有固定大小。
使用
SSBO的初始化和一般的缓冲对象相同,只是类型变成了GL_SHADER_STROAGE_BUFFER
。我目前封装的glShaderStorageBuffer
类如下:
// .h
class GLShaderStorageBuffer
{
public:
explicit GLShaderStorageBuffer(size_t bufferSize);
virtual ~GLShaderStorageBuffer();
template <typename T>
void writeSsboData(const std::vector<T>& bufferData, int bindingPoint)
{
size_t bufferSize = bufferData.size() * sizeof(T);
if (bufferSize == 0 || bufferSize > m_InitBufferSize)
{
LOG_ERROR(std::format("[{}]: BufferSize invalid (0 or > ssbo's bufferSize) !!", __FUNCTION__));
return;
}
m_CurBufferSize = bufferSize;
bind();
GLCall(glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, m_CurBufferSize, bufferData.data()));
GLCall(glBindBufferRange(GL_SHADER_STORAGE_BUFFER, bindingPoint, m_rendererID, 0, m_CurBufferSize));
unbind();
}
// 这个没测试过, 不知道对不对
template <typename T>
void readSsboData(const T* pBufferDataDst, size_t bufferSizeDst)
{
bind();
// 保证数据修改完成后再读取
GLCall(glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT));
void* ptr = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY);
memcpy_s(pBufferDataDst, bufferSizeDst, ptr, m_CurBufferSize);
GLCall(glUnmapBuffer(GL_SHADER_STORAGE_BUFFER));
unbind();
}
void bind() const;
void unbind() const;
private:
unsigned int m_rendererID;
size_t m_InitBufferSize;
size_t m_CurBufferSize;
};
// .cpp
GLShaderStorageBuffer::GLShaderStorageBuffer(size_t bufferSize)
{
m_InitBufferSize = bufferSize;
GLCall(glGenBuffers(1, &m_rendererID));
bind();
GLCall(glBufferData(GL_SHADER_STORAGE_BUFFER, m_InitBufferSize, NULL, GL_DYNAMIC_COPY));
unbind();
}
GLShaderStorageBuffer::~GLShaderStorageBuffer()
{
GLCall(glDeleteBuffers(1, &m_rendererID));
}
void GLShaderStorageBuffer::bind() const
{
GLCall(glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_rendererID));
}
void GLShaderStorageBuffer::unbind() const
{
GLCall(glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0));
}
着色器修改
别忘了修改对应着色器:
layout (std430, binding = 1) readonly buffer BoneMatrices {
mat4 finalBonesMatrices[];
};
layout (std430, binding = 2) readonly buffer BoneDqs {
mat2x4 finalBonesDQs[];
};
这里设置为只读,因为我们不需要写它。
有了SSBO,我们的最终蒙皮矩阵/对偶四元数数组就不需要设置定长了,可以根据关节数量灵活变更。
参考资料
- 《C++ Game Animation Programming》