c – 重用bindBufferBase和OpenGL计算着色器
我正在尝试在OpenGL中构建一个计算着色器来执行骨架化算法.我在仅CPU版本中测试了算法,并且它在那里是正确的.但是,我在移植它以计算着色器代码时遇到了一些麻烦.
问题是无论我运行多少次计算着色器调用,输出都不会在第一次调用之后发生变化.事实上,如果我在while循环结束时取出检查,程序永远不会终止. 我有两个用于输入和输出的内存区域.我试图在主循环中使用glBindBufferBase()进行一次技巧,我将它们中的两个交换掉(最后一轮的输出成为当前轮次的输入).见main.cpp中的第270-318行.这样我就不会在CPU和GPU之间来回复制数据了很多次. 所以,我的问题是: 1)我可以使用glBindBuffers做这个技巧,我交换它们以便我可以多次操作数据而不将其移回CPU吗?在测试较小的问题(只是添加短数组)时,它起作用了. 2)如果诀窍很好,我哪里错了? 注意:此代码需要640 x 400大小的.pgm(黑白图像),称为“test.pgm”.你可以在GIMP中制作一个,但一定要把它保存为二进制而不是ASCII. 此代码使用以下标志进行编译 g++ -g pgm.cpp main.cpp -lglut -lGLU -lGL -lm -lGLEW -o test 另外,原谅我使用C但做C风格的技巧.我在C上花的时间比在C上花的时间多. main.cpp中 // Include standard headers #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #include <math.h> #include <errno.h> //For stat #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> // Include GLEW #include <GL/glew.h> //Glut #include <GL/glut.h> //Project specific #include "skeletonize.hpp" #include "PGM.hpp" // OpenGL shader info GLuint programID; GLuint output_image; #define IMG_0 0 #define IMG_1 1 #define CMD 2 #define NUM_BUFS 3 #define CMD_BUF_WIDTH 0 #define CMD_BUF_HEIGHT 1 #define CMD_BUF_CMD 2 #define CMD_BUF_RESPONSE 3 #define CMD_BUF_LEN 4 #define CMD_EXPAND 1 #define CMD_THIN_N 2 #define CMD_THIN_S 3 #define CMD_THIN_E 4 #define CMD_THIN_W 5 #define CMD_NORMALIZE 6 #define CMD_REGULARIZE 7 #define INITIALIZED 0 #define NOT_FINISHED 1 GLuint computeProgram; GLuint buffers[NUM_BUFS]; //SSBO objects,one for IMG_0,one for IMG_1,and one for commands/response static GLchar* computeSource; GLuint shaderProgram; //TODO: don't really need 2 textures yet,but will eventually when doing overlay of original image. GLuint textures[2]; GLchar* LoadSource(const char* pFile) { struct stat buf; GLchar *source; int fd; if (stat(pFile,&buf) == -1) { printf("Error opening filen"); printf("Error: %sn",strerror(errno)); return NULL; } fd = open(pFile,O_RDONLY); if (fd == -1) { printf("Error opening file. Error: %sn",strerror(errno)); return NULL; } source = new GLchar[buf.st_size + 1]; if (read(fd,source,buf.st_size) == -1) { printf("Error reading file. Error: %sn",strerror(errno)); delete[] source; return NULL; } source[buf.st_size] = ' '; //Shader compiler needs null to know end of input return source; } // Shader sources const GLchar* vertexSource = "#version 450 coren" "in vec2 position;" "in vec2 texcoord;" "out vec2 Texcoord;" "void main()" "{" " Texcoord = texcoord;" " gl_Position = vec4(position,0.0,1.0);" "}"; const GLchar* fragmentSource = "#version 450 coren" "in vec2 Texcoord;" "out vec4 outColor;" "uniform sampler2D texData;" "void main()" "{" " vec4 imColor = texture(texData,Texcoord);" " outColor = vec4(0.0,imColor.r,1.0);" //" outColor = texture(texData,Texcoord);" //" outColor = vec4(1.0,1.0,1.0);" "}"; void checkError(int line) { GLint err; do { err = glGetError(); switch (err) { case GL_NO_ERROR: //printf("%d: No errorn",line); break; case GL_INVALID_ENUM: printf("%d: Invalid enum!n",line); break; case GL_INVALID_VALUE: printf("%d: Invalid valuen",line); break; case GL_INVALID_OPERATION: printf("%d: Invalid operationn",line); break; case GL_INVALID_FRAMEBUFFER_OPERATION: printf("%d: Invalid framebuffer operationn",line); break; case GL_OUT_OF_MEMORY: printf("%d: Out of memoryn",line); break; default: printf("%d: glGetError default case. Should not happen!n",line); } } while (err != GL_NO_ERROR); } void display() { glClearColor(0.0f,0.0f,1.0f,0.0f); glClear(GL_COLOR_BUFFER_BIT); glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0); glFlush(); glutSwapBuffers(); } void reshape(int width,int height) { double w2h = (height>0) ? (double)width/height : 1; // Set viewport as entire window glViewport(0,width,height); } void runComputeProgram(uint32_t *data,uint32_t *data2) { int width = 640; int height = 400; uint32_t *ptr; uint32_t cmd[CMD_BUF_LEN]; computeSource = LoadSource("compute.shader"); if (computeSource == NULL) { return; } GLuint computeShader = glCreateShader(GL_COMPUTE_SHADER); glShaderSource(computeShader,1,&computeSource,NULL); glCompileShader(computeShader); computeProgram = glCreateProgram(); glAttachShader(computeProgram,computeShader); glLinkProgram(computeProgram); GLint status; glGetProgramiv(computeProgram,GL_LINK_STATUS,&status); if (status == GL_TRUE) { printf("link goodn"); } else { printf("link badn"); GLchar log[4096]; GLsizei len; glGetProgramInfoLog(computeProgram,4096,&len,log); printf("%sn",log); return; } // First action is to transform the image into binary values (0,1) cmd[CMD_BUF_CMD] = CMD_NORMALIZE; cmd[CMD_BUF_WIDTH] = width; cmd[CMD_BUF_HEIGHT] = height; glGenBuffers(NUM_BUFS,buffers); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[CMD]); glBufferData(GL_SHADER_STORAGE_BUFFER,sizeof(cmd),cmd,GL_DYNAMIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_0]); glBufferData(GL_SHADER_STORAGE_BUFFER,sizeof(uint32_t) * width * height,data,2,buffers[IMG_1]); glBufferData(GL_SHADER_STORAGE_BUFFER,data2,GL_DYNAMIC_DRAW); glUseProgram(computeProgram); glDispatchCompute(width / 16,height / 16,1); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_1]); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); // Rebind ptr for our while loop glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[CMD]); //glBufferData(GL_SHADER_STORAGE_BUFFER,GL_DYNAMIC_DRAW); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); int i = 0; do { printf("iteration: %d",i); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); cmd[CMD_BUF_RESPONSE] = INITIALIZED; switch (i % 4) { case 0: cmd[CMD_BUF_CMD] = CMD_THIN_N; break; case 1: cmd[CMD_BUF_CMD] = CMD_THIN_S; break; case 2: cmd[CMD_BUF_CMD] = CMD_THIN_E; break; case 3: cmd[CMD_BUF_CMD] = CMD_THIN_W; break; } glBufferData(GL_SHADER_STORAGE_BUFFER,GL_DYNAMIC_DRAW); glDispatchCompute(width / 16,1); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); if (i % 2 == 0) { printf("Input is now img_1. Output is img_0n"); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_1]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_0]); checkError(__LINE__); } else { printf("Input is now img_0. Output is img_1n"); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_0]); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_1]); checkError(__LINE__); } glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[CMD]); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); printf("cmd issued at start: %d response: %dn",ptr[CMD_BUF_CMD],ptr[CMD_BUF_RESPONSE]); i++; } while(ptr[CMD_BUF_RESPONSE] != INITIALIZED && i < 10); //Using i < 10,otherwise this never terminates glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); // Free ptr // Transform Binary image (0,1) to (0,0xFFFFFFFF) values for texture display cmd[CMD_BUF_CMD] = CMD_REGULARIZE; glBindBufferBase(GL_SHADER_STORAGE_BUFFER,GL_DYNAMIC_DRAW); glDispatchCompute(width / 16,1); glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[CMD]); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); printf("Regularize: cmd: %d width: %d height: %d response: %dn",ptr[CMD_BUF_WIDTH],ptr[CMD_BUF_HEIGHT],ptr[CMD_BUF_RESPONSE]); // Create texure glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glGenTextures(2,textures); checkError(__LINE__); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,textures[0]); checkError(__LINE__); if (i % 2 == 0) { printf("output image is img_1n"); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_1]); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); } else { printf("output image is img_0n"); glBindBufferBase(GL_SHADER_STORAGE_BUFFER,buffers[IMG_0]); ptr = (uint32_t *)glMapBuffer(GL_SHADER_STORAGE_BUFFER,GL_READ_ONLY); } glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); glUseProgram(shaderProgram); glTexImage2D(GL_TEXTURE_2D,GL_RGB,height,GL_RED,ptr); //TODO: this is wrong. worry about later. checkError(__LINE__); glUniform1i(glGetUniformLocation(shaderProgram,"texData"),0); checkError(__LINE__); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); checkError(__LINE__); } void initGL() { // Vertices & texture init GLuint vao; glGenVertexArrays(1,&vao); glBindVertexArray(vao); GLuint vbo; glGenBuffers(1,&vbo); GLfloat vertices[] = { // X Y S T -1.0f,// Top-left 1.0f,// Top-right 1.0f,-1.0f,// Bottom-right -1.0f,1.0f // Bottom-left }; glBindBuffer(GL_ARRAY_BUFFER,vbo); glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW); GLuint ebo; glGenBuffers(1,&ebo); GLuint elements[] = { 0,3,0 }; glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,ebo); glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(elements),elements,GL_STATIC_DRAW); // Create shaders GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader,&vertexSource,NULL); glCompileShader(vertexShader); GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader,&fragmentSource,NULL); glCompileShader(fragmentShader); shaderProgram = glCreateProgram(); glAttachShader(shaderProgram,vertexShader); glAttachShader(shaderProgram,fragmentShader); glBindFragDataLocation(shaderProgram,"outColor"); glLinkProgram(shaderProgram); glUseProgram(shaderProgram); // Vertex data specification GLint posAttrib = glGetAttribLocation(shaderProgram,"position"); glEnableVertexAttribArray(posAttrib); glVertexAttribPointer(posAttrib,GL_FLOAT,GL_FALSE,4 * sizeof(GLfloat),0); GLint texAttrib = glGetAttribLocation(shaderProgram,"texcoord"); glEnableVertexAttribArray(texAttrib); glVertexAttribPointer(texAttrib,(void *)(2 * sizeof(GLfloat))); checkError(__LINE__); } int main(int argc,char** argv) { // Image setup PGM pgmImage; pgmImage.ReadFile("test.pgm"); uint32_t *data = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()]; uint32_t *data2 = new uint32_t[pgmImage.GetHeight() * pgmImage.GetWidth()]; unsigned int size = pgmImage.GetHeight() * pgmImage.GetWidth(); uint8_t *pgmData = pgmImage.GetData(); for (int i=0; i < size; i++) { data[i] = pgmData[i]; } int count = 0; for (int i =0; i < pgmImage.GetHeight() * pgmImage.GetWidth(); i++) { if (data[i] == 0xFF) { count++; } } printf("count: %dn",count); // Window Setup glutInitWindowSize(640,400); glutInitWindowPosition (140,140); glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); glutInit(&argc,argv); glutCreateWindow( "OpenGL Application" ); glutDisplayFunc(display); glutReshapeFunc(reshape); glewExperimental = true; if (glewInit() != GLEW_OK) { fprintf(stderr,"Failed to initialize GLEWn"); return -1; } initGL(); runComputeProgram(data,data2); checkError(__LINE__); glutMainLoop(); return 0; } compute.shader #version 450 core //#extension GL_NV_shader_buffer_load : enable #define WIDTH 0 // Width of image #define HEIGHT 1 // Height of image #define CMD 2 // Command to execute #define RESPONSE 3 // Response to command #define BUF_LEN 4 #define CMD_UNUSED 1 // TODO: remove this. Will have to be mirroed in C code. #define CMD_THIN_N 2 #define CMD_THIN_S 3 #define CMD_THIN_E 4 #define CMD_THIN_W 5 #define CMD_NORMALIZE 6 #define CMD_REGULARIZE 7 #define NOT_FINISHED 1 layout (local_size_x = 16,local_size_y = 16) in; //layout (local_size_x = 1) in; //TODO: remove layout (std430,binding = 0) buffer Cmd { uint cmd_buf[BUF_LEN]; //Width,command,response }; layout (std430,binding = 1) buffer Img1 { uint image_0[]; }; layout (std430,binding = 2) buffer Img2 { uint image_1[]; }; int sigma(uint data[9]) { int i; int sigma = 0; // Assume 9 pixels,A0 (pixel of interest) -> A8 // In image,A0 is center // 1 2 3 // 8 0 4 // 7 6 5 for (i=1; i < 9; i++) { sigma += int(data[i]); } return sigma; } int chi(uint data[9]) { int chi; // Assume 9 pixels,A0 (pixel of interest) -> A8 // 1 2 3 // 8 0 4 // 7 6 5 chi = int(data[1] != data[3]) + int(data[3] != data[5]) + int(data[5] != data[7]) + int(data[7] != data[1]) + 2 * ( int((data[2] > data[1]) && (data[2] > data[3])) ) + int((data[4] > data[3]) && (data[4] > data[5])) + int((data[6] > data[5]) && (data[6] > data[7])) + int((data[8] > data[7]) && (data[8] > data[1])); return chi; } // 1 2 3 // 8 0 4 // 7 6 5 int getPos(in int x,int y) { return y * int(cmd_buf[WIDTH]) + x; } uint getVal(in int pos) { return image_0[ uint(pos) ]; } int removePoint(uint neighborhood[9]) { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); if (chi(neighborhood) == 2 && sigma(neighborhood) != 1) { image_1[getPos(x,y)] = 0; cmd_buf[RESPONSE] = NOT_FINISHED; return 1; } else { //TODO: needed? Swapping back and forth between input and output should account for this image_1[getPos(x,y)] = 1; } return 0; } void getNeighborhood(inout uint neighborhood[9]) { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); int bottom = int(cmd_buf[WIDTH] * (cmd_buf[HEIGHT] - 1)); int pos = getPos(x,y); int width = int(cmd_buf[WIDTH]); int height = int(cmd_buf[HEIGHT]); uint pixel; int i = 0; for (i=1; i < 9; i++) { neighborhood[i] = 2; } if (pos < width) { // Pixel on top,fill outiside image with zero neighborhood[1] = 0; neighborhood[2] = 0; neighborhood[3] = 0; } if (pos % width == 0) { // Pixel is on left edge. Fill area outside of image with zero neighborhood[1] = 0; neighborhood[8] = 0; neighborhood[7] = 0; } if ((pos % width) == (width - 1)) { // Pixel is on right edge. neighborhood[3] = 0; neighborhood[4] = 0; neighborhood[5] = 0; } if (pos >= bottom) { // Pixel is on bottom edge. neighborhood[5] = 0; neighborhood[6] = 0; neighborhood[7] = 0; } // Get remaining pixels for (i=1; i < 9; i++) { if (neighborhood[i] == 2) { switch (i) { case 1: // Upper left pixel neighborhood[i] = getVal(pos - 1 - width); break; case 2: // Upper middle pixel neighborhood[i] = getVal(pos - width); break; case 3: // Upper right pixel neighborhood[i] = getVal(pos + 1 - width); break; case 4: // Right pixel neighborhood[i] = getVal(pos + 1); break; case 5: // Bottom right pixel neighborhood[i] = getVal(pos + width + 1); break; case 6: // Bottom middle pixel neighborhood[i] = getVal(pos + width); break; case 7: // Bottom left pixel neighborhood[i] = getVal(pos + width - 1); break; case 8: // Left pixel neighborhood[i] = getVal(pos - 1); break; } } } } void normalize() { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); uint val = image_0[getPos(x,y)] == 0x0 ? 0 : 1; image_0[getPos(x,y)] = val; image_1[getPos(x,y)] = val; } void regularize() { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); uint val = image_0[getPos(x,y)] == 0x0 ? 0 : 0xFFFFFFFF; if (val != 0xFFFFFFFF) { cmd_buf[RESPONSE] = 99; //Test Value -- TODO: remove } image_1[getPos(x,y)] = val; } // North-South-East-West skeletonization void skeleton() { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); uint neighborhood[9]; neighborhood[0] = getVal(getPos(x,y)); // Only consider cases where the center is 1 if (neighborhood[0] != 1) { return; } getNeighborhood(neighborhood); switch (cmd_buf[CMD]) { case CMD_THIN_N: //north if (neighborhood[2] == 0 && neighborhood[6] == 1) { removePoint(neighborhood); } break; case CMD_THIN_S: //south if (neighborhood[2] == 1 && neighborhood[6] == 0) { removePoint(neighborhood); } break; case CMD_THIN_E: //east if (neighborhood[4] == 0 && neighborhood[8] == 1) { removePoint(neighborhood); } break; case CMD_THIN_W: //west if (neighborhood[4] == 1 && neighborhood[8] == 0) { removePoint(neighborhood); } break; } } void main() { switch (cmd_buf[CMD]) { case CMD_THIN_N: case CMD_THIN_S: case CMD_THIN_E: case CMD_THIN_W: skeleton(); break; case CMD_NORMALIZE: normalize(); break; case CMD_REGULARIZE: regularize(); break; } } pgm.cpp #include "PGM.hpp" #define PGM_HEADER "P5" PGM::PGM() { mpData = NULL; Clear(); } PGM::~PGM() { Clear(); } uint8_t* PGM::GetData() { return mpImgData; } uint16_t PGM::GetWidth() { return mWidth; } uint16_t PGM::GetHeight() { return mHeight; } uint8_t PGM::GetMaxWhite() { return mMaxWhite; } void PGM::Clear() { if (mpData != NULL) { delete[] mpData; } mpImgData = NULL; mWidth = 0; mHeight = 0; mMaxWhite = 255; } // Finds the int PGM::PopulateFields(size_t size) { int i; bool EOL = false; bool haveWhite = false; bool comment = false; if (mpData == NULL) { return -1; } // Check header if ((mpData[0] != 0x50) || (mpData[1] != 0x35)) { return -2; } //Ignore the comment //Start at 3rd position in file,after "P5" header for (i = 2; i < size; i++) { if (mpData[i] == '#') { comment = true; continue; } if (mpData[i] == 0x0A && comment == true) { comment = false; break; } if (comment == true) { continue; } } // Get width and height i++; sscanf((char *)&mpData[i],"%4" SCNu16 " %4" SCNu16,&mWidth,&mHeight); for (i; i < size; i++) { //Move past the width and height we just found if (mpData[i] == 0x0A && EOL == false) { EOL = true; continue; } // If past the width and height,now at the range. Save it. if (EOL == true && haveWhite == false) { sscanf((char *)&mpData[i],"%3" SCNu8,&mMaxWhite); haveWhite = true; } if (haveWhite == true && mpData[i] == 0x0A) { i++; //Move to next element - start of the actual data break; } } if (i == size) { return -3; //Did not find the start of data. } mpImgData = &mpData[i]; return 0; } // Reads a PGM file. Returns 0 on success,other values on failure int PGM::ReadFile(const char *pPath) { struct stat st; int fd; if (this->mpData != NULL) { Clear(); } if (stat(pPath,&st) != 0) { return 1; } fd = open(pPath,O_RDONLY); if (fd == -1) { return 1; } //this->mpData = (uint8_t *) malloc(st.st_size); mpData = new uint8_t[st.st_size]; if (this->mpData == NULL) { return 2; } if (read(fd,this->mpData,st.st_size) == -1) { Clear(); } close(fd); PopulateFields(st.st_size); return 0; } pgm.hpp #ifndef __PGM_H__ #define __PGM_H__ #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <inttypes.h> class PGM { public: int ReadFile(const char *pPath); uint8_t* GetData(); uint16_t GetWidth(); uint16_t GetHeight(); uint8_t GetMaxWhite(); PGM(); ~PGM(); private: void Clear(); int PopulateFields(size_t size); uint8_t *mpData; uint8_t *mpImgData; uint16_t mWidth; uint16_t mHeight; uint8_t mMaxWhite; }; #endif // __PGM_H__ 解决方法
(答案也张贴在交叉发布的网站上)
我找到了!问题源于交换中的问题.当我们交换缓冲区时,输出缓冲区成为输入,输入缓冲区成为输出.但是,现在输出的缓冲区未更新以匹配! To illustrate: Init: In Out 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 First Iteration (remove north corners): In Out 1 1 1 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 Swap In Out 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 Second iteration (remove east corners): In Out 0 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 ... (and so on) 通过修改计算着色器中的skeleton()以将image_0复制到image_1,在此之前,算法可以正常工作. void skeleton() { int x = int(gl_GlobalInvocationID.x); int y = int(gl_GlobalInvocationID.y); uint neighborhood[9]; neighborhood[0] = getVal(getPos(x,y)); image_1[getPos(x,y)] = image_0[getPos(x,y)]; // Only consider cases where the center is 1 if (neighborhood[0] != 1) { image_1[0] = 0; return; } ... 另外,我清理了相当多的代码.例如,您可以在调度调用之前通过glBindBufferBase()设置输入和输出缓冲区. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- c# – 安装了PagedList软件包,控制器中出现“缺少程序集引用
- postgresql – 减少包含实体的JPQL POJO的查询数
- ACE_Reactor(五)ACE_TP_Reactor和ACE_Select_Reactor的区别
- postgresql – 如何使用pgAdmin在Ubuntu上连接Postgres到本
- Oracle 12c 报错:ORA-28040: No matching authentication
- Oracle数据库:实例、用户、表、表空间
- sqlite manager相当好用,是FF插件
- 在Magento 2中创建管理员菜单
- C中的命令行参数
- vb.net合作版版机房收费系统常见问题汇总