基础OpenGL,顶点缓冲区和Pyglet

7 投票
2 回答
5434 浏览
提问于 2025-04-16 09:31

编辑:rotoglup 找到了我代码中的问题,添加我之前移除的着色器后解决了这个问题。请查看我下面的回答,里面有正确的代码(包含着色器)。

大家好!

我正在尝试从这个教程学习一些现代 OpenGL 的基础知识。

不过,我想用 python/pyglet 来实现,而不是 C++。我知道 pyglet 可以帮我简化很多底层 OpenGL 的内容;但在把这些内容隐藏在抽象层之前,我想先理解一些基础知识。

我的问题非常简单:下面的代码只画了一个点,而我期待的是三个点。我的代码和教程中的 C++ 代码基本一致,唯一的不同是我移除了顶点和片段着色器(通过gletools在 python 中实现),这似乎对我的问题没有影响。

把事情简化成一个点后,出现了我不理解的行为(第一个坐标似乎是唯一影响结果的),这让我觉得我可能对 pyglet、OpenGL,甚至是 3D 的一些基本概念理解得不够透彻 :p

以下是相关代码:

import pyglet
from pyglet.gl import *

window = pyglet.window.Window()

positionBufferObject = GLuint()
vao = GLuint()

vertexPositions = [0.0, 0.0, 0.0,
                   0.25, 0.0, 0.0,
                   1.75, 1.75, 0.0]

vertexPositionsGl = (GLfloat * len(vertexPositions))(*vertexPositions)

@window.event
def on_draw():
    glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0)
    glDrawArrays(GL_POINTS, 0, 3)
    glDisableVertexAttribArray(0)

glGenBuffers(1, positionBufferObject)
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject)
glBufferData(GL_ARRAY_BUFFER, len(vertexPositionsGl)*4, vertexPositionsGl, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)

glClearColor(0.0, 0.0, 0.0, 0.0)
pyglet.app.run()

2 个回答

5

我终于搞明白了!

原来我之前被一些事情搞混了,比如调整顶点、使用glDrawArrays和glVertexAttribPointer,有时候只会显示一个点。这其实是个意外:按照rotoglup的回答进行修正后,什么都没有被绘制出来。这才是我最开始问题中代码的“正确”表现。

我总结了一下自己的错误,希望能帮助到其他人:

  • 把w坐标设置成了0,而不是1,完全没理解教程中关于裁剪空间转换的解释。

  • 给glBufferData传递了缓冲区的顶点数量,而它其实需要的是字节数。

  • 把顶点着色器和片段着色器去掉了(为了简化代码,寻找上面两个问题)。这样导致什么都无法渲染……除了我偶尔看到的那个(有问题的?)单点。

顺便说一下,下面的代码能显示一个三角形,使用的是通过的着色器,我希望这段代码是最新且正确的OpenGL。需要的库有pyglet和gletools。

import pyglet
from pyglet.gl import *
from gletools import ShaderProgram, FragmentShader, VertexShader

window = pyglet.window.Window()

positionBufferObject = GLuint()

vertexPositions = [0.75, 0.75, 0.0, 1.0,
                   0.75, -0.75, 0.0, 1.0,
                   -0.75, -0.75, 0.0, 1.0]
vertexPositionsGl = (GLfloat * len(vertexPositions))(*vertexPositions)

program = ShaderProgram(
    FragmentShader('''
    #version 330
    out vec4 outputColor;
    void main()
    {
       outputColor = vec4(1.0f, 1.0f, 1.0f, 1.0f);
    }'''),
    VertexShader('''
    #version 330
    layout(location = 0) in vec4 position;
    void main()
    {
        gl_Position = position;
    }''')
)

@window.event
def on_draw():
    with program:
        glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject)
        glEnableVertexAttribArray(0)
        glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0)
        glDrawArrays(GL_TRIANGLES, 0, 3)
        glDisableVertexAttribArray(0)

glGenBuffers(1, positionBufferObject)
glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject)
glBufferData(GL_ARRAY_BUFFER, len(vertexPositionsGl)*4, vertexPositionsGl, GL_STATIC_DRAW)
glBindBuffer(GL_ARRAY_BUFFER, 0)

pyglet.app.run()
5
glDrawArrays(GL_POINTS, 0, 1)

在你的教程中,指示绘制1个点,但实际上1应该是3:

glDrawArrays(GL_POINTS, 0, 3)

还要注意,你的顶点的第4个(w)部分应该是1,而不是0:

  vertexPositions = [0.0, 0.0, 0.0, 1.0,
                     0.25, 0.0, 0.0, 1.0,
                     1.75, 1.75, 0.0, 1.0]

另外,你可以去掉w这个部分,

  vertexPositions = [0.0, 0.0, 0.0,
                     0.25, 0.0, 0.0,
                     1.75, 1.75, 0.0]

然后把下面的调用改成:

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

还有一件事,我不是pyglet方面的专家,但可能glBufferData和它在C语言中的对应函数一样,接受的是字节为单位的大小,而不是元素的数量。每个浮点数占4个字节,你可以试试:

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

撰写回答