0

I'm a complete beginner to OpenGL programming and am trying to follow the Breakout tutorial at learnopengl.com but would like to draw the ball as an actual circle, instead of using a textured quad like Joey suggests. However, every result that Google throws back at me for "draw circle opengl 3.3" or similar phrases seems to be at least a few years old, and using even-older-than-that versions of the API :-(

The closest thing that I've found is this SO question, but of course the OP just had to use a custom VertexFormat object to abstract some of the details, without sharing his/her implementation of such! Just my luck! :P

There's also this YouTube tutorial that uses a seemingly-older version of the API, but copying the code verbatim (except for the last few lines which is where the code looks old) still got me nowhere.

My version of SpriteRenderer::initRenderData() from the tutorial:

void SpriteRenderer::initRenderData() {
    GLuint vbo;

    auto attribSize = 0;
    GLfloat* vertices = nullptr;

    // Determine whether this sprite is a circle or
    // quad and setup the vertices array accordingly
    if (!this->isCircle) {
        attribSize = 4;
        vertices = new GLfloat[24] {...}   // works for rendering quads
    } else {
        // This code is adapted from the YouTube tutorial that I linked
        // above and is where things go pear-shaped for me...or at least
        // **not** circle-shaped :P
        attribSize = 3;

        GLfloat x = 0.0f;
        GLfloat y = 0.0f;
        GLfloat z = 0.0f;
        GLfloat r = 100.0f;

        GLint numSides = 6;
        GLint numVertices = numSides + 2;

        GLfloat* xCoords = new GLfloat[numVertices];
        GLfloat* yCoords = new GLfloat[numVertices];
        GLfloat* zCoords = new GLfloat[numVertices];

        xCoords[0] = x;
        yCoords[0] = y;
        zCoords[0] = z;

        for (auto i = 1; i < numVertices; i++) {
            xCoords[i] = x + (r * cos(i * (M_PI * 2.0f) / numSides));
            yCoords[i] = y + (r * sin(i * (M_PI * 2.0f) / numSides));
            zCoords[i] = z;
        }

        vertices = new GLfloat[numVertices * 3];
        for (auto i = 0; i < numVertices; i++) {
            vertices[i * 3] = xCoords[i];
            vertices[i * 3 + 1] = yCoords[i];
            vertices[i * 3 + 2] = zCoords[i];
        }
    }

    // This is where I go back to the learnopengl.com code. Once
    // again, the following works for quads but not circles!
    glGenVertexArrays(1, &vao);
    glGenBuffers(1, &vbo);

    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, 24 * sizeof(
        GLfloat), vertices, GL_STATIC_DRAW);

    glBindVertexArray(vao);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(0, attribSize, GL_FLOAT, GL_FALSE,
        attribSize * sizeof(GLfloat), (GLvoid*)0);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);
}

And here's the SpriteRenderer::DrawSprite() method (the only difference from the original being lines 24 - 28):

void SpriteRenderer::Draw(vec2 position, vec2 size, GLfloat rotation, vec3 colour) {
    // Prepare transformations
    shader.Use();

    auto model = mat4(1.0f);
    model = translate(model, vec3(position, 0.0f));

    model = translate(model, vec3(0.5f * size.x, 0.5f * size.y, 0.0f));   // Move origin of rotation to center
    model = rotate(model, rotation, vec3(0.0f, 0.0f, 1.0f));              // Rotate quad
    model = translate(model, vec3(-0.5f * size.x, -0.5f * size.y, 0.0f)); // Move origin back

    model = scale(model, vec3(size, 1.0f)); // Lastly, scale

    shader.SetMatrix4("model", model);

    // Render textured quad
    shader.SetVector3f("spriteColour", colour);

    glActiveTexture(GL_TEXTURE0);
    texture.Bind();

    glBindVertexArray(vao);

    if (!isCircular) {
        glDrawArrays(GL_TRIANGLES, 0, 6);
    } else {
        glDrawArrays(GL_TRIANGLE_FAN, 0, 24);  // also tried "12" and "8" for the last param, to no avail
    }

    glBindVertexArray(0);
}

