基础的OpenGL、顶点缓冲区和Pyglet

7

编辑:rotoglup在我的代码中找到了问题,添加我已删除的着色器完成了解决方案。请参阅下面的答案获取正确的代码(包括着色器)。

大家好!

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

我想使用Python/pyglet而不是C++来完成。我知道pyglet可以抽象掉很多低级别的OpenGL;但是,在将它们隐藏在抽象层之后,我想先了解一些基础知识。

我的问题非常简单:下面的代码只绘制一个点,而不是我期望的3个点。

就我所知,我的代码与教程中的C++代码相同,除了删除了顶点和片段着色器(通过Python中的gletools完成),但似乎对我的问题没有影响。

将事情简化为一个点显示出我不理解的行为(第一个坐标似乎是唯一影响任何东西的),使我相信我简单地未能理解关于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()

你的意思是说,使用更正后的代码后,仍然只有一个点被绘制了吗?这个点是在屏幕中央绘制的吗?如果你改变第一个坐标,这个点会移动吗? - rotoglup
通过更正后的代码,我最好得到的仍然是一个单点,它不随w坐标在1处和字节大小传递而移动。如果我删除w坐标并相应地更改glVertexAttribPointer,我什么也得不到。我非常困惑! - Nicolas Lefebvre
更正:w 必须为 0 才能显示出任何东西,这让我更加困惑。 - Nicolas Lefebvre
这可能与一些你不知道的剪切有关。由于您没有设置任何投影或变换矩阵,因此您的第三个点肯定在屏幕外。由于Z值的原因,其他点也可能被剪切。我不记得默认的OpenGL投影了。您应该尝试调用glOrtho来安装投影。您还应该尝试绘制一个三角形,如果某些点在无限远处或屏幕外,它将使图形更加明显。 - rotoglup
2个回答

5
glDrawArrays(GL_POINTS, 0, 1)

在您的教程中,指示绘制1个点,但实际上1代表3。

glDrawArrays(GL_POINTS, 0, 3)

请注意,您的顶点中第四个(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)

谢谢您的回答!我已经按照您的建议编辑了我的问题(实际上我尝试了正确版本的drawArray调用,但在复制代码之前忘记还原它)。然而问题仍然存在...我开始怀疑我的pyglet版本是否有问题,我不得不安装开发版本才能使用gletools。 - Nicolas Lefebvre
谢谢(进一步)回答!不幸的是仍然没有成功……我开始怀疑这是与OpenGL无关而是与pyglet有关的问题。我将尝试在pyglet邮件列表中寻求帮助并回报结果! - Nicolas Lefebvre
那可能是一个解决方案。如果你们都是3D、OpenGL等方面的新手,你们也应该尝试跟随一个更简单的教程,不使用缓冲区对象,而是使用普通的glVertex调用。这样做可以通过消除Python浮点数转换为字节的问题来使事情更加明显。你可以尝试NeHe的教程。 - rotoglup
再次感谢您,我终于让它正常运行并将修正后的代码添加为答案。我错误地认为现在可以跳过着色器,但实际上它们是必要的,只是由于您在我的代码中纠正的问题导致它们无法正常工作!我意识到从缓冲对象和着色器开始有点冒险,但我想了解“古老”的OpenGL(已经阅读了一些NeHe教程)和更近期的做法之间的区别 :) - Nicolas Lefebvre

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()

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