1
\$\begingroup\$

I'm sorry for asking such a common question, yet all of the other answers have not been very applicable to my problem. In short, I have begun learning OpenGL, and have started trying to draw the mandelbrot set as a small exercise. All of the pixel data is stored in a vector (I checked with GDB), yet for some reason when I transfer it into a texture and then draw it it does not work. To draw the texture I created a class called DrawBuffer, which handles all the OpenGL.

Any insight onto why this may be happening would be appreciated.

Fragment Shader

#version 150

in vec2 Tex_coord;

out vec4 outColor;

uniform sampler2D tex;

void main() {
    outColor = texture(tex, Tex_coord);
}

Vertex Shader

#version 150

in vec2 position;
in vec2 tex_coord;

out vec2 Tex_coord;

void main() {
    gl_Position = vec4(position, 0.0, 1.0);
    Tex_coord = tex_coord;
}

DrawBuffer.cpp

#include "Draw_Buffer.h"
#include <memory>
#include <algorithm>
#include <stdexcept>
#include <vector>
#include <sstream>
#include <fstream>
#include <string>

// Util function to compile a shader from source
void compile_shader(GLuint &shader, const std::string &src) {
    std::ifstream is(src);
    std::string code;

    std::string temp_str;
    while (std::getline(is, temp_str)) {
        code += temp_str + '\n';
    }

    const char *c_code = code.c_str();
    glShaderSource(shader, 1, &c_code, NULL);
    glCompileShader(shader);
}

Draw_Buffer::Draw_Buffer(Window<int> *win, const std::string &vertex_shader_src, const std::string &frag_shader_src) :
        window(win), buffer(std::vector<RGB>(window->size())), pos_iter(buffer.begin()) {
// Initialise GLFW
    if (!glfwInit()) {
        throw std::runtime_error("error: GLFW unable to initialise");
    }

// Set up the window
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

    glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);

    screen.reset(glfwCreateWindow(win->width(), win->height(), "Mandelbrot Fractal", nullptr, nullptr));

    make_current();

// Initialise glew
    glewExperimental = GL_TRUE;
    GLenum glewinit = glewInit();

    if (glewinit != GLEW_OK) {
        std::ostringstream ss;
        ss << "error: Glew unable to initialise" << glewinit;
        throw std::runtime_error(ss.str());
    }

// Clear
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);

// Generate shaders
    GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
    GLuint frag_shader = glCreateShader(GL_VERTEX_SHADER);

    GLint compile_status;
    compile_shader(vertex_shader, vertex_shader_src);
    glGetShaderiv(vertex_shader, GL_COMPILE_STATUS, &compile_status);
    if (compile_status != GL_TRUE) {
        char buffer[512];
        glGetShaderInfoLog(vertex_shader, 512, NULL, buffer);
        throw std::runtime_error(buffer);
    }

    compile_shader(frag_shader, frag_shader_src);
    glGetShaderiv(frag_shader, GL_COMPILE_STATUS, &compile_status);
    if (compile_status != GL_TRUE) {
        char buffer[512];
        glGetShaderInfoLog(frag_shader, 512, NULL, buffer);
        throw std::runtime_error(buffer);
    }

// Put shaders into shader program
    shader_prog = glCreateProgram();
    glAttachShader(shader_prog, vertex_shader);
    glAttachShader(shader_prog, frag_shader);
    glBindFragDataLocation(shader_prog, 0, "outColor");
    glLinkProgram(shader_prog);
    glUseProgram(shader_prog);

// Set shader attributes
    GLint pos_attrib = glGetAttribLocation(shader_prog, "position");
    glVertexAttribPointer(pos_attrib, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), 0);
    glEnableVertexAttribArray(pos_attrib);

    GLint tex_coord_attrib = glGetAttribLocation(shader_prog, "tex_coord");
    glEnableVertexAttribArray(tex_coord_attrib);
    glVertexAttribPointer(tex_coord_attrib, 2, GL_FLOAT, GL_FALSE,
                        4 * sizeof(GLfloat), (void*)(2 * sizeof(GLfloat)));

// Generate texture
    glGenTextures(1, &mandelbrot_tex);
}

Draw_Buffer::~Draw_Buffer() {


// Terminate GLFW
    glfwTerminate();
}

void Draw_Buffer::flush() {

// Render pixels to image
    glBindTexture(GL_TEXTURE_2D, mandelbrot_tex);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, window->width(), window->height(), 0, GL_RGB, GL_FLOAT, &buffer[0]);

    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_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

// Draw a rectangle
    const static GLfloat vertices[] = {
            // Position   Tex-coords
            -1.0f,  1.0f, 0.0f, 0.0f, // Top-left
             1.0f,  1.0f, 1.0f, 0.0f, // Top-right
             1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
            -1.0f, -1.0f, 0.0f, 1.0f  // Bottom-left
    };

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

    const static GLuint elements[] = {
            0, 1, 2,
            2, 3, 0
    };

    GLuint ebo;
    glGenBuffers(1, &ebo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STREAM_DRAW);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    glfwSwapBuffers(screen.get());
}

