如何在PyOpenGL中使用顶点数组对象和glDrawElements进行绘图

9
我有以下代码,它应该只是将一个绿色三角形绘制到屏幕上。它使用顶点数组对象和索引缓冲区进行绘制,并具有我可以制作的最简单着色器。
起初,我没有使用索引缓冲区,只是使用glDrawArrays进行绘图调用,这很好,但当我改用glDrawElements时,屏幕上什么都没有绘制(完全黑屏)。
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()

如果我将glDrawElements注释掉并取消注释glDrawArrays行,则它可以正常工作,所以至少顶点VBO被正确输入了。

我做错了什么?我能够找到的所有OpenGL文档都表明我做得很正确,因此我显然对OpenGL本身或PyOpenGL包装程序有所误解。

编辑

将VAO设置更改为使用更直接的OpenGL函数而不是VBO包装程序,例如:

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)

没有任何区别。glDrawArrays仍然有效,而glDrawElements仍然无法使用。


这些函数 indexPositions.bind() 的功能不清楚。 - Nicol Bolas
@NicolBolas 很抱歉,它们是PyOpenGL的东西。 它们在内部所做的一切就是调用一次glBindBuffer(以及相应的解绑)。 同样,vbo.VBO() 调用了 glGenBuffers, glBindBufferglBufferData - Milliams
@NicolBolas,我已尝试使用实际的OpenGL命令(如问题编辑中所示)替换这些调用,但似乎没有任何区别。 - Milliams
1个回答

15

感谢@NicolBolas。他激励我真正地将这个代码变成可运行的。我已经删除了vertexArrayObject(因为我们已经有了用于点和索引的VBOs)。所以在glDraw*调用之前,您只需绑定索引和顶点缓冲区(以及属性)即可。当然非常重要的是,将glDrawElements的indices参数传递None(空指针),而不是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()

1
它似乎没有改变任何东西。我看过的所有文档(例如[维基](https://www.opengl.org/wiki/Vertex_Rendering#Basic_Drawing))都建议*count*是要使用的索引数。此外,如果使用设置了`GL_ELEMENT_ARRAY_BUFFER`的VAO,我认为我不必指定*indices*? - Milliams
-1:因为错误而返回。glDrawElementsindices数组中提取的索引数量。 - Nicol Bolas
@NicolBolas - 好的。看起来你是对的。那么可能是这样的 glDrawElements(GL_TRIANGLES, indices) - kerim
2
太好了。现在它可以工作了。看来我犯的两个主要错误是indices类型是uint16而不是uint32,并且我传递给glDrawElements的是0而不是None。我还能够使用您的代码与VAO一起使用(因为这是我计划今后使用的)。 - Milliams
谢谢!我花了很多时间让我的代码运行,甚至写了C语言版本(它能够运行!)。解决方法是将“None”传递给“glDrawElements”,而不是“0”。这个方法在VAO中仍然适用。 - Alexey Markov
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接