使用1D纹理缓冲区时,GLSL出现意外结果



在下面的测试程序中,我试图在屏幕左下角(500 x 500)呈现一个绿色正方形(250 x 250)。

我使用两个缓冲区纹理将两个int数组(每个数组的大小为500*500)传递给片段着色器。数组简单地填充x(第一个数组)和y(第二个数组)值,这些值对应于屏幕空间中的所有(x,y)坐标(如下面代码的init()部分所示)。

在片段着色器中,如果当前片段位置(在屏幕空间中)在x和y方向上都小于250.0,则该片段将显示为绿色。否则,它将显示为红色。图像如下所示

下面是完整的OpenGL代码,以及贯穿顶点着色器(位于主程序中)和片段着色器(从文件中读取)。在片段着色器中,有三个测试(A、B和C)测试B和C未按预期工作。他们在右下角而不是左下角画绿色正方形

测试A(正确):当我在条件if检查中使用gl_FragCoord.xy时,此代码的输出按预期工作(屏幕左下角的绿色正方形)。

测试B(不正确):如果我使用缓冲区纹理和texelFetch来检索当前片段的x和y值,然后在条件If检查中使用该值,则会在右下角绘制绿色正方形。

测试C(不正确):如果我放弃缓冲区纹理,而只是计算当前片段索引,并使用简单的mod和除法来获得x,y索引,那么绿色正方形仍然绘制在右下角。

任何见解都将不胜感激。

我正在使用:

Ubuntu 12.04.5 LTS, 64 bit
glGetString(GL_VERSION) = 4.4.0 NVIDIA 331.113 

主程序:mytest.cc

// This program was modified from:
// https://www.opengl.org/discussion_boards/showthread.php/173917-samplerBuffer-example-needed
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <iostream>
#include <fstream>
#define GL_GLEXT_PROTOTYPES 1
#include <GL/gl.h>
#include <GL/glut.h>
using namespace std;
static const char Vertex_src[] =
  "void main(void)               n"
  "{                             n"
  "  gl_Position = ftransform(); n"
  "}                             n";
std::string readFile(const char *filePath)
{
    std::string content;
    std::ifstream fileStream(filePath, std::ios::in);
    if(!fileStream.is_open()) {
        std::cerr << "Could not read file " << filePath << ". File does not exist." << std::endl;
        return "";
    }
    std::string line = "";
    while(!fileStream.eof()) {
        std::getline(fileStream, line);
        content.append(line + "n");
    }
    fileStream.close();
    return content;
}
void keybd ( unsigned char, int, int )
{
  exit ( 0 ) ;
}

