2017-10-06 12 views
2

Ich habe mich bemüht, die Ausgabe von glReadPixels zu verstehen, die in der Theorie offensichtlich einfach zu sein scheint, aber tatsächlich verwirrende (zumindest für mich) Ergebnisse hervorbringt.glReadPixels gibt immer den gleichen Wert in glClearColor zurück

Angenommen, ich habe einen einfachen Fragment-Shader, der ein einzelnes Dreieck mit einem Farbwert von vec4 (0,2, 0, 0, 0) zeichnet, während die Hintergrundfarbe auf (0,3, 1,0, 1,0, 0,0) eingestellt ist. etwa so:

enter image description here

Unten ist der vollständige Code (mit Ausnahme von Shader-Konstruktion), die ich oben das Bild erzeugen verwenden:

#include "shader.h" // shader compile/link/use 
#include <GLFW\glfw3.h> 
#include <iostream> 

const int DISPLAY_WIDTH = 16; 
const int DISPLAY_HEIGHT = 16; 

//============= shader code ========================== 

const GLchar *vertexShaderSource = R"glsl(#version 440 
in vec2 position; 
void main() 
{ 
    gl_Position = vec4(position, 0.0, 1.0); 
})glsl"; 

const GLchar *fragmentShaderSource = R"glsl(#version 440 
out vec4 outColor; 

void main() 
{ 
    outColor = vec4(0.2,0.,0.,0.); 
})glsl"; 

//============= c++ entry point ========================== 

int main(int argc, char** argv) { 

    glfwInit(); 
    GLFWwindow* window = glfwCreateWindow(DISPLAY_WIDTH, DISPLAY_HEIGHT, "test", NULL, NULL); 
    glfwMakeContextCurrent(window); 

    GLenum res = glewInit(); 

    // triangle data (xy-position) 
    float vertices[] = { 
    0.0f, 0.5f, 
    0.5f, -0.5f, 
    -0.5f, -0.5f 
    }; 

    GLuint vbo; 
    glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

    // enable vertex xy-position attribute 
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 
    glEnableVertexAttribArray(0); 

    // compile, link and use shader program 
    Shader shader(vertexShaderSource, fragmentShaderSource); 
    shader.Use(); 

    // rendering loop 
    while (!glfwWindowShouldClose(window)) { 

     glClearColor(0.3f, 1.0f, 1.0f, 0.0f); 
     glClear(GL_COLOR_BUFFER_BIT); 

     glDrawArrays(GL_TRIANGLES, 0, 3); 
     glFlush(); 

     // read pixels from backbuffer 
     GLubyte data[DISPLAY_WIDTH * DISPLAY_HEIGHT]; 
     glReadPixels(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, data); 

     for (int i = 0; i < DISPLAY_WIDTH * DISPLAY_HEIGHT; i++) { 
      int a = data[i]; // implicit conversion of unsigned char to int 
      std::cout << a << std::endl; 
     } 
     std::getchar(); // wait for user input 

     glfwSwapBuffers(window); 
     glfwPollEvents(); 
    } 

    glfwTerminate(); 
    return 0; 
} 

Beachten Sie, dass ich einen Standard-Framebuffer bin mit , die meine Farbwerte als normalisierte s behandelt Ganzzahlige Ganzzahlen und konvertiert sie in den Bereich zwischen [0-255], dh meine Hintergrundfarbe wird (76, 255, 255, 0) sein, während meine Geometriefarbe (51, 0, 0, 0 sein wird).

Und so, nachdem ich meine Geometrie zeichnen und die Puffer tauschen, bekomme ich mein Bild. Jetzt möchte ich die Farbwerte auslesen. Um es zu tun, füge ich die notwendige glReadPixels bezogenen Code, kurz bevor ich Puffer tauschen:

GLubyte* data = new GLubyte[DISPLAY_WIDTH * DISPLAY_HEIGHT]; 
glReadPixels(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, data); 

