9

I have the following code which should simply draw a green triangle to the screen. It is using Vertex Array Objects and index buffers to draw and has the simplest shader I could make.

At first I was not using index buffers and was simply making the draw call with glDrawArrays which worked fine but when I change it to use glDrawElements then nothing is drawn to the screen (it is entirely black).

from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
                                                  glBindVertexArray

import pygame

import numpy as np

def run():
    pygame.init()
    screen = pygame.display.set_mode((800,600), pygame.OPENGL)

    #Create the Vertex Array Object
    vertexArrayObject = GLuint(0)
    glGenVertexArrays(1, vertexArrayObject)
    glBindVertexArray(vertexArrayObject)

    #Create the VBO
    vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
    vertexPositions = vbo.VBO(vertices)

    #Create the index buffer object
    indices = np.array([0,1,2], dtype='uint16')
    indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)

    indexPositions.bind()
    vertexPositions.bind()

    glEnableVertexAttribArray(0) # from 'location = 0' in shader
    glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)

    glBindVertexArray(0)
    vertexPositions.unbind()
    indexPositions.unbind()

    #Now create the shaders
    VERTEX_SHADER = shaders.compileShader("""
    #version 330
    layout(location = 0) in vec4 position;
    void main()
    {
        gl_Position = position;
    }
    """, GL_VERTEX_SHADER)

    FRAGMENT_SHADER = shaders.compileShader("""
    #version 330
    out vec4 outputColor;
    void main()
    {
        outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
    }
    """, GL_FRAGMENT_SHADER)

    shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)

    #The draw loop
    while True:
        glUseProgram(shader)
        glBindVertexArray(vertexArrayObject)

        #glDrawArrays(GL_TRIANGLES, 0, 3) #This line works
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, 0) #This line does not

        glBindVertexArray(0)
        glUseProgram(0)

        # Show the screen
        pygame.display.flip()

run()

If I simply comment out the glDrawElements and uncomment the glDrawArrays line then it works correctly so at least the vertex VBO is being input correctly.

What am I doing wrong here? All the OpenGL documentation I have been able to find suggests that I am doing this correctly so I am obviously misunderstanding something either about OpenGL itself or the PyOpenGL wrapper.

EDIT

Changing the VAO setup to use more direct OpenGL function rather than the VBO wrapper like:

vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
vertexPositions = glGenBuffers(1)
glBindBuffer(GL_ARRAY_BUFFER, vertexPositions)
glBufferData(GL_ARRAY_BUFFER, vertices, GL_STATIC_DRAW)

#Create the index buffer object
indices = np.array([0,1,2], dtype='uint16')
indexPositions = glGenBuffers(1)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexPositions)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexPositions)
glBindBuffer(GL_ARRAY_BUFFER, vertexPositions)

glEnableVertexAttribArray(0) # from 'location = 0' in shader
glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)

glBindVertexArray(0)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)
glBindBuffer(GL_ARRAY_BUFFER, 0)

makes no difference. glDrawArrays still works and glDrawElements still doesn't.

Justin Meiners
  • 9,345
  • 6
  • 44
  • 90
Milliams
  • 1,373
  • 1
  • 19
  • 29
  • It's unclear what these functions do: `indexPositions.bind()`. – Nicol Bolas Jan 16 '13 at 19:31
  • @NicolBolas Sorry, they're PyOpenGL things. All they do inside is a single call to `glBindBuffer` (and unbind respectively). Similarly, `vbo.VBO()` calls `glGenBuffers`, `glBindBuffer` and `glBufferData`. – Milliams Jan 16 '13 at 20:09
  • @NicolBolas I've tried exchanging those calls with the actual OpenGL commands (as shown in the question edit) and it doesn't appear to make a difference. – Milliams Jan 16 '13 at 20:51

1 Answers1

14

