Abu Yousuf
Abu Yousuf

Reputation: 6107

How to draw line in OpenGL?

I want to draw a line in OpenGL.

glBegin(GL_LINES);
    glVertex2f(.25,0.25);
    glVertex2f(.75,.75);
glEnd();

This code draws the line, but if I want to draw a line from coordinate (10,10) to coordinate (20,20) what should I do?

What do (.25, .25) and (.75, .75) mean?

Upvotes: 26

Views: 151694

Answers (4)

Dave
Dave

Reputation: 43

One way to draw lines in modern OpenGL is by batching. Since lines are just 2 points, we can feasibly store the vertex data in a list.

Here's an implementation to draw 2D and 3D lines in C++ using glm with shaders.

std::vector<float> lineData{};

// draw a line by dynamically storing vertex data
void draw_line(const glm::vec3 &p1, const glm::vec3 &p2, const glm::vec3 &color)
{
  // point 1
  lineData.push_back(p1.x);
  lineData.push_back(p1.y);
  lineData.push_back(p1.z);

  // color
  lineData.push_back(color.r);
  lineData.push_back(color.g);
  lineData.push_back(color.b);

  // point 2
  lineData.push_back(p2.x);
  lineData.push_back(p2.y);
  lineData.push_back(p2.z);

  // color
  lineData.push_back(color.r);
  lineData.push_back(color.g);
  lineData.push_back(color.b);
}
void draw_line_2d(const glm::vec2 &p1, const glm::vec2 &p2,
                  const glm::vec3 &color)
{
  draw_line(glm::vec3(p1, 0), glm::vec3(p2, 0), color);
}

void draw_lines_flush()
{
  static unsigned int vao, vbo;

  static bool created = false;
  if (!created)
  {
    created = true;

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

    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, lineData.size() * sizeof(float),
                 lineData.data(), GL_DYNAMIC_DRAW);

    glEnableVertexAttribArray(0);
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                          (void *)0);

    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float),
                          (void *)(3 * sizeof(float)));
  }
  else
  {
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, lineData.size() * sizeof(float),
                 lineData.data(), GL_DYNAMIC_DRAW);
  }

  // 6 floats make up a vertex (3 position 3 color)
  // divide by that to get number of vertices to draw
  int count = lineData.size() / 6;

  glBindVertexArray(vao);
  glDrawArrays(GL_LINES, 0, count);

  lineData.clear();
}

line.vert

#version 330 core

layout(location = 0) in vec3 aPos;
layout(location = 1) in vec3 aColor;

out flat vec3 Color;

uniform mat4 view;
uniform mat4 projection;

void main()
{
    gl_Position = projection * view * vec4(aPos, 1.0);
    Color = aColor;
}

line.frag

#version 330 core

out vec4 FragColor;
in flat vec3 Color;

void main()
{
    FragColor = vec4(Color, 1.0);
}

In your render loop, you can call draw_line() however you like. To actually draw all the lines, flush the batch by calling draw_lines_flush(). It effectively draws a single big dynamically created mesh which contains the lines.

Upvotes: 1

bitbang
bitbang

Reputation: 2192

Try this too, you can choose color and adjust line width.

def drawline(self, x1, y1, z1, x2, y2, z2, r=0, g=0, b=0, w=2):

    glLineWidth(w)
    glBegin(GL_LINES)
    glColor3fv((r/255, g/255, b/255))
    glVertex3fv((x1, y1, z1))
    glVertex3fv((x2, y2, z2))
    glEnd()

Upvotes: 1

jackw11111
jackw11111

Reputation: 1547

With OpenGL2 :

Each point value in glVertex2f is between -1 and 1, bottom left is (-1, -1), top right is (1,1) and center is (0, 0).

To map an absolute point to normalized space:

  • Divide x through by the window width, w, to get the point in the range from 0 to 1.

  • Multiply it by 2 to get the range from 0 to 2.

  • Subtract 1 to get the desired -1 to 1 range.

  • Repeat for y value and windows height ,h.

For example:

