2016-09-10 4 views
0

Ich nehme einen Computer Graphics Kurs an meiner Universität. Ich muss grundlegende Linienzeichnungsalgorithmen in modernen OpenGL (3.3 +) implementieren, um Primitive auf dem Bildschirm zu zeichnen. Hier ist die Funktion für Bresenhamschen Strichzeichnung Algorithmus, die ich umsetzen will - Zeichnen von Pixeln in OpenGL

void bresenham(GLint xStart, GLint yStart, GLint xEnd, GLint yEnd) { 
    if (!(xStart < xEnd)) { 
    swap(xStart, xEnd); 
    swap(yStart, yEnd); 
    } 

    GLint dx = xEnd - xStart; 
    GLint dy = yEnd - yStart; 
    GLint p = 2 * dy - dx; 

    GLint x = xStart; 
    GLint y = yStart; 
    setPixel(x,y); 

    while (x < xEnd) { 
     x += 1; 
     if (p < 0) 
      p += (2 * dy); 
     else { 
      p += (2 * (dy - dx)); 
      y += 1; 
     setPixel(x,y); 
     } 
    } 
} 

Ich bin ratlos, wie die setPixel() Funktion zu realisieren. Die meisten Antworten, die ich hier und anderswo verwenden ältere OpenGL-Funktionen -

void setPixel(int x, int y) 
{ 
    glColor3f(0.0, 0.0, 0.0); //Set pixel to black 
    glBegin(GL_POINTS); 
    glVertex2i(x, y); //Set pixel coordinates 
    glEnd(); 
    glFlush(); //Render pixel 
} 

Was ist das Äquivalent Weg, dies in OpenGl 3.3+ zu tun? Angenommen, ich kann die "Pixel" zu einem std::vector-Array hinzufügen, wie initialisiere ich das Vertex-Puffer-Array, um diese Daten zu speichern?

Ein weiteres Problem beim Versuch, einen Punkt mit GL_POINTS zu plotten ist, dass aufgrund von Clipping während der Konvertierung zu normalisierten Gerätekoordinaten, Punkte außerhalb des Bereichs [-1,1] in beide Richtungen nicht auf dem Fenster angezeigt werden.

Zum Beispiel erscheinen nur die ersten drei Punkte auf dem Fensterbildschirm. Siehe die initialize() Funktion -

#include <stdio.h> 
#include <stdlib.h> 

#include <GL/glew.h> 
#include <GLFW/glfw3.h> 
#include <cstdlib> 
#include <vector> 
#include <iostream> 
#include <fstream> 

// Read a shader source from a file 
// store the shader source in a std::vector<char> 
void read_shader_src(const char* fname, std::vector<char> &buffer); 

// Compile a shader 
GLuint load_and_compile_shader(const char *fname, GLenum shaderType); 

// Create a program from two shaders 
GLuint create_program(const char *path_vert_shader, const char *path_frag_shader); 

// Render scene 
void display(GLuint &vao, GLFWwindow* window); 

// Initialize the data to be rendered 
void initialize(GLuint &vao); 


//GLFW Callbacks 
static void error_callback(int error, const char* description) { 
    fprintf(stderr, "Error: %s\n", description); 
} 

static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) { 
    if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) { 
     glfwSetWindowShouldClose(window, GL_TRUE); 
    } 
} 

int main() { 
    glfwSetErrorCallback(error_callback); 
    //Initialize GLFW 
    if (!glfwInit()) { 
     fprintf(stderr, "Failed to initialize GLFW.\n"); 
     return -1; 
    } 

    //Set GLFW window settings and create window 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); 
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3); 
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); 
    glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); 
    GLFWwindow* window = glfwCreateWindow(500, 500, "My window", NULL, NULL); 
    if(!window) { 
     fprintf(stderr, "Window or context creation failed.\n"); 
     return -1; 
    } 

    glfwSetKeyCallback(window, key_callback); 
    glfwMakeContextCurrent(window); 

    //Initialize GLEW 
    glewExperimental = GL_TRUE; 
    if(glewInit() != GLEW_OK) { 
     fprintf(stderr, "Failed to initialize glew"); 
     glfwTerminate(); 
     return -1; 
    } 
    //Create a vertex array object 
    GLuint vao; 

    //Initialize the data to be rendered 
    initialize(vao); 

    while (!glfwWindowShouldClose(window)) { 
     display(vao, window); 
     glfwPollEvents(); 
    } 
    glfwTerminate(); 
    return 0; 
} 