Um den Prozess der Prüfung der Pixelwerte zu erleichtern, die ich aus dem Framebuffer ausgelesen, ich Rotkanals nur extrahieren, daher ist die Größe der Daten, die erforderlich sind, um Pixeldaten aufzunehmen, DISPLAY_WIDTH * DISPLAY_HEIGHT. Außerdem bedeutet dies, dass die Werte, die ich ausdrucke, für die Hintergrundfarbe '76' und für die Geometrie '51' sein sollten.

Überraschenderweise ist jeder einzelne Rotkanal-Pixeldaten (alle DISPLAY_WIDTH * DISPLAY_HEIGHT Pixel gedruckt), die ich ausdruck, zufällig '76', als ob die Geometrie ignoriert würde. Beachten Sie, dass ich Pixel nach dem Zeichenaufruf lese und bevor ich Puffer austausche.

Ich würde mich sehr freuen, wenn Sie mich wissen lassen könnten, was ich hier vermisse.

+3

Wenn Sie Farbe für den Hintergrund, aber nicht für das Objekt erhalten, scheint 'glReadPixels' zwischen den Aufrufen" Clear Color "und" Swap Buffer "zu lesen. Oder es liest aus dem 'Front Buffer' statt dem Standard' Back Buffer' nach einem ersten Tausch, den Sie irgendwie vermissen. – Ripi2

+1

Was Sie tun, sieht richtig aus. Sind Sie _sure_ Sie sehen nur Hintergrundpixel? Von Ihrem Bild wird es eine Menge Hintergrundpixel vor der ersten Geometrie geben ... – Bahbar

+0

Bearbeiten in [mcve]. Fühlen Sie sich frei, [this] (https://stackoverflow.com/a/8841923/44729) als Basis zu verwenden. – genpfault

Antwort

1

Also hier ist die ganze Geschichte. Es ist erwähnenswert, dass, wie Bahbar sagte, ich Pixel richtig gelesen wurde. Allerdings hatte ich eine falsche Annahme bezüglich der Größe meines Standard-Framebuffers, dessen Dimensionen auf die Dimensionen eines von mir erstellten Fensters eingestellt sind.

Die Fenstergröße, die ich im Code mit glfwCreateWindow angeben, ist 16x16. Offensichtlich hat das Bild, das ich in der ursprünglichen Frage anbrachte, eine größere Breite.

Die GLFW documentation (siehe glfwSetWindowSize) lautet wie folgt: „Der Fenstermanager auf setzen Grenzen können, welche Größen erlaubt GLFW kann und diese Grenzen nicht außer Kraft setzen sollte“. Nun, es stellt sich heraus, dass ich auf meinem Windows 10-Rechner keine Breite von weniger als 120 Pixel einstellen kann. Anschließend ergab eine Untersuchung von data Arrays mit 120x16 Pixeln anstelle von 16x16 Pixeln, dass der Wert von "51" tatsächlich korrekt aufgezeichnet wurde.

Um sicherzustellen, dass die Geometrie genau dort gezeichnet wird, wo sie gezeichnet werden soll, muss ich vor meinen Zeichenaufrufen gViewport aufrufen, um die gewünschte Transformation von normalisierten Gerätekoordinaten (NDC) zu Pixeln in meinen zu spezifizieren Fenster (oder Pixel in meinem Render-Puffer oder eine Textur, wenn ich mit einem benutzerdefinierten Framebuffer-Objekt außerhalb des Bildschirms rendere). Unterhalb der Ausgang ist, und ein etwas von genpfault vorgesehen modifizierten Code (seine Code ist vollständig in sich abgeschlossen und enthält Bits Shader-Konstruktion, die meinem Beispiel fehlt):

GLEW version: 2.0.0 
GLFW version: 3.2.1 Win32 WGL EGL VisualC 
GL_VERSION : 4.5.0 NVIDIA 376.53 
GL_VENDOR : NVIDIA Corporation 
GL_RENDERER : GeForce GTX 970/PCIe/SSE2 

76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 51 51 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 51 51 76 76 76 76 76 76 76 
76 76 76 76 76 76 51 51 51 51 76 76 76 76 76 76 
76 76 76 76 76 76 51 51 51 51 76 76 76 76 76 76 
76 76 76 76 76 51 51 51 51 51 51 76 76 76 76 76 
76 76 76 76 76 51 51 51 51 51 51 76 76 76 76 76 
76 76 76 76 51 51 51 51 51 51 51 51 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 

Der Code selbst enthält einen glViewport Anruf (rechts vor Schleife Rendering) und eine leicht modifizierte Druckschleife, um die Ausgabe auf das aktuelle Bild zu entsprechen:

#include <GL/glew.h> 
#include <GLFW/glfw3.h> 
#include <iostream> 
#include <cstdarg> 

struct Program 
{ 
    static GLuint Load(const char* shader, ...) 
    { 
     GLuint prog = glCreateProgram(); 
     va_list args; 
     va_start(args, shader); 
     while (shader) 
     { 
      const GLenum type = va_arg(args, GLenum); 
      AttachShader(prog, type, shader); 
      shader = va_arg(args, const char*); 
     } 
     va_end(args); 
     glLinkProgram(prog); 
     CheckStatus(prog); 
     return prog; 
    } 

private: 
    static void CheckStatus(GLuint obj) 
    { 
     GLint status = GL_FALSE; 
     if (glIsShader(obj)) glGetShaderiv(obj, GL_COMPILE_STATUS, &status); 
     if (glIsProgram(obj)) glGetProgramiv(obj, GL_LINK_STATUS, &status); 
     if (status == GL_TRUE) return; 
     GLchar log[1 << 15] = { 0 }; 
     if (glIsShader(obj)) glGetShaderInfoLog(obj, sizeof(log), NULL, log); 
     if (glIsProgram(obj)) glGetProgramInfoLog(obj, sizeof(log), NULL, log); 
     std::cerr << log << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    static void AttachShader(GLuint program, GLenum type, const char* src) 
    { 
     GLuint shader = glCreateShader(type); 
     glShaderSource(shader, 1, &src, NULL); 
     glCompileShader(shader); 
     CheckStatus(shader); 
     glAttachShader(program, shader); 
     glDeleteShader(shader); 
    } 
}; 

const GLchar *vertexShaderSource = R"glsl(#version 130 
    in vec2 position; 
    void main() 
    { 
     gl_Position = vec4(position, 0.0, 1.0); 
    })glsl"; 

const GLchar *fragmentShaderSource = R"glsl(#version 130 
    out vec4 outColor; 

    void main() 
    { 
     outColor = vec4(0.2,0.,0.,0.); 
    })glsl"; 


