OpenGL渐变“条带”工件

时间:2013-04-15 00:29:43

标签: opengl

最近我一直试图清理我的openGl渲染。我现在已经有这些文物了一段时间,但从来没有真正考虑过它。这是一个屏幕截图: enter image description here

enter image description here 经过一番研究后,我无法弄清楚它有什么问题。我在OSX上使用OpenGl,但我在其他系统上尝试过它,并且会出现相同的工件。

2 个答案:

答案 0 :(得分:6)

您遇到的是每通道色彩空间8位的有限动态范围。简单的灰度梯度,即每通道8比特帧缓冲器上的R = B = G,可以仅具有2 ^ 8 = 256个不同的值。如果您在较大区域内的相似值(如图片中)之间进行转换,则结果是低动态范围条带。

解决此问题的唯一方法是使用更多位来计算梯度。为了在低动态范围屏幕上显示图像,您可以使用抖动。

答案 1 :(得分:2)

作为explained by datenwolf,问题是将子像素值四舍五入为8位范围,并且在大多数OpenGL实现上都是GL_DITHER的无操作实现。为了缓解这种情况,您可以将抖动作为后处理步骤(另一个选项是直接在片段着色器中为每个相关基元执行此操作)。但请注意,OpenGL实现有一些要求:

  1. 支持高精度纹理内部格式:您必须能够访问所得颜色的额外精度。更精确,更精细的颜色变化,您可以平滑地渲染。选项包括:

    • 浮点纹理(需要GL_ARB_texture_float或OpenGL 3.0+)llike RGBA32FRGBA16FR11F_G11F_B10F

    • 宽于8位整数格式,例如RGB10_A2RGBA16

  2. FBO(GL_ARB_framebuffer_object):您确实想要进行后期处理。

  3. NPOT纹理:大多数屏幕都具有非二维功率;对于窗口模式来说,它更为重要。

  4. GLSL:您确实想要使用着色器。另一个选项是GL_ARB_fragment_program

  5. 正确支持高精度帧缓冲配置。如果您选择整体纹理格式,如RGBA16,它可能会默默地替换为例如RGBA8TexImage2D(请参阅MONITOR_6_BPP可能的内部格式规范)。

  6. 所有这些都说,这是一个演示使用上面提到的功能(和浮点纹理,以确保结果),允许比较无抖动的亮灰色立方体与抖动的灰色立方体。请注意,默认情况下,由于实际上是6bpp而不是8bpp,因此监视器会执行自己的抖动会产生错误的结果。对于6bpp的监视器,在编译此代码时定义// Dithering shader implementation inspired by (and completely reworked): // http://www.anisopteragames.com/how-to-fix-color-banding-with-dithering/ #include <stdio.h> #include <string.h> #include <sys/time.h> #include <GL/glew.h> #include <GL/glut.h> GLboolean animating=GL_TRUE, dithering=GL_FALSE; GLuint ditherProgram=0; GLuint ditherShader=0; GLuint frameTexture=0,bayerMatrixTexture=0; GLuint frameFramebuffer=0, depthRenderBuffer=0; void initDitheringShader() { ditherProgram=glCreateProgram(); ditherShader=glCreateShader(GL_FRAGMENT_SHADER); const char* src= "uniform sampler2D frame, bayerMatrix;\n" "void main()\n" "{\n" " vec4 color=texture2D(frame,gl_TexCoord[0].xy);\n" " float bayer=texture2D(bayerMatrix,gl_FragCoord.xy/8.).r*(255./64.); // scaled to [0..1]\n" #ifdef MONITOR_6_BPP // use this for 6 bit per subpixel monitors " const float rgbByteMax=63.;\n" #else " const float rgbByteMax=255.;\n" #endif " vec4 rgba=rgbByteMax*color;\n" " vec4 head=floor(rgba);\n" " vec4 tail=rgba-head;\n" " color=head+step(bayer,tail);\n" " gl_FragColor=color/rgbByteMax;\n" "}\n" ; const GLint length=strlen(src); glShaderSource(ditherShader,1,&src,&length); glCompileShader(ditherShader); GLint status; glGetShaderiv(ditherShader,GL_COMPILE_STATUS,&status); if(!status) { fprintf(stderr,"Failed to compile shader\n"); exit(2); } glAttachShader(ditherProgram,ditherShader); glLinkProgram(ditherProgram); glGetProgramiv(ditherProgram,GL_LINK_STATUS,&status); if(!status) { fprintf(stderr,"Failed to link shading program\n"); exit(3); } static const char bayerPattern[] = { 0, 32, 8, 40, 2, 34, 10, 42, /* 8x8 Bayer ordered dithering */ 48, 16, 56, 24, 50, 18, 58, 26, /* pattern. Each input pixel */ 12, 44, 4, 36, 14, 46, 6, 38, /* is scaled to the 0..63 range */ 60, 28, 52, 20, 62, 30, 54, 22, /* before looking in this table */ 3, 35, 11, 43, 1, 33, 9, 41, /* to determine the action. */ 51, 19, 59, 27, 49, 17, 57, 25, 15, 47, 7, 39, 13, 45, 5, 37, 63, 31, 55, 23, 61, 29, 53, 21,}; glGenTextures(1,&bayerMatrixTexture); glBindTexture(GL_TEXTURE_2D,bayerMatrixTexture); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, 8,8, 0,GL_LUMINANCE, GL_UNSIGNED_BYTE, bayerPattern); } void initLightAndMaterial() { const GLfloat ambient[4]={0.5,0.5,0.5,1}; glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ambient); const GLfloat emission[4]={0.2,0.2,0.2,1}; glMaterialfv(GL_FRONT, GL_EMISSION, emission); const GLfloat diffuseColor[4]={1,1,1,1}; glLightfv(GL_LIGHT0, GL_DIFFUSE, diffuseColor); const GLfloat position[4]={2,0,-2,1}; glLightfv(GL_LIGHT0, GL_POSITION, position); glShadeModel(GL_SMOOTH); glEnable(GL_LIGHT0); const GLfloat globalAmbient[4]={0,0,0,1}; glLightModelfv(GL_LIGHT_MODEL_AMBIENT,globalAmbient); } void checkRequirements() { if(!GL_ARB_texture_float) { fputs("Float textures are not supported, this demo relies on them\n",stderr); exit(1); } if(!GL_ARB_framebuffer_object) { fputs("FBO is not supported, no good way to do postprocessing\n",stderr); exit(1); } /* We need OpenGL 2.0+ for GLSL and NPOT textures. * Extension interface fot GL_ARB_shader_objects is too * different from core so not trying to use it. */ if(!GLEW_VERSION_2_0) { fprintf(stderr,"Need OpenGL>=2.0 for GLSL and NPOT textures\n"); exit(1); } } GLboolean init() { checkRequirements(); initLightAndMaterial(); initDitheringShader(); return 1; } unsigned getTime() { struct timeval tv; gettimeofday(&tv,NULL); return tv.tv_usec/1000+tv.tv_sec*1000; } void renderScene() { glClearColor(0,0,0,1); glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); static unsigned oldTime; if(!oldTime) oldTime=getTime(); const unsigned curTime=getTime(); if(animating) { static double angle=0; glMatrixMode(GL_MODELVIEW); glLoadIdentity(); angle += (curTime-oldTime)%13500 * 360 / 13500.; glRotatef(angle,1,0,0); glRotatef(angle,0,1,0); } oldTime=curTime; typedef struct CubeVertex { GLfloat x, y, z; GLfloat nx, ny, nz; GLfloat u, v; } CubeVertex; static const CubeVertex vertices[] = { // x y z nx ny nz u v { 1,-1,-1, 0, 0,-1, 0,1}, { 1, 1,-1, 0, 0,-1, 0,0}, {-1, 1,-1, 0, 0,-1, 1,0}, {-1,-1,-1, 0, 0,-1, 1,1}, {-1,-1,-1, -1, 0, 0, 0,1}, {-1, 1,-1, -1, 0, 0, 0,0}, {-1, 1, 1, -1, 0, 0, 1,0}, {-1,-1, 1, -1, 0, 0, 1,1}, {-1,-1, 1, 0, 0, 1, 0,1}, {-1, 1, 1, 0, 0, 1, 0,0}, { 1, 1, 1, 0, 0, 1, 1,0}, { 1,-1, 1, 0, 0, 1, 1,1}, { 1,-1, 1, 1, 0, 0, 0,1}, { 1, 1, 1, 1, 0, 0, 0,0}, { 1, 1,-1, 1, 0, 0, 1,0}, { 1,-1,-1, 1, 0, 0, 1,1}, { 1,-1,-1, 0,-1, 0, 0,1}, {-1,-1,-1, 0,-1, 0, 0,0}, {-1,-1, 1, 0,-1, 0, 1,0}, { 1,-1, 1, 0,-1, 0, 1,1}, { 1, 1, 1, 0, 1, 0, 0,1}, {-1, 1, 1, 0, 1, 0, 0,0}, {-1, 1,-1, 0, 1, 0, 1,0}, { 1, 1,-1, 0, 1, 0, 1,1}, }; const GLushort indices[]= { 0, 1, 2, 2, 3, 0, 4, 5, 6, 6, 7, 4, 8, 9,10, 10,11, 8, 12,13,14, 14,15,12, 16,17,18, 18,19,16, 20,21,22, 22,23,20, }; glEnableClientState(GL_VERTEX_ARRAY); glEnableClientState(GL_NORMAL_ARRAY); glFrontFace(GL_CW); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glVertexPointer(3, GL_FLOAT, sizeof(CubeVertex), vertices); glNormalPointer(GL_FLOAT, sizeof(CubeVertex), (char*)vertices+3*sizeof(GLfloat)); glDrawElements(GL_TRIANGLES, sizeof indices/sizeof*indices, GL_UNSIGNED_SHORT, indices); glDisable(GL_LIGHTING); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glFrontFace(GL_CW); glDisableClientState(GL_NORMAL_ARRAY); glDisableClientState(GL_VERTEX_ARRAY); } void blitFBToScreen() { glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,frameTexture); if(dithering) { const GLint frameLoc=glGetUniformLocation(ditherProgram,"frame"); if(frameLoc==-1) fprintf(stderr,"Failed to get location of frame uniform\n"); glUseProgram(ditherProgram); glUniform1i(frameLoc,0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D,bayerMatrixTexture); const GLint bayerMatrixLoc=glGetUniformLocation(ditherProgram,"bayerMatrix"); if(bayerMatrixLoc==-1) fprintf(stderr,"Failed to get location of Bayer matrix uniform\n"); glUniform1i(bayerMatrixLoc,1); } glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glOrtho(0,1,0,1,-1,1); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D,frameTexture); glEnable(GL_TEXTURE_2D); glBegin(GL_QUADS); glTexCoord2f(0,0); glVertex2f(0,0); glTexCoord2f(0,1); glVertex2f(0,1); glTexCoord2f(1,1); glVertex2f(1,1); glTexCoord2f(1,0); glVertex2f(1,0); glEnd(); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,0); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); glUseProgram(0); } void display() { static GLboolean inited; if(!inited) inited=init(); glBindFramebuffer(GL_FRAMEBUFFER,frameFramebuffer); renderScene(); glBindFramebuffer(GL_FRAMEBUFFER,0); blitFBToScreen(); glutSwapBuffers(); glutPostRedisplay(); } void reshape(int width, int height) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION); glLoadIdentity(); const float aspect=(float)width/height; gluPerspective(180./4, aspect, 1, 100); if(aspect<1) { const GLfloat fixup[4*4]= { aspect, 0, 0, 0, 0, aspect, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }; glMultMatrixf(fixup); } gluLookAt(0, 0,-5, 0, 0, 0, 0, 1, 0); // reinitialize FBO if(!frameTexture) glGenTextures(1,&frameTexture); glBindTexture(GL_TEXTURE_2D,frameTexture); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA32F,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); if(!frameFramebuffer) glGenFramebuffers(1,&frameFramebuffer); glBindFramebuffer(GL_FRAMEBUFFER,frameFramebuffer); if(!depthRenderBuffer) glGenRenderbuffers(1,&depthRenderBuffer); glBindRenderbuffer(GL_RENDERBUFFER,depthRenderBuffer); glRenderbufferStorage(GL_RENDERBUFFER,GL_DEPTH_COMPONENT32,width,height); glFramebufferRenderbuffer(GL_FRAMEBUFFER,GL_DEPTH_ATTACHMENT,GL_RENDERBUFFER,depthRenderBuffer); glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,frameTexture,0); GLenum status=glCheckFramebufferStatus(GL_FRAMEBUFFER); if(status!=GL_FRAMEBUFFER_COMPLETE) { fprintf(stderr,"Error: framebuffer is incomplete: status=%#x\n",status); exit(10); } glBindFramebuffer(GL_FRAMEBUFFER,0); glBindRenderbuffer(GL_RENDERBUFFER,0); glBindTexture(GL_TEXTURE_2D,0); } void keyboard(unsigned char key,int x,int y) { char winTitle[1024]; switch(key) { case ' ': animating=!animating; snprintf(winTitle,sizeof winTitle,"Animation %sabled",animating?"en":"dis"); break; case 'd': dithering=!dithering; snprintf(winTitle,sizeof winTitle,"Dithering %sabled",dithering?"en":"dis"); break; default: return; } glutSetWindowTitle(winTitle); glutPostRedisplay(); } int main(int argc, char** argv) { glutInit(&argc, argv); glutInitDisplayMode (GLUT_DOUBLE|GLUT_RGB); glutInitWindowSize (1200, 900); glutCreateWindow ("Dithering test"); if(glewInit()!=GLEW_OK) { fputs("Failed to init GLEW\n",stderr); exit(1); } glutReshapeFunc(reshape); glutDisplayFunc(display); glutKeyboardFunc(keyboard); fputs("Press <SPACE> to toggle animation, 'd' to toggle dithering\n",stderr); glutMainLoop(); return 0; }

    $ bin/zodbpack -d 7 zodbpack-conf.xml