使用 1D 纹理缓冲区时 GLSL 的意外结果
Unexpected results with GLSL when using 1D texture buffers
在下面的测试程序中,我试图在屏幕 (500 x 500) 的左下角渲染一个绿色方块 (250 x 250)。
我正在使用两个缓冲区纹理将两个 int 数组(每个数组的大小为 500 * 500)传递给片段着色器。这些数组只是简单地填充了 x(第一个数组)和 y(第二个数组)值,这些值对应于屏幕 space 中的所有 (x, y) 坐标(这在下面代码的 init() 部分中显示) ).
在片段着色器中,如果当前片段位置(在屏幕 space 中)在 x 和 y 方向上都小于 250.0,则该片段为绿色。否则,它是红色的。 图片如下所示。
下面是完整的 OpenGL 代码,以及直通顶点着色器(位于主程序中)和片段着色器(从文件中读取)。在片段着色器中,有三个测试(A、B 和 C)。 测试 B 和 C 未按预期工作。他们在右下角而不是左下角画了绿色方块。
测试 A(正确): 当我在有条件的如果检查。
测试 B(不正确): 如果我使用缓冲区纹理和 texelFetch 来检索当前片段的 x 和 y 值,然后在条件检查中使用它, 绿色方块绘制在右下角。
测试 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: %s\n", 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 ] != '[=11=]' )
fprintf ( stderr, "%s:\n%s\n", 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 的结果
Test A http://www.shilpigupta.com/stack/TestA.png
测试 B 和测试 C 的结果
(另请注意图像右上角的绿线伪像。)
Test B and Test C http://www.shilpigupta.com/stack/TestB.png
错误是你计算指数的方式:
int index = int((gl_FragCoord.y * float(width)) + gl_FragCoord.x);
此代码仅在 gl_FragCoord
的小数部分为零时有效。请注意,OpenGL 的 window space 的定义方式是像素中心位于半整数位置 ("at comma 5")。如果没有多重采样或其他花哨的设置,片段着色器将完全针对像素中心调用,因此您的所有 gl_FragCoord
值都为 .5,这基本上会导致您观察到的 0.5 * 宽度像素偏移。 (在 B 和 C 的情况下,绿色区域实际上不是正方形,因为顶部缺少一条高度线,因为你已经从 x_i=250 开始底线)。
正确的做法是
int index = int(gl_FragCoord.y) * width + int(gl_FragCoord.x);
在下面的测试程序中,我试图在屏幕 (500 x 500) 的左下角渲染一个绿色方块 (250 x 250)。
我正在使用两个缓冲区纹理将两个 int 数组(每个数组的大小为 500 * 500)传递给片段着色器。这些数组只是简单地填充了 x(第一个数组)和 y(第二个数组)值,这些值对应于屏幕 space 中的所有 (x, y) 坐标(这在下面代码的 init() 部分中显示) ).
在片段着色器中,如果当前片段位置(在屏幕 space 中)在 x 和 y 方向上都小于 250.0,则该片段为绿色。否则,它是红色的。 图片如下所示。
下面是完整的 OpenGL 代码,以及直通顶点着色器(位于主程序中)和片段着色器(从文件中读取)。在片段着色器中,有三个测试(A、B 和 C)。 测试 B 和 C 未按预期工作。他们在右下角而不是左下角画了绿色方块。
测试 A(正确): 当我在有条件的如果检查。
测试 B(不正确): 如果我使用缓冲区纹理和 texelFetch 来检索当前片段的 x 和 y 值,然后在条件检查中使用它, 绿色方块绘制在右下角。
测试 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: %s\n", 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 ] != '[=11=]' )
fprintf ( stderr, "%s:\n%s\n", 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 的结果 Test A http://www.shilpigupta.com/stack/TestA.png
测试 B 和测试 C 的结果 (另请注意图像右上角的绿线伪像。) Test B and Test C http://www.shilpigupta.com/stack/TestB.png
错误是你计算指数的方式:
int index = int((gl_FragCoord.y * float(width)) + gl_FragCoord.x);
此代码仅在 gl_FragCoord
的小数部分为零时有效。请注意,OpenGL 的 window space 的定义方式是像素中心位于半整数位置 ("at comma 5")。如果没有多重采样或其他花哨的设置,片段着色器将完全针对像素中心调用,因此您的所有 gl_FragCoord
值都为 .5,这基本上会导致您观察到的 0.5 * 宽度像素偏移。 (在 B 和 C 的情况下,绿色区域实际上不是正方形,因为顶部缺少一条高度线,因为你已经从 x_i=250 开始底线)。
正确的做法是
int index = int(gl_FragCoord.y) * width + int(gl_FragCoord.x);