//Render scene 
void display(GLuint &vao, GLFWwindow* window) { 
    //Red background 
    glClearColor(1.0f, 0.0f, 0.0f, 0.0f); 
    glClear(GL_COLOR_BUFFER_BIT); 

    glBindVertexArray(vao); 
    glDrawArrays(GL_POINTS, 0, 12); 
    // Swap front and back buffers 
    glfwSwapBuffers(window); 
} 

void initialize(GLuint &vao) { 
    glEnable(GL_PROGRAM_POINT_SIZE); 
    // Use a Vertex Array Object 
    glGenVertexArrays(1, &vao); 
    glBindVertexArray(vao); 

    //Store verex positions in an array 
    GLfloat vertices[24] = { 
     0.0, 0.0, // Only these 
     0.5, 0.5, //three points show up 
     1.0, 1.0, //on the window screen 

     4.0, 4.0, 
     5.0, 5.0, 
     6.0, 6.0, 

     7.0, 7.0, 
     8.0, 8.0, 
     9.0, 9.0, 

     10.0, 10.0, 
     11.0, 11.0, 
     12.0, 12.0, 
    }; 

    //Create a vertex buffer object to store the vertex data 
    GLuint vbo; 
    //Generates 1 buffer object name and stores it in vbo 
    glGenBuffers(1, &vbo); 
    //Bind the buffer object to the buffer binding target 
    glBindBuffer(GL_ARRAY_BUFFER, vbo); 
    //Creates and initializes the buffer object's data store(with data from vertices) 
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); 

    GLuint shaderProgram = create_program("/Users/.../vert.shader", "/Users/.../frag.shader"); //path to shader files 

    // Get the location of the attributes that enters in the vertex shader 
    GLint posAttrib = glGetAttribLocation(shaderProgram, "position"); 

    // Specify how the data for position can be accessed 
    glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 0, 0); 

    //Enable the attribute 
    glEnableVertexAttribArray(posAttrib); 
} 

// Read a shader source from a file 
// store the shader source in a std::vector<char> 
void read_shader_src(const char *fname, std::vector<char> &buffer) { 
    std::ifstream in; 
    in.open(fname, std::ios::binary); 

    if(in.is_open()) { 
     // Get the number of bytes stored in this file 
     in.seekg(0, std::ios::end); 
     size_t length = (size_t)in.tellg(); 

     // Go to start of the file 
     in.seekg(0, std::ios::beg); 

     // Read the content of the file in a buffer 
     buffer.resize(length + 1); 
     in.read(&buffer[0], length); 
     in.close(); 
     // Add a valid C - string end 
     buffer[length] = '\0'; 
    } 
    else { 
     std::cerr << "Unable to open " << fname << " I'm out!" << std::endl; 
     exit(-1); 
    } 
} 

//Compile a shader 
GLuint load_and_compile_shader(const char* fname, GLenum shaderType) { 
    //Load a shader from an external file 
    std::vector<char> buffer; 
    read_shader_src(fname, buffer); 
    const char *src = &buffer[0]; 

    //Create and compile the shader 
    GLuint shader = glCreateShader(shaderType); 
    glShaderSource(shader, 1, &src, NULL); 
    glCompileShader(shader); 

    GLint shader_compiled; 
    glGetShaderiv(shader, GL_COMPILE_STATUS, &shader_compiled); 
    if(!shader_compiled) { 
     GLchar message[1024]; 
     glGetShaderInfoLog(shader, 1024, NULL, message); 
     std::cerr << "Shader compilation failed."; 
     std::cerr << "Log: " << &message << std::endl; 
     glfwTerminate(); 
     exit(-1); 
    } 
    return shader; 
} 

// Create a program from two shaders 
GLuint create_program(const char *path_vert_shader, const char *path_frag_shader) { 
    // Load and compile the vertex and fragment shaders 
    GLuint vertexShader = load_and_compile_shader(path_vert_shader, GL_VERTEX_SHADER); 
    GLuint fragmentShader = load_and_compile_shader(path_frag_shader, GL_FRAGMENT_SHADER); 

    // Attach the above shader to a program 
    GLuint shaderProgram = glCreateProgram(); 
    glAttachShader(shaderProgram, vertexShader); 
    glAttachShader(shaderProgram, fragmentShader); 

    // Flag the shaders for deletion 
    glDeleteShader(vertexShader); 
    glDeleteShader(fragmentShader); 

    // Link and use the program 
    glLinkProgram(shaderProgram); 
    glUseProgram(shaderProgram); 

    return shaderProgram; 
} 

Welche Änderungen muss ich machen, damit ich den Rest der Punkte plotten?

Vertex Shader -

#version 150 core 

in vec4 position; 

void main() { 
    gl_Position = position; 
    gl_PointSize = 10.0; 
} 

