简介
Atomic Counter是OpenGL 4.2版本通过GL_ARB_shader_atomic_counters扩展引入的新特性,它可以在各种着色语言中使用。Atomic Counter具体来说指的是在缓冲区对象(Buffer Object)中存储着一个或者多个可以用来计数的变量值,对这些变量定义了特定的操作方式,可以让它们在着色语言中进行加一和减一的操作,除此之后其他所有的操作都是非法的。
关于Atomic Counter的使用场景,一个简单的案例是统计场景中哪些像素会先进行渲染。具体的实现思路:当我们调用片元Shader对像素进行着色的时候,我们可以让atomic Counter变量值加1,然后将这个值转换成一种颜色来渲染我们的像素,这样我们就可以很清楚看到那些像素点是先渲染的,哪些像素点是后渲染的了。
使用
首先我们需要创建Atomic Counter的缓冲区,和其他类型的缓冲区的实现过程都是一样的。
GLuint atomicsBuffer; glGenBuffers(1, &atomicsBuffer); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicsBuffer); glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint) * 3, NULL, GL_DYNAMIC_DRAW); glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
上面的代码生成了一个Atomic Counter Object对象,并且在它的存储区中存放了三个Atomic Counter类型的变量(Atomic Counter本质上是一个GLuint类型的变量,占4个字节) 与其他缓冲区一样,更新和获取Atomic Counter缓冲区对象的方法如下所示:
- 更新
- GLuint *userCounters;
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicsBuffer);
- userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
- 0 ,
- sizeof(GLuint) * 3,
- GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
- memset(userCounters, 0, sizeof(GLuint) *3 );
- // unmap the buffer
- glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
- 或者使用glBufferSubData的方式进行更新
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_AtomicCountersBuffer);
- GLuint a[3] = {0,0,0};
- glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0 , sizeof(GLuint) * 3, a);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
GLuint *userCounters;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicsBuffer);
userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
0 ,
sizeof(GLuint) * 3,
GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_UNSYNCHRONIZED_BIT);
memset(userCounters, 0, sizeof(GLuint) *3 );
// unmap the buffer
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
或者使用glBufferSubData的方式进行更新
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_AtomicCountersBuffer);
GLuint a[3] = {0,0,0};
glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0 , sizeof(GLuint) * 3, a);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
- 获取
- GLuint *userCounters;
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicsBuffer);
- userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
- 0,
- sizeof(GLuint) * 3,
- GL_MAP_READ_BIT
- );
- redPixels = userCounters[0];
- greenPixels = userCounters[1];
- bluePixels = userCounters[2];
- glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
- 或者使用另一种方式来获取
- GLuint userCounters[3];
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_AtomicCountersBuffer);
- glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint) * 3, userCounters);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
- redPixels = userCounters[0];
- greenPixels = userCounters[1];
- bluePixels = userCounters[2];
GLuint *userCounters;
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, atomicsBuffer);
userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
0,
sizeof(GLuint) * 3,
GL_MAP_READ_BIT
);
redPixels = userCounters[0];
greenPixels = userCounters[1];
bluePixels = userCounters[2];
glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
或者使用另一种方式来获取
GLuint userCounters[3];
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, m_AtomicCountersBuffer);
glGetBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint) * 3, userCounters);
glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
redPixels = userCounters[0];
greenPixels = userCounters[1];
bluePixels = userCounters[2];
Shader中的设置
当我们在shader中需要使用Atomic Counter的时候,我们可以定义Atomic Counter类型的变量atomic_uint,在我们定义atomic_uint类型变量的时候我们需要设置这些变量对应到AtomicCounter缓冲区对象存储空间的位置和偏移量,一般来说定义Atomic Counter变量的方式如下:
layout (binding = 1, offset = 0) uniform atomic_uint atRed; layout (binding = 2, offset = 0) uniform atomic_uint atGreen; layout (binding = 2, offset = 4) uniform atomic_uint atBlue;
atRed变量对应的binding point是1,偏移量是0 atGreen对应的binding point是2,偏移量是0 atBlue对应的binding point是2,偏移量是4(正好越过atGreen的4个字节) 需要注意的是当我们设置两个变量同样的binding和offset,那么他们实际上是同一个atomic_uint
layout (binding = 1, offset = 0) uniform atomic_uint at1; layout (binding = 1, offset = 0) uniform atomic_uint at2;
at1和at2指向的是同一个变量
尽管atomic_uint类型的变量作为uniform来使用,但是它和samplers一样并不能用在Uniform Block之中
示例
atomic_int支持的操作非常有限,仅包含一下几种:
- //获取
- uint atomicCounter(atomic_uint c);
- // 减一并返回新值
- uint atomicCounterDecrement(atomic_uint c);
- //加一并返回旧值
- uint atomicCounterIncrement(atomic_uint c);
//获取 uint atomicCounter(atomic_uint c); // 减一并返回新值 uint atomicCounterDecrement(atomic_uint c); //加一并返回旧值 uint atomicCounterIncrement(atomic_uint c);
下面这个例子来自OSG Examples,我将它转换成OpenGL的方式实现: 实现的内容:记录一帧绘制中的像素绘制顺序,先绘制的颜色呈现淡黄色,后绘制的颜色黄色逐渐加深直至为(1,1,0)的黄色,实现代码如下:
- #pragma comment(lib, "glew32.lib")
- #pragma comment(lib, "freeglut.lib")
- #include <gl/glew.h>
- #include <gl/freeglut.h>
- #include <iostream>
- #include <vector>
- #include <algorithm>
- #include "glshadertools.h"
- GLuint programID;
- GLuint vboID;
- GLuint eboID;
- GLuint atomicCounterArrayRedAndGreen[2];
- GLuint atomicCounterArrayBlue[1];
- GLuint acboRedAndGreen;
- GLuint acboBlue;
- GLfloat invNumPixel;
- //
- GLfloat vertices[] = {
- -1.0f, 1.0f,0.0f,
- -1.0f, -1.0f,0.0f,
- 1.0f, -1.0f,0.0f,
- 1.0f, 1.0f, 0.0f
- };
- GLuint indices[] = {
- 0,1,2,
- 2,3,0
- };
- //
- GLfloat xRot;
- GLfloat yRot;
- GLfloat zoom = -10.0f;
- bool mouseLeftDown;
- float mouseX, mouseY;
- void init()
- {
- programID = gltLoadShaderProgram("atomiccounter.vert", "atomiccounter.frag");
- glGenBuffers(1, &vboID);
- glBindBuffer(GL_ARRAY_BUFFER, vboID);
- glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glGenBuffers(1, &eboID);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboID);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- atomicCounterArrayBlue[0] = 0;
- atomicCounterArrayRedAndGreen[0] = 0;
- atomicCounterArrayRedAndGreen[1] = 0;
- glGenBuffers(1, &acboRedAndGreen);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboRedAndGreen);
- glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint)*2, NULL, GL_STREAM_COPY);
- glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint)*2, atomicCounterArrayRedAndGreen);
- glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 0, acboRedAndGreen);
- glGenBuffers(1, &acboBlue);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboBlue);
- glBufferData(GL_ATOMIC_COUNTER_BUFFER, sizeof(GLuint), NULL, GL_STREAM_COPY);
- glBindBufferBase(GL_ATOMIC_COUNTER_BUFFER, 2, acboBlue);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
- GLint invNumPixelLocation;
- invNumPixelLocation = glGetUniformLocation(programID, "invNumPixel");
- glProgramUniform1f(programID, invNumPixelLocation, 1.0f/(800.0f*600.0f));
- glUseProgram(programID);
- }
- void reshape(int w, int h)
- {
- glViewport(0, 0, (GLsizei)w, (GLsizei)h);
- glMatrixMode(GL_PROJECTION);
- glLoadIdentity();
- gluPerspective(60.0f, (float)(w)/h, 0.1f, 1000.0f);
- glMatrixMode(GL_MODELVIEW);
- glLoadIdentity();
- }
- void display()
- {
- glClearColor(0, 0, 0, 0);
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
- glColor3f(1.0f, 0.0f, 1.0f);
- glLoadIdentity();
- glTranslatef(0, 0, zoom);
- glRotatef(xRot, 1, 0, 0); // pitch
- glRotatef(yRot, 0, 1, 0); // heading
- //更新蓝色成分缓冲区中的数据为0,让绘制结果呈现黄色
- GLuint *userCounters;
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboBlue);
- userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
- 0,
- sizeof(GLuint) * 1,
- GL_MAP_WRITE_BIT
- );
- atomicCounterArrayBlue[0] = 0;
- glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
- //将蓝色成分缓冲区中的数据设置为0
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboBlue);
- glBufferSubData(GL_ATOMIC_COUNTER_BUFFER, 0, sizeof(GLuint), &atomicCounterArrayBlue);
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, 0);
- glEnableClientState(GL_VERTEX_ARRAY);
- glEnableClientState(GL_INDEX_ARRAY);
- glBindBuffer(GL_ARRAY_BUFFER, vboID);
- glVertexPointer(3, GL_FLOAT, 0, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, eboID);
- glIndexPointer(GL_UNSIGNED_INT, 0, 0);
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
- glBindBuffer(GL_ARRAY_BUFFER, 0);
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
- glDisableClientState(GL_VERTEX_ARRAY);
- glDisableClientState(GL_INDEX_ARRAY);
- glutSwapBuffers();
- //
- //一帧结束之后的操作
- //获取前一帧绘制的像素总数(记录在蓝色成分的AtomicBuffer之中)
- //并将这个值设置给invNumPixel
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboBlue);
- GLubyte *src = (GLubyte*)glMapBuffer(GL_ATOMIC_COUNTER_BUFFER, GL_READ_ONLY);
- if (src)
- {
- memcpy((void*)atomicCounterArrayBlue, src, 4);
- }
- glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
- unsigned int numPixel = std::max(1u, atomicCounterArrayBlue[0]);
- GLint invNumPixelLocation;
- invNumPixelLocation = glGetUniformLocation(programID, "invNumPixel");
- GLfloat invNumPixelValue;
- glGetUniformfv(programID, invNumPixelLocation, &invNumPixelValue);
- glProgramUniform1f(programID, invNumPixelLocation, 1.0f / static_cast<float>(numPixel));
- //将表示红绿成分的AtomicBuffer设置为0
- glBindBuffer(GL_ATOMIC_COUNTER_BUFFER, acboRedAndGreen);
- userCounters = (GLuint*)glMapBufferRange(GL_ATOMIC_COUNTER_BUFFER,
- 0,
- sizeof(GLuint) * 2,
- GL_MAP_READ_BIT
- );
- atomicCounterArrayRedAndGreen[0] = 0;
- atomicCounterArrayRedAndGreen[1] = 0;
- glUnmapBuffer(GL_ATOMIC_COUNTER_BUFFER);
- }
- void mouse(int button, int state, int x, int y)
- {
- mouseX = (float)x;
- mouseY = (float)y;
- switch (button)
- {
- case GLUT_LEFT_BUTTON:
- {
- if (state == GLUT_DOWN) {
- mouseLeftDown = true;
- } else if (state == GLUT_UP) {
- mouseLeftDown = false;
- }
- }
- break;
- case GLUT_RIGHT_BUTTON:
- {
- if (state == GLUT_DOWN) {
- }
- }
- break;
- default:
- break;
- }
- }
- void mouseMove(int x, int y)
- {
- if(mouseLeftDown)
- {
- yRot += (x - mouseX);
- xRot += (y - mouseY);
- mouseX = (float)x;
- mouseY = (float)y;
- }
- glutPostRedisplay();
- }
- void mouseWheel(int wheel, int direction, int x, int y)
- {
- switch (direction)
- {
- case 1: //means wheel up
- {
- zoom -= 1.0f;
- }
- break;
- case -1: //means wheel down
- {
- zoom += 1.0f;
- }
- break;
- default:
- break;
- }
- glutPostRedisplay();
- }
- void keyboard(unsigned char key, int x, int y)
- {
- switch(key)
- {
- case 27: // ESCAPE
- exit(0);
- break;
- default:
- break;
- }
- }
- void idle()
- {
- glutPostRedisplay();
- }
- int main(int argc, char** argv)
- {
- glutInit(&argc, argv);
- glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
- glutInitWindowSize(640, 480);
- glutCreateWindow(argv[0]);
- if (glewInit()) {
- std::cerr << "Unable to initialize GLEW ... exiting" << std::endl;
- exit(EXIT_FAILURE);
- }
- init();
- glutDisplayFunc(display);
- glutReshapeFunc(reshape);
- glutMouseFunc(mouse);
- glutMotionFunc(mouseMove);
- glutMouseWheelFunc(mouseWheel);
- glutKeyboardFunc(keyboard);
- glutIdleFunc(idle);
- glutMainLoop();
- }