const int DISPLAY_WIDTH = 16; 
const int DISPLAY_HEIGHT = 16; 


int main(int argc, char** argv) 
{ 
    glfwInit(); 
    GLFWwindow* window = glfwCreateWindow(DISPLAY_WIDTH, DISPLAY_HEIGHT, "test", NULL, NULL); 
    glfwMakeContextCurrent(window); 

    GLenum res = glewInit(); 

    std::cout << "GLEW version: " << glewGetString(GLEW_VERSION) << std::endl; 
    std::cout << "GLFW version: " << glfwGetVersionString() << std::endl; 
    std::cout << "GL_VERSION : " << glGetString(GL_VERSION) << std::endl; 
    std::cout << "GL_VENDOR : " << glGetString(GL_VENDOR) << std::endl; 
    std::cout << "GL_RENDERER : " << glGetString(GL_RENDERER) << std::endl << std::endl; 

    // triangle data (xy-position) 
    float vertices[] = 
    { 
     0.0f, 0.5f, 
     0.5f, -0.5f, 
     -0.5f, -0.5f 
    }; 

    GLuint vbo; 
    glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

    // enable vertex xy-position attribute 
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 
    glEnableVertexAttribArray(0); 

    // compile, link and use shader program 
    GLuint program = Program::Load 
     (
     vertexShaderSource, GL_VERTEX_SHADER, 
     fragmentShaderSource, GL_FRAGMENT_SHADER, 
     NULL 
     ); 
    glUseProgram(program); 

    glViewport(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT); 
    // rendering loop 
    while (!glfwWindowShouldClose(window)) { 

     glClearColor(0.3f, 1.0f, 1.0f, 0.0f); 
     glClear(GL_COLOR_BUFFER_BIT); 

     glDrawArrays(GL_TRIANGLES, 0, 3); 
     glFlush(); 

     // read pixels from backbuffer 
     GLubyte data[DISPLAY_WIDTH * DISPLAY_HEIGHT]; 
     glReadPixels(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, data); 

     for (int y = DISPLAY_HEIGHT-1; y >= 0; y--) 
     { 
      for (int x = 0; x < DISPLAY_WIDTH; x++) 
      { 
       std::cout << (int)data[y*DISPLAY_HEIGHT + x] << " "; // implicit conversion of unsigned char to int 
      } 
      std::cout << std::endl; 
     } 
     std::getchar(); 
     std::cout << std::endl; 

     glfwSwapBuffers(window); 
     glfwPollEvents(); 
    } 

    glfwTerminate(); 
    return 0; 
} 

