OpenGL之UBO(Uniform Buffer Object)和SSBO(Shader Storage Buffer Object)

1,074 阅读3分钟

最近正好在学习这些,所以索性一起整理下,先说说两者的使用场景和区别吧。

UBO:和普通的uniform变量相比,就像是一个结构体,可以容纳更多变量,存储在显存的常量区,速度较快,编译时大小是确定的,而且大小是有限制的,在着色器里可读但不可写,修饰符uniform,一般用于少量的变量设置,在所有着色器都常用到。

SSBO:和UBO相比,SSBO功能更强大,在着色器里是可读可写的,修饰符buffer,存储在全局变量区,速度比UBO慢些,但是其大小可以在编译时不确定,而且大小基本没有限制,一般用于两个着色器之间处理后数据的传递,多见于计算着色器中。

一、UBO与SSBO的创建及着色器的绑定

先给着色器里的两个Uniform块的定义:

layout (std140,binding = 0) uniform UBO_data
{
    float randNum;
    float vtime;
};
layout (std430,binding = 1) buffer SSBO_data
{
    vec3 SSBO_color;
};

再是代码里与SSBO块对应声明的结构体:

struct SSBO_Data
{
    float color[3];
} ssbo_data;

什么都没有代码来得直接:

        GLuint UBO,SSBO;//声明两个Buffer对象
 
        GLint bindingIndex = 0; //设置Uniform块的binding值
	GLuint ubIndex = glGetUniformBlockIndex(programObject[0], "UBO_data"); //获取Uniform块在着色器对应的索引号,不是binding值
	glUniformBlockBinding(programObject[0], ubIndex, bindingIndex); //着色器中Uniform块的索引值与binding值的绑定,也可以在着色器中对Uniform块代码里设置binding值绑定
 
	GLint bufferSize = 0;
	glGetActiveUniformBlockiv(programObject[0], ubIndex, GL_UNIFORM_BLOCK_DATA_SIZE, &bufferSize);//获取Uniform块的大小
 
	glGenBuffers(1, &UBO);//创建UBO对象
	glBindBuffer(GL_UNIFORM_BUFFER, UBO);
	glBufferData(GL_UNIFORM_BUFFER, bufferSize, NULL, GL_DYNAMIC_READ);//分配只读的内存
 
	glBindBufferBase(GL_UNIFORM_BUFFER, 0, UBO);//将缓冲区对象绑定到索引缓冲区目标,由于对象只有一个,所以索引号为0,如果是数组,需要按索引号设置
 
	ssbo_data.color[0] = 1.0;
	ssbo_data.color[1] = 0.0;
	ssbo_data.color[2] = 0.0;
	bindingIndex = 1;
	GLuint block_index = glGetProgramResourceIndex(programObject[0], GL_SHADER_STORAGE_BLOCK, "SSBO_data");//获取着色器中索引号
	GLint BlockDataSize = 0;
	glShaderStorageBlockBinding(programObject[0], block_index, bindingIndex);//着色器中Uniform块的索引值与binding值的绑定,也可以在着色器中对Uniform块代码里设置binding值绑定
 
	glGenBuffers(1, &SSBO);
	glBindBuffer(GL_SHADER_STORAGE_BUFFER, SSBO);
	//glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(vec3), NULL, GL_DYNAMIC_DRAW);//分配内存的话这种方式也是可以的,注意数据类型就好,计算好内存大小
	glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(ssbo_data), &ssbo_data, GL_DYNAMIC_COPY);
	glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, SSBO);

上面注释也挺详细的了,耐心点看吧,glBuffData分配内存,只要内存大小设置好就没问题。

二、UBO与SSBO设置参数和使用

上面说过,SSBO是支持可读可写的,如果在着色器里改变了buffer块的数据,怎么获取呢?

起初我也百度了很久,好像没人说这一块,后面想想,可能是因为太简单了,所以没人提吧,但是对于新手来说,属实要郁闷一会儿,还好没有放弃。第二段里说了SSBO参数的传入,是通过将数据拷贝到着色器buffer块的内存地址,那么获取数据是否可以从着色器buffer块地址拷贝到结构体呢?试了下,确实是这样,代码如下:

	glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT);//隔断作用,为了让数据修改完成
	p = glMapBuffer(GL_SHADER_STORAGE_BUFFER, GL_READ_ONLY); //获取着色器buffer块内存地址
	memcpy(&ssbo_data, p, sizeof(ssbo_data));//拷贝buffer块数据到结构体
        glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);

到这里,SSBO就算完结了。

结尾:写这篇博客主要是为了检验自己学习后的理解程度,也希望能给有缘人带来帮助,有问题欢迎交流,我们一起进步!