Thanks @NicolBolas. He motivated me to actually take this code and make it work. Instead of theoritizing:) I have removed vertexArrayObject(it's redundand as we already have VBOs for vertices and indices). So you just bind index and vertex buffers(along with attributes) prior to glDraw* call. And of course very important to pass None(null pointer) to glDrawElements indices instead of 0!

from OpenGL.GL import shaders
from OpenGL.arrays import vbo
from OpenGL.GL import *
from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, \
                                                  glBindVertexArray

import pygame

import numpy as np

def run():
    pygame.init()
    screen = pygame.display.set_mode((800,600), pygame.OPENGL|pygame.DOUBLEBUF)

    #Create the VBO
    vertices = np.array([[0,1,0],[-1,-1,0],[1,-1,0]], dtype='f')
    vertexPositions = vbo.VBO(vertices)

    #Create the index buffer object
    indices = np.array([[0,1,2]], dtype=np.int32)
    indexPositions = vbo.VBO(indices, target=GL_ELEMENT_ARRAY_BUFFER)

    #Now create the shaders
    VERTEX_SHADER = shaders.compileShader("""
    #version 330
    layout(location = 0) in vec4 position;
    void main()
    {
        gl_Position = position;
    }
    """, GL_VERTEX_SHADER)

    FRAGMENT_SHADER = shaders.compileShader("""
    #version 330
    out vec4 outputColor;
    void main()
    {
        outputColor = vec4(0.0f, 1.0f, 0.0f, 1.0f);
    }
    """, GL_FRAGMENT_SHADER)

    shader = shaders.compileProgram(VERTEX_SHADER, FRAGMENT_SHADER)

    #The draw loop
    while True:
        glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT)
        glUseProgram(shader)

        indexPositions.bind()

        vertexPositions.bind()
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 3, GL_FLOAT, False, 0, None)

        #glDrawArrays(GL_TRIANGLES, 0, 3) #This line still works
        glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, None) #This line does work too!

        # Show the screen
        pygame.display.flip()

run()
kerim
  • 2,106
  • 16
  • 16
  • 1
    It does not appear to change anything. All the docs I've seen (such as [the wiki](https://www.opengl.org/wiki/Vertex_Rendering#Basic_Drawing)) suggest that *count* is the number if indices to use. Also, I thought that I didn't have to specify *indices* if using a VAO with `GL_ELEMENT_ARRAY_BUFFER` set? – Milliams Jan 16 '13 at 19:13
  • -1: For being wrong. `glDrawElements` takes the number of indices to pull from the `indices` array. – Nicol Bolas Jan 16 '13 at 19:30
  • @NicolBolas - ok. Seems you're right. Could be something like this then `glDrawElements(GL_TRIANGLES, indices)`? – kerim Jan 16 '13 at 20:02
  • 2
    Fantastic. That works now. It seems the two major errors I had were the type of `indices` being `uint16` instead of `uint32` and that I was passing `0` instead of `None` to `glDrawElements`. I'm able to use your code also with VAO (as they are what I am planning on using going forward). – Milliams Jan 16 '13 at 21:58
  • Sure, VAO is the way to go. I assumed that vbo.VBO already uses it. I was wrong...again! – kerim Jan 17 '13 at 07:02
  • Yeah, PyOpenGL lacks a Pythonic VAO wrapper. As I learn more about their intricacies I might might provide them with a patch for one. – Milliams Jan 17 '13 at 11:28
  • Thanks so much!!! I suffered from this problem for days. Finally I just pass None instead of 0, and glDrawElements worked! I dont know why they made thing so inconsistent... ... ... – Shao-Kui Aug 22 '18 at 05:04
  • Is it really necessary to compile the shader and program during the code? PyOpenGL doesn't give you any other way? – user972014 Aug 24 '18 at 10:15
  • Thanks! I spent many time making my code works, even write C-version (and it works!). The solution was passing `None` to the `glDrawElements` instead of `0`. And this still works with VAO. – Alexey Markov Apr 06 '19 at 22:06