Schließlich konnte ich ein Bild mit Stackoverflow Funktionalität nicht laden, hier so ist ein Link für diejenigen Imgur die interessiert sind, : https://imgur.com/6bfLhfW.

Wenn mir jemand sagen könnte, ob das als Antwort akzeptiert werden kann, werde ich einfach weitermachen und den Knopf drücken. Oder vielleicht könnte jemand anderes eine sinnvollere Antwort geben. Danke für die Eingabe Jungs!

2

Workin' auf meinem Debian Stretch-Box:

GLEW version: 2.0.0 
GLFW version: 3.2.1 X11 GLX EGL clock_gettime /dev/js Xf86vm shared 
GL_VERSION : 3.0 Mesa 13.0.6 
GL_VENDOR : Intel Open Source Technology Center 
GL_RENDERER : Mesa DRI Intel(R) Kabylake GT2 

76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 51 51 51 51 51 51 51 51 76 76 76 76 
76 76 76 76 76 51 51 51 51 51 51 76 76 76 76 76 
76 76 76 76 76 51 51 51 51 51 51 76 76 76 76 76 
76 76 76 76 76 76 51 51 51 51 76 76 76 76 76 76 
76 76 76 76 76 76 51 51 51 51 76 76 76 76 76 76 
76 76 76 76 76 76 76 51 51 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 51 51 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 
76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 76 

Alle zusammen (ließ den Shader #version-130 da Mesa nichts Vergangenheit GL 3.0 in Nicht-Kern Kontexten unterstützen):

// g++ main.cpp -lGLEW -lGL -lglfw 
#include <GL/glew.h> 
#include <GLFW/glfw3.h> 
#include <iostream> 
#include <cstdarg> 

struct Program 
{ 
    static GLuint Load(const char* shader, ...) 
    { 
     GLuint prog = glCreateProgram(); 
     va_list args; 
     va_start(args, shader); 
     while(shader) 
     { 
      const GLenum type = va_arg(args, GLenum); 
      AttachShader(prog, type, shader); 
      shader = va_arg(args, const char*); 
     } 
     va_end(args); 
     glLinkProgram(prog); 
     CheckStatus(prog); 
     return prog; 
    } 

private: 
    static void CheckStatus(GLuint obj) 
    { 
     GLint status = GL_FALSE; 
     if(glIsShader(obj)) glGetShaderiv(obj, GL_COMPILE_STATUS, &status); 
     if(glIsProgram(obj)) glGetProgramiv(obj, GL_LINK_STATUS, &status); 
     if(status == GL_TRUE) return; 
     GLchar log[ 1 << 15 ] = { 0 }; 
     if(glIsShader(obj)) glGetShaderInfoLog(obj, sizeof(log), NULL, log); 
     if(glIsProgram(obj)) glGetProgramInfoLog(obj, sizeof(log), NULL, log); 
     std::cerr << log << std::endl; 
     exit(EXIT_FAILURE); 
    } 

    static void AttachShader(GLuint program, GLenum type, const char* src) 
    { 
     GLuint shader = glCreateShader(type); 
     glShaderSource(shader, 1, &src, NULL); 
     glCompileShader(shader); 
     CheckStatus(shader); 
     glAttachShader(program, shader); 
     glDeleteShader(shader); 
    } 
}; 

const GLchar *vertexShaderSource = R"glsl(#version 130 
in vec2 position; 
void main() 
{ 
    gl_Position = vec4(position, 0.0, 1.0); 
})glsl"; 

