2016-09-10 2 views
0

Я беру курс компьютерной графики в своем университете. Мне нужно реализовать основные алгоритмы рисования линий в современном OpenGL (3.3+), чтобы нарисовать примитивы на экране. Вот функция для рисования линий алгоритма Bresenham, что я хочу реализовать - Рисование пикселей в 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); 
     } 
    } 
} 

Я невежественный о том, как реализовать функцию setPixel(). Большинство ответов я нашел здесь и в других местах использовать старые OpenGL функции -

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 
} 

Что является эквивалентом способ сделать это в OpenGL 3.3+? Предполагая, что я могу добавить «пиксели» в массив std::vector, как инициализировать массив буфера вершин для хранения этих данных?

Другая проблема, с которой я столкнулся при попытке построения точки с использованием GL_POINTS, заключается в том, что из-за отсечения при преобразовании в нормализованные координаты устройства точки за пределами диапазона [-1,1] в любом направлении не отображаются в окне.

Например, на экране окна отображаются только первые три точки. См initialize() функции -

#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; 
} 

Каких изменений я делаю, так что я могу построить остальные пункты?

Vertex Shader -

#version 150 core 

in vec4 position; 

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

Фрагмент Shader -

#version 150 core 

out vec4 out_color; 

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

Я понимаю, что это только для практики, но установка отдельных пикселей с использованием OpenGL для рисования примитивов своего рода отрицает цель GL ... – HolyBlackCat

+0

Вам нужно только рисовать линии или вам нужно использовать алгоритм Брешенема? – pleluron

+0

@pleluron Я должен использовать алгоритм для «растеризации». Я знаю, что мог бы использовать «GL_LINES» в противном случае. – ByteMan2021

ответ

2

Из комментариев кажется, что вы пытаетесь использовать OpenGL вместо действительно старой графической библиотеки, необходимой для класса. Поскольку компьютерная графика изменилась настолько, что то, что вы пытаетесь сделать в современном OpenGL, необоснованно, вы должны попробовать сделать это для текущего задания и ваших более поздних:

Отказ от ответственности: Это не разумный способ рисования линия в современной OpenGL

  1. Создание 2d массив некоторого произвольного размера, достаточно большой, что вся линия может быть обращено на него.
  2. Проведем линию, используя оригинальную функцию, создать некоторую setPixel функцию, которая изменяет элементы в массиве
  3. После того, как вы закончите рисовать линию (или делать все, что еще в будущем задания будет вам делать), создать текстуру OpenGL от этот массив. Отличный гид доступен здесь: https://open.gl/textures

Некоторые шероховатой 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); 

Тогда вы можете создать четырехугольник (на самом деле только два треугольника), что нарисовать эту текстуру на экран. После открытия.gl guide должен хорошо работать, поскольку они уже делают это для их текстур. Вытащив из их при условии коды (который делает дополнительным бит, все правильно и следующие спецификации, хотя):

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

Если вы хотите получить действительно фантазию, вы можете получить настойчиво отображаемый пиксельный буферный объект. При этом операции нажатия пикселей будут выполняться (логически) непосредственно в графической памяти, а финишная обработка glTexSubImage будет только обновлять объект текстуры. В этой строке вы можете даже пойти на краю и использовать API Vulkan вместо этого, где «текстура» на самом деле представляет собой только метаданные о том, как интерпретировать данные в произвольно доступном буфере в качестве данных изображения. – datenwolf

+0

Можете ли вы подробно остановиться на пунктах 1. и 2.? В частности, я хочу знать, как объявить и инициализировать переменную 'pixel', переданную в' glTexImage2D'. В учебнике показано, как загружать текстуры из уже существующего изображения. – ByteMan2021

+0

@datenwolf \t На основании вашего ответа здесь (http://stackoverflow.com/a/18369213/5306573): «Однако текстура может быть не самым эффективным способом прямого обновления отдельных пикселей на экране. отличная идея, чтобы сначала нарисовать пиксели пиксельного буфера, который для отображения загружен в текстуру, который затем нарисован на полный квадрат видового экрана ». Любые подсказки о том, как это сделать (рисование пикселей в пиксельном буфере); который, как я полагаю, может быть передан при вызове 'glTexImage2D'? – ByteMan2021

-2

Вместо того, чтобы пытаться нарисовать пиксель сразу попытаться сохранить координаты в массив, а затем сделать массив с помощью одной команды. Есть разные способы сделать это в OpenGL. Вы можете узнать больше об этом здесь http://www.songho.ca/opengl/gl_vertexarray.html и здесь https://www.opengl.org/wiki/Vertex_Specification

Смежные вопросы