Fragment Shader -

#version 150 core 

out vec4 out_color; 

void main() { 
    out_color = vec4(1.0, 1.0, 1.0, 1.0); 
} 
+5

Ich verstehe, dass dies nur für die Praxis, sondern Einstellung getrennte Pixel OpenGL Primitiven ziehen verweigert irgendwie den Zweck GL ... – HolyBlackCat

+0

Müssen Sie nur Linien zeichnen oder müssen Sie den Bresenham-Algorithmus verwenden? – pleluron

+0

@pleluron Ich muss den Algorithmus für die "Rasterisierung" verwenden. Ich weiß, ich hätte 'GL_LINES' sonst verwenden können. – ByteMan2021

Antwort

2

Aus den Kommentaren klingt es wie Sie versuchen, OpenGL anstelle einer wirklich alten Grafikbibliothek, die für eine Klasse erforderlich ist, zu verwenden. Da Computergrafik so viel verändert hat, dass das, was Sie in modernen OpenGL versuchen, unangemessen ist, sollten Sie dies für die aktuelle Aufgabe und Ihre späteren tun:

Haftungsausschluss: Dies ist keine sinnvolle Art zu zeichnen eine Linie in der modernen OpenGL

  1. erstellen eine 2D-Array von einer beliebigen Größe, groß genug, dass die gesamte Linie auf sie gezogen werden kann.
  2. die Linie Zeichnen Sie die ursprüngliche Funktion verwenden, erstellen Sie eine setPixel Funktion, die Elemente in diesem Array
  3. Sobald Sie die Linienzeichnung fertig ändert (oder was auch immer sonst zukünftige Aufgaben haben tun Sie tun), erstellen Sie eine OpenGL-Textur aus dieses Array. Eine hervorragende Anleitung finden Sie hier: https://open.gl/textures

Einige grobe psuedocode:

GLuint tex; 
glGenTextures(1, &tex); 
glBindTexture(GL_TEXTURE_2D, tex); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_FLOAT, pixels); 
glBindTexture(GL_TEXTURE_2D, 0); 

Dann würden Sie einen Quad erstellen (wirklich nur zwei Dreiecke), die diese Textur auf Bildschirm zeichnen. Nach dem Öffnen.gl guide sollte gut funktionieren, da sie dies bereits für ihre Texturen tun. Ziehen von ihrem bereitgestellten Code (das ist ein bisschen mehr der Fall ist, alle richtig und nach dem spec obwohl):

GLfloat vertices[] = { 
// Position  Color    Texcoords 
    -0.5f, 0.5f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, // Top-left 
    0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Top-right 
    0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, // Bottom-right 
    -0.5f, -0.5f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f // Bottom-left 
}; 

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

// Set up buffer objects, create shaders, initialize GL, etc. 

//drawing 
//bind buffers, enable attrib arrays, etc 
glBindTexture(GL_TEXTURE_2D, tex); 

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); 
+1

Wenn Sie wirklich schick werden möchten, könnten Sie sich ein dauerhaft abbildbares Pixel-Puffer-Objekt beschaffen. Damit würden Ihre Pixel-Push-Operationen (logisch) direkt im Grafikspeicher stattfinden und das finishing glTexSubImage würde nur das Textur-Objekt aktualisieren. In dieser Zeile könnten Sie sogar blutig gehen und stattdessen die Vulkan-API verwenden, bei der eine "Textur" eigentlich nur Metadaten für die Interpretation der Daten in einem beliebig zugänglichen Puffer als Bilddaten ist. – datenwolf

+0

Können Sie die Punkte 1. und 2. näher erläutern? Genauer gesagt möchte ich wissen, wie man die an "glTexImage2D" übergebene 'pixel'-Variable deklariert und initialisiert. Das Tutorial zeigt nur, wie Texturen aus einem bereits vorhandenen Bild geladen werden. – ByteMan2021

+0

@datenwolf \t Basierend auf Ihrer Antwort hier (http://stackoverflow.com/a/18369213/5306573): "Eine Textur ist jedoch möglicherweise nicht die effizienteste Möglichkeit, einzelne Pixel auf dem Bildschirm zu aktualisieren. Es ist jedoch eine großartige Idee, zuerst Pixel eines Pixelpuffers zu zeichnen, der zur Anzeige in eine Textur geladen wird, die dann auf ein vollständiges Ansichtsfenster-Viereck gezeichnet wird. " Hinweise zur Vorgehensweise (Zeichnen von Pixeln in einem Pixelpuffer); was ich vermute kann dann in einem Aufruf an 'glTexImage2D' übergeben werden? – ByteMan2021