double x1 = 10;
double y1 = 10;
double x2 = 20;
double y2 = 20;

x1 = 2*x1 / w - 1;
y1 = 2*y1 / h - 1;

x2 = 2*x2 / w - 1;
y2 = 2*y2 / h - 1;

glBegin(GL_LINES);
    glVertex2f(x1, y1);
    glVertex2f(x2, y2);
glEnd();

With OpenGL3+ : Using the programmable pipeline to draw a line is slightly more involved. You can create a Line class that will take two points and send them to the GPU, and draw them with a simple shader program. All the setup can be done in the constructor, and can be modified with a few access functions:

class Line {
    int shaderProgram;
    unsigned int VBO, VAO;
    vector<float> vertices;
    vec3 startPoint;
    vec3 endPoint;
    mat4 MVP;
    vec3 lineColor;
public:
    Line(vec3 start, vec3 end) {

        startPoint = start;
        endPoint = end;
        lineColor = vec3(1,1,1);
        MVP = mat4(1.0f);

        const char *vertexShaderSource = "#version 330 core\n"
            "layout (location = 0) in vec3 aPos;\n"
            "uniform mat4 MVP;\n"
            "void main()\n"
            "{\n"
            "   gl_Position = MVP * vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
            "}\0";
        const char *fragmentShaderSource = "#version 330 core\n"
            "out vec4 FragColor;\n"
            "uniform vec3 color;\n"
            "void main()\n"
            "{\n"
            "   FragColor = vec4(color, 1.0f);\n"
            "}\n\0";

        // vertex shader
        int vertexShader = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
        glCompileShader(vertexShader);
        // check for shader compile errors

        // fragment shader
        int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
        glCompileShader(fragmentShader);
        // check for shader compile errors

        // link shaders
        shaderProgram = glCreateProgram();
        glAttachShader(shaderProgram, vertexShader);
        glAttachShader(shaderProgram, fragmentShader);
        glLinkProgram(shaderProgram);
        // check for linking errors

        glDeleteShader(vertexShader);
        glDeleteShader(fragmentShader);

        vertices = {
             start.x, start.y, start.z,
             end.x, end.y, end.z,

        };
        
        glGenVertexArrays(1, &VAO);
        glGenBuffers(1, &VBO);
        glBindVertexArray(VAO);

        glBindBuffer(GL_ARRAY_BUFFER, VBO);
        glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices.data(), GL_STATIC_DRAW);

        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
        glEnableVertexAttribArray(0);

        glBindBuffer(GL_ARRAY_BUFFER, 0); 
        glBindVertexArray(0); 

    }

    int setMVP(mat4 mvp) {
        MVP = mvp;
        return 1;
    }

    int setColor(vec3 color) {
        lineColor = color;
        return 1;
    }

    int draw() {
        glUseProgram(shaderProgram);
        glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
        glUniform3fv(glGetUniformLocation(shaderProgram, "color"), 1, &lineColor[0]);

        glBindVertexArray(VAO);
        glDrawArrays(GL_LINES, 0, 2);
        return 1;
    }

    ~Line() {

        glDeleteVertexArrays(1, &VAO);
        glDeleteBuffers(1, &VBO);
        glDeleteProgram(shaderProgram);
    }
};

Initializing some 3D lines with Line line(vec3 ..., vec3 ...), setting the model-view-projection matrix line.setMVP(projection * view * model) and line.draw() and rotating the camera will produce something like this:

xyz axis

Example code

Note: If all you need is 2D lines, you will just need to specify the vec3 end point coordinates with the z value set to 0, and remove the projection matrix from the setMVP call, and set camera position to (0,0,0). The same applies to drawing 2D lines as explained above for OpenGL2, so coordinates will need to be sent to OpenGL in NDC space.

Upvotes: 46

n1xx1
n1xx1

Reputation: 2076

(.25, .25) and (.75,.75) are line's start and end point.

To draw a line from (10,10) to (20,20):

glBegin(GL_LINES);
    glVertex2f(10, 10);
    glVertex2f(20, 20);
glEnd();

Upvotes: 33

Related Questions