void reshape(int wid, int ht)
{
  glViewport(0, 0, wid, ht);
}
void showGLerror ()
{
  GLenum err ;
  while ( (err = glGetError()) != GL_NO_ERROR )
    fprintf ( stderr, "OpenGL Error: %sn", gluErrorString ( err ) )  ;
}
void display ( void )
{
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glViewport(0, 0, 500, 500);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    // Draw a full screen quad.
    GLfloat s = 1.0;
    glBegin(GL_QUADS);
        glVertex3f(0.0f, 0.0f, 0.0f);
        glVertex3f(s,    0.0f, 0.0f);
        glVertex3f(s,    s,    0.0f);
        glVertex3f(0.0f, s,    0.0f);
    glEnd();
    glPopAttrib();
    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
  glutSwapBuffers   () ;
  glutPostRedisplay () ;
}
void showShaderInfo ( const char *what, GLuint handle )
{
  int len = 0 ;
  glGetObjectParameterivARB ( handle, GL_OBJECT_INFO_LOG_LENGTH_ARB, &len ) ;
  if ( len > 0 )
  {
    int trueLen ;
    char *s = new char [ len ] ;
    glGetInfoLogARB ( handle, len, &trueLen, s ) ;
    if ( trueLen > 0 && s [ 0 ] != '' )
      fprintf ( stderr, "%s:n%sn", what, s ) ;
    delete [] s ;
  }
}
GLuint compileShader ( const char *src, GLenum type )
{
  const char *type_str = type == GL_VERTEX_SHADER ? "vertex" : "fragment";
  GLuint handle = glCreateShader( type ) ;
  glShaderSource ( handle, 1, &src, 0 ) ;
  glCompileShader( handle ) ;
  GLint compiled ;
  glGetShaderiv( handle, GL_COMPILE_STATUS, &compiled ) ;
  if ( !compiled )
  {
    showShaderInfo ( type_str, handle ) ;
    fprintf ( stderr, "Failed to compile %s shader.n", type_str );
    exit ( 1 ) ;
  }
  return handle ;
}
GLuint linkShaders ( GLuint vsHandle, GLuint fsHandle )
{
  GLint  linked ;
  GLuint handle = glCreateProgram() ;
  glAttachShader            ( handle, vsHandle ) ;
  glAttachShader            ( handle, fsHandle ) ;
  glLinkProgram             ( handle ) ;
  glGetProgramiv            ( handle, GL_LINK_STATUS, & linked ) ;
  if ( !linked )
  {
    showShaderInfo ( "Linking", handle ) ;
    fprintf ( stderr, "Failed to link shader program.n" ) ;
    exit ( 1 ) ;
  }
  return handle ;
}
void init()
{
  int arraySize = 500 * 500;
  int *array_x = new int[arraySize];
  int *array_y = new int[arraySize];
  // Populate arrays.
  for (int y = 0; y < 500; y++) {
      for (int x = 0; x < 500; x++) {
          array_x[(y * 500) + x] = x;
          array_y[(y * 500) + x] = y;
      }
  }
  const size_t  size = sizeof( int ) * arraySize;
  //// array_x
  //
  // Generate and fill buffer object
  GLuint buffer;
  glGenBuffers   ( 1, &buffer );
  glBindBuffer   ( GL_TEXTURE_BUFFER, buffer );
  glBufferData   ( GL_TEXTURE_BUFFER, size, array_x, GL_STATIC_DRAW );  // Alloc & Fill
  // Generate texture "wrapper" around buffer object
  GLuint tex;
  glGenTextures  ( 1, &tex );
  glActiveTexture( GL_TEXTURE0);
  glBindTexture  ( GL_TEXTURE_BUFFER, tex );
  glTexBuffer    ( GL_TEXTURE_BUFFER, GL_R32I, buffer );
  //// array_y
  //
  // Generate and fill buffer object
  GLuint buffer2;
  glGenBuffers   ( 1, &buffer2 );
  glBindBuffer   ( GL_TEXTURE_BUFFER, buffer2 );
  glBufferData   ( GL_TEXTURE_BUFFER, size, array_y, GL_STATIC_DRAW );  // Alloc & Fill
  // Generate texture "wrapper" around buffer object
  GLuint tex2;
  glGenTextures  ( 1, &tex2 );
  glActiveTexture( GL_TEXTURE0 + 1);
  glBindTexture  ( GL_TEXTURE_BUFFER, tex2 );
  glTexBuffer    ( GL_TEXTURE_BUFFER, GL_R32I, buffer2 );
}
int main ( int argc, char **argv )
{
  // Init GL context
  glutInit            ( &argc, argv ) ;
  glutInitDisplayMode ( GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE ) ;
  glutInitWindowSize  ( 500, 500 ) ;
  glutCreateWindow    ( "Shader Test" ) ;
  glutDisplayFunc     ( display  ) ;
  glutKeyboardFunc    ( keybd    ) ;
  glutReshapeFunc     ( reshape  ) ;
  // Create buffer object and its texture buffer object wrapper
  init();
  // Load and compile shaders
  printf( "Compiling vertex shader...n" );
  GLuint vsHandle = compileShader ( Vertex_src, GL_VERTEX_SHADER  );
  printf( "Compiling fragment shader...n" );
  GLuint fsHandle = compileShader ( (readFile("mytest.glsl")).c_str(), GL_FRAGMENT_SHADER);
  // Link shaders
  printf( "Linking...n" );
  GLuint handle   = linkShaders ( vsHandle, fsHandle ) ;
  // Activate shader
  glUseProgram( handle ) ;
  // Populate uniform
  //   (buffer texture will be on TEXUNIT 0)
  glUniform1i( glGetUniformLocation( handle, "tex" ), 0 );
  //   (buffer2 texture will be on TEXUNIT 1)
  glUniform1i( glGetUniformLocation( handle, "tex2" ), 1 );
  // Draw with shader
  glutMainLoop () ;
  return 0 ;
}

片段着色器:mytest.glsl

#version 130                           
#extension GL_EXT_gpu_shader4: enable  
uniform isamplerBuffer tex;
uniform isamplerBuffer tex2;
void main(void)
{
  int width = 500;
  // Get the current screen index we are going to work on.
  // (Used for TEST B and TEST C below.)
  int index = int((gl_FragCoord.y * float(width)) + gl_FragCoord.x);
  // TEST A: This works as expected.
  /*if (gl_FragCoord.x < 250.0 && gl_FragCoord.y < 250.0) {
      gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
  }
  else {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }*/
  // TEST B: Use buffer textures to get the value of the arrays at
  // the index computed above.  This does not work as expected.
  int x_i = int(texelFetch(tex, index).r);
  int y_i = int(texelFetch(tex2, index).r);
  if (x_i < 250 && y_i < 250) {
      gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
  }
  else {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }
  // TEST C: Get the x, y screen space coordinates based on the index
  // computed above.  This does not work as expected.
  /*int x_i = index % 500;
  int y_i = index / 500;
  if (x_i < 250 && y_i < 250) {
      gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);
  }
  else {
      gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
  }*/
}

测试A的结果测试Ahttp://www.shilpigupta.com/stack/TestA.png

测试B和测试C的结果(还要注意图像右上角的绿线伪影。)测试B和测试Chttp://www.shilpigupta.com/stack/TestB.png

错误在于如何计算索引:

int index = int((gl_FragCoord.y * float(width)) + gl_FragCoord.x);

只有当gl_FragCoord的小数部分为零时,此代码才会工作。请注意,OpenGL的窗口空间是以这样一种方式定义的,即像素中心位于半整数位置("逗号5处")。在没有多重采样或其他奇特设置的情况下,片段着色器将完全为像素中心调用,因此所有gl_FragCoord值都为.5,这基本上会导致您观察到的0.5*宽度像素的偏移。(在B和C的情况下,绿色区域实际上不是正方形,因为顶部缺少一条高度线,因为底部线的起点x_i=250)。

正确的方法是

int index = int(gl_FragCoord.y) * width + int(gl_FragCoord.x);

最新更新