void Draw_Buffer::keep_window_open() {
    while(!glfwWindowShouldClose(screen.get())) {
        glfwPollEvents();
    }
}

Draw_Buffer &operator<<(Draw_Buffer &db, const RGB &pixel) {
    if (db.pos_iter != db.buffer.end()) {
        *(db.pos_iter) = pixel;
        ++db.pos_iter;
        return db;
    }
    else {
        throw std::runtime_error("error: Cannot append past end of buffer : Remember to flush the buffer");
        return db;
    }
}

DrawBuffer.h

#include "Window.h"
#include "Cleanup.h"

struct RGB {
    float r;
    float g;
    float b;
};

class Draw_Buffer {
friend Draw_Buffer &operator<<(Draw_Buffer &db, const RGB &pixel);
    std::unique_ptr<Window<int>> window;

    std::unique_ptr<GLFWwindow, Cleaner> screen;

    std::vector<RGB> buffer;

    // Tracks position of appending in buffer
    std::vector<RGB>::iterator pos_iter;

    // Texture where pixels are written to
    GLuint mandelbrot_tex;

    // GLSL Shader program
    GLuint shader_prog;
public:
    Draw_Buffer(Window<int> *, const std::string &, const std::string &);
    ~Draw_Buffer();

    void make_current() {
        glfwMakeContextCurrent(screen.get());
    }

    void flush();

    void keep_window_open();
};

Draw_Buffer &operator<<(Draw_Buffer &db, const RGB &pixel);


#endif //MANDELBROT_FRACTAL_DRAWER_DRAW_BUFFER_H
\$\endgroup\$
  • \$\begingroup\$ I don't see where you're setting the uniform that specifies the texture unit you're going to use. I'd think you'd want to call glUniform1i() at some point, no? \$\endgroup\$ – user1118321 Mar 26 '17 at 5:02
1
\$\begingroup\$

First of all, there's a typo. You're making 2 vertex shaders.

GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
GLuint frag_shader = glCreateShader(GL_VERTEX_SHADER); // Should be GL_FRAGMENT_SHADER

Also from the code you have supplied, it seems like you're not creating a vertex array. So prior to glGenBuffers(1, &vbo) add:

GLuint vao;
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);

Note that the above vertex array must be bound before calling glEnableVertexAttribArray() and glVertexAttribPointer().

Also given that glDrawElements() and glfwSwapBuffers() is in Draw_Buffer::flush(). I'm assuming this method is called to draw the rectangle. The extremely bad thing about this is that you're calling glGenBuffers(), etc. each frame. But you're never calling glDeleteBuffers(). The best thing would be to move all that into Draw_Buffer::Draw_Buffer().

Then inside Draw_Buffer::flush() simply have:

glBindVertexArray(vao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

glfwSwapBuffers(screen.get());

Also your driver is most likely clever enough to catch this. But as mentioned in the comments, you need to do this as well:

glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glUniform1i(glGetUniformLocation(shader_prog, "tex"), 0);

Last but not least, since your geometry is static you can replace GL_STREAM_DRAW with GL_STATIC_DRAW.

\$\endgroup\$
  • \$\begingroup\$ Thank you for the extensive answer :) I really appreciate it when people put time into their answers and I hope, when I become more experienced I can eventually do the same. \$\endgroup\$ – James Balajan Mar 26 '17 at 6:21
  • \$\begingroup\$ You're welcome! This is why I mainly enjoy answering OpenGL questions (on Stack Overflow). Because there you usually have time to think, instead of having to answer within 10 seconds if it was a regular Java or Python question. Besides that, just keep on learning, it will get easier, and one day you'll be able to help others as well! :) \$\endgroup\$ – Vallentin Mar 26 '17 at 6:25
  • \$\begingroup\$ Sorry for bothering you yet for some reason I am still not getting anything drawing. Even when I try to test that the texture is even working by changing the outColor from the frag shader to vec4(1.0, 1.0, 1.0, 1.0) I am still getting a black screen :( \$\endgroup\$ – James Balajan Mar 26 '17 at 6:31
  • \$\begingroup\$ You probably accidentally missed something. I did indeed test it, so here is the dummy code I used, I extracted pieces from your code. Note that I'm using dummy texture data, and I'm also using GLAD instead of GLEW so simply switch that out if you want to keep GLEW. If you still can't resolve the problem, then I need to see your new code, and what you changed! :) \$\endgroup\$ – Vallentin Mar 26 '17 at 6:57
  • 1
    \$\begingroup\$ Just keep asking, that's what Stack Overflow and Gamedev is for! :) I took a quick glance at your code, the problem is that you call glEnableVertexAttribArray and glVertexAttribPointer before creating the vbo and ebo. If you take a look at the code I linked before, I set it up to create the vao, vbo and ebo and then it calls glEnableVertexAttribArray, etc. \$\endgroup\$ – Vallentin Mar 26 '17 at 7:48

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.