And finally, the shaders (different to the ones used for quads):

// Vertex shader
#version 330 core

layout (location = 0) in vec3 position;

uniform mat4 model;
uniform mat4 projection;

void main() {
    gl_Position = projection * model *
        vec4(position.xyz, 1.0f);
}

// Fragment shader
#version 330 core

out vec4 colour;

uniform vec3 spriteColour;

void main() {    
    colour = vec4(spriteColour, 1.0);
}

P.S. I know I could just use a quad but I'm trying to learn how to draw all primitives in OpenGL, not just quads and triangles (thanks anyway Joey)!

P.P.S I just realised that the learnopengl.com site has a whole section devoted to debugging OpenGL apps, so I set that up but to no avail :-( I don't think the error handling is supported by my driver (Intel UHD Graphics 620 latest driver) since the GL_CONTEXT_FLAG_DEBUG_BIT was not set after following the instructions:

Requesting a debug context in GLFW is surprisingly easy as all we have to do is pass a hint to GLFW that we'd like to have a debug output context. We have to do this before we call glfwCreateWindow:

glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);

Once we initialize GLFW we should have a debug context if we're using OpenGL version 4.3 or higher, or else we have to take our chances and hope the system is still able to request a debug context. Otherwise we have to request debug output using its OpenGL extension(s).

To check if we successfully initialized a debug context we can query OpenGL:

GLint flags; glGetIntegerv(GL_CONTEXT_FLAGS, &flags);

if (flags & GL_CONTEXT_FLAG_DEBUG_BIT) { // initialize debug output }

That if statement is never entered into!

halfer
  • 19,824
  • 17
  • 99
  • 186
Kenny83
  • 769
  • 12
  • 38
  • What's the result you get? – Yakov Galka Dec 22 '19 at 01:25
  • The last parameter of [`glDrawArrays`](https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawArrays.xhtml) has to be the number of vertices. Waht do you mean by *"how to draw all primitives"*? OpenGL [Primitives](https://www.khronos.org/opengl/wiki/Primitive) are points, lines and triangles. – Rabbid76 Dec 22 '19 at 10:29
  • Do you ever set the uniform `projection`, after the program is installed (by `shader.Use();`)? – Rabbid76 Dec 22 '19 at 10:39
  • Thanks for the comments peeps! :-) @ybungalobill I get most of my scene rendered correctly, except the circles which don't appear at all. @Rabbid76 Sorry I thought "primitives" meant "basic 2D shapes". Guess I need to read a LOT more documentation lol :P And yes, I set the projection matrix in my game's setup function, like so : `circleShader.Use().SetMatrix4("projection", ortho(0.0f, static_cast(this->Width), static_cast(this->Height), 0.0f, -1.0f, 1.0f));`. – Kenny83 Dec 22 '19 at 11:44
  • [Learning Modern 3D Graphics Programming](https://paroj.github.io/gltut/Illumination/Tutorial%2013.html) – Ripi2 Dec 22 '19 at 17:22

1 Answers1

0

Thanks to @Mykola's answer to this question I have gotten half-way there:

numVertices = 43;
vertices = new GLfloat[numVertices];

auto i = 2;
auto x = 0.0f,
     y = x,
     z = x,
     r = 0.3f;

auto numSides = 21;
auto TWO_PI = 2.0f * M_PI;
auto increment = TWO_PI / numSides;

for (auto angle = 0.0f; angle <= TWO_PI; angle += increment) {
    vertices[i++] = r * cos(angle) + x;
    vertices[i++] = r * sin(angle) + y;
}

Which gives me this.

Two questions I still have:

  1. Why is there an extra line going from the centre to the right side and how can I fix it?
  2. According to @user1118321's comment on a related SO answer, I should be able to prepend another vertex to the array at (0, 0) and use GL_TRIANGLE_FAN instead of GL_LINE_LOOP to get a coloured circle. But this results in no output for me :-( Why?
Kenny83
  • 769
  • 12
  • 38