const GLchar *fragmentShaderSource = R"glsl(#version 130 
out vec4 outColor; 

void main() 
{ 
    outColor = vec4(0.2,0.,0.,0.); 
})glsl"; 


const int DISPLAY_WIDTH = 16; 
const int DISPLAY_HEIGHT = 16; 


int main(int argc, char** argv) 
{ 
    glfwInit(); 
    GLFWwindow* window = glfwCreateWindow(DISPLAY_WIDTH, DISPLAY_HEIGHT, "test", NULL, NULL); 
    glfwMakeContextCurrent(window); 

    GLenum res = glewInit(); 

    std::cout << "GLEW version: " << glewGetString(GLEW_VERSION) << std::endl; 
    std::cout << "GLFW version: " << glfwGetVersionString() << std::endl; 
    std::cout << "GL_VERSION : " << glGetString(GL_VERSION) << std::endl; 
    std::cout << "GL_VENDOR : " << glGetString(GL_VENDOR) << std::endl; 
    std::cout << "GL_RENDERER : " << glGetString(GL_RENDERER) << std::endl; 

    // triangle data (xy-position) 
    float vertices[] = 
    { 
     0.0f, 0.5f, 
     0.5f, -0.5f, 
     -0.5f, -0.5f 
    }; 

    GLuint vbo; 
    glGenBuffers(1, &vbo); 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

    // enable vertex xy-position attribute 
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0); 
    glEnableVertexAttribArray(0); 

    // compile, link and use shader program 
    GLuint program = Program::Load 
     (
     vertexShaderSource, GL_VERTEX_SHADER, 
     fragmentShaderSource, GL_FRAGMENT_SHADER, 
     NULL 
     ); 
    glUseProgram(program); 

    // rendering loop 
    while (!glfwWindowShouldClose(window)) { 

    glClearColor(0.3f, 1.0f, 1.0f, 0.0f); 
    glClear(GL_COLOR_BUFFER_BIT); 

    glDrawArrays(GL_TRIANGLES, 0, 3); 
    glFlush(); 

    // read pixels from backbuffer 
    GLubyte data[DISPLAY_WIDTH * DISPLAY_HEIGHT]; 
    glReadPixels(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, GL_RED, GL_UNSIGNED_BYTE, data); 

    int i = 0; 
    for (int y = 0; y < DISPLAY_HEIGHT; y++) 
    { 
     for (int x = 0; x < DISPLAY_WIDTH; x++) 
     { 
      int a = data[i]; // implicit conversion of unsigned char to int 
      std::cout << a << " ";; 
      i++; 
     } 
     std::cout << std::endl; 
    } 
    std::cout << std::endl; 

    glfwSwapBuffers(window); 
    glfwPollEvents(); 
    } 

    glfwTerminate(); 
    return 0; 
} 
+1

Ich habe endlich herausgefunden, was passiert. Die Fenstergröße, die ich mit 'glfwCreateWindow' angegeben habe, ist 16x16. Offensichtlich hat das Bild, das ich anbrachte, eine größere Breite. In der [GLFW-Dokumentation] (http://www.glfw.org/docs/latest/group__window.html#ga371911f12c74c504dd8d47d832d095cb) (siehe glfwSetWindowSize) steht Folgendes: "Der Fenstermanager darf Grenzen dafür festlegen, welche Größen zulässig sind. GLFW kann nicht und sollte diese Grenzen nicht überschreiben ". Nun, es stellt sich heraus, dass ich keine Breite von weniger als 120 Pixel einstellen kann. Nachdem dies gesagt wurde, ergab die Untersuchung von "Daten" -Array, dass der Wert von "51" tatsächlich korrekt aufgezeichnet wurde. – MutomboDikey

Verwandte Themen