1

I recently got into OpenGL, and used PyOpenGL and the fixed-function pipeline (I know, I know) to draw cubes and stuff.

Anyway, everyone told me that fixed-function is horrible and deprecated, so I got into core-profile OpenGL just now. I've been followig this tutorial, which is in C++, by just transforming everything into Python with basically the same libraries.

I've gotten to the point where I want to render a single 2D triangle using VBOs and VAOs, and the code runs, but doesn't actually draw anything.

Here it is (bear in mind, I'm totally new to this, so I probably messed up an elementary function call somewhere, and I don't really know how everything works):

import numpy as np
import glfw
from OpenGL.GL import *

def main():

    glfw.init()
    glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 4)
    glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 5)
    glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)

    window = glfw.create_window(800, 600, "helo wold", None, None)

    glfw.make_context_current(window)


    vertices = np.array([-0.5, -0.5, 0, 0.5, -0.5, 0, 0, 0.5, 0], dtype = 'float32')

    vertexShaderSource = '''#version 450 core
    layout (location = 0) in vec3 aPos;
    void main() {
        gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
    }
    '''

    vertexShader = glCreateShader(GL_VERTEX_SHADER)
    glShaderSource(vertexShader, vertexShaderSource)
    glCompileShader(vertexShader)

    fragmentShaderSource = '''#version 450 core
    out vec4 FragColor;
    void main() {
        FragColor = vec4(1.0f, 0.5f, 0.2f, 1.0f);
    }
    '''

    fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
    glShaderSource(fragmentShader, fragmentShaderSource)
    glCompileShader(fragmentShader)

    shaderProgram = glCreateProgram(1)
    glAttachShader(shaderProgram, vertexShader)
    glAttachShader(shaderProgram, fragmentShader)
    glLinkProgram(shaderProgram)

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)

    vbo, vao = glGenBuffers(1), glGenVertexArrays(1)

    glBindVertexArray(vao)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(GL_ARRAY_BUFFER, len(vertices), vertices, GL_STATIC_DRAW)

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, 0)
    glEnableVertexAttribArray(0)

    glBindBuffer(GL_ARRAY_BUFFER, 0)
    glBindVertexArray(0)

    while not glfw.window_should_close(window):

        glClearColor(0.2, 0.3, 0.3, 1.0)
        glClear(GL_COLOR_BUFFER_BIT)

        glUseProgram(shaderProgram)
        glBindVertexArray(vao)
        glDrawArrays(GL_TRIANGLES, 0, 3)

        glfw.swap_buffers(window)
        glfw.poll_events()

    glfw.terminate()

if __name__ == '__main__':
    main()

It opens the window fine, and has the background color I gave it, but it doesn't draw the triangle in any way.

I'd love it if someone could tell me what I'm doing wrong, thanks!

Rabbid76
  • 202,892
  • 27
  • 131
  • 174
Parashoo
  • 315
  • 1
  • 10

1 Answers1

1

The 2nd parameter of glBufferData has to be the size in bytes, len(vertices)*4 rather than len(vertices):

glBufferData(GL_ARRAY_BUFFER, len(vertices)*4, vertices, GL_STATIC_DRAW)

Since PyOpenGL's glBufferData is overloaded, the size parameter can be omitted (if data is a ctypes array or numpy.array):

glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW)

The last parameter of glVertexAttribPointer ahs to be of type const GLvoid *. Thus it has to be None or ctypes.c_void_p(0) rather than 0:

Either

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, None)

or

import ctypes
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 12, ctypes.c_void_p(0))

Further more I recommend to evaluate if the shaders are compiled successfully (glGetShaderiv):

vertexShader = glCreateShader(GL_VERTEX_SHADER)
glShaderSource(vertexShader, vertexShaderSource)
glCompileShader(vertexShader)
if not glGetShaderiv(vertexShader, GL_COMPILE_STATUS ):
    print('vertex shader compile error:')
    print(glGetShaderInfoLog(vertexShader))
fragmentShader = glCreateShader(GL_FRAGMENT_SHADER)
glShaderSource(fragmentShader, fragmentShaderSource)
glCompileShader(fragmentShader)
if not glGetShaderiv(fragmentShader, GL_COMPILE_STATUS):
    print('fragment shader compile error:')
    print(glGetShaderInfoLog(fragmentShader))

And the program is linked successfully (glGetProgramiv):

glLinkProgram(shaderProgram)
if not glGetProgramiv(shaderProgram, GL_LINK_STATUS):
    print('link error:')
    print(glGetProgramInfoLog(shaderProgram))
Rabbid76
  • 202,892
  • 27
  • 131
  • 174
  • Thanks a lot, that worked for me! Is there a way to generalize the stride in `glVertexAttribPointer`? Just giving it 12 because I know that there are 3 floats of 4 bytes puts me on edge. – Parashoo May 04 '20 at 09:25
  • @Parashoo The _stride_ has to be specified in bytes. But, since you have just the vertices, _stride_ can be set 0. If _stride_ is 0, then it is computed by the tuple size (3) and the size of the type (`GL_FLOAT`). – Rabbid76 May 04 '20 at 09:29