简体   繁体   English

PyOpenGL如何将VAO重新绑定到另一个VBO以便快速切换需要绘制的数据

[英]PyOpenGL how to rebind VAO to another VBO in order to quickly switch data that needs to be drawn

I am using PyQt5 QOpenGLWidget to simply draw some points on the screen with different colors.我正在使用 PyQt5 QOpenGLWidget 用不同的 colors 在屏幕上简单地绘制一些点。 I prepared two datasets, each containing their coordinates and their colors, 4 arrays on total, and I want the program to paint one of the datasets for each 100ms (by using a QTimer).我准备了两个数据集,每个数据集都包含它们的坐标和它们的 colors,总共 4 个 arrays,我希望程序每 100 毫秒绘制一个数据集(通过使用 QTimer)。 I put these 4 arrays into 4 VBOs, and for each paintGL() call I want to switch from a dataset to another, that is, if this frame uses VBO0(coordinate), VBO1(color), then next frame will use VBO2(coordinate), VBO3(color), and vice versa.我将这 4 个 arrays 放入 4 个 VBO 中,对于每个 paintGL() 调用我想从一个数据集切换到另一个,也就是说,如果这一帧使用 VBO0(坐标),VBO1(颜色),那么下一帧将使用 VBO2(坐标),VBO3(颜色),反之亦然。

My idea is to use a VAO, and since VBO0/1 has exactly same structure with VBO2/3, for each new frame I can just rebind the VAO to another VBO.我的想法是使用 VAO,并且由于 VBO0/1 与 VBO2/3 具有完全相同的结构,因此对于每个新帧,我可以将 VAO 重新绑定到另一个 VBO。 However this does not work as expected, so what is the correct way of achieving my goal?但是,这并没有按预期工作,那么实现我的目标的正确方法是什么?

Minimum Example:最小示例:

import numpy as np
from PyQt5.QtWidgets import QOpenGLWidget, QApplication, QWidget, QHBoxLayout
from PyQt5.QtCore import QTimer

from OpenGL.GL import *


def compileShader(vsSource, fsSource):

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

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

    program = glCreateProgram()
    glAttachShader(program, vertexShader)
    glAttachShader(program, fragmentShader)
    glLinkProgram(program)

    glDeleteShader(vertexShader)
    glDeleteShader(fragmentShader)

    return program


class MyOpenGLWidget(QOpenGLWidget):

    def __init__(self):

        super().__init__()

   


    def initializeGL(self):

        # with open('./shaders/vertexShader.shader', 'r') as f:
        #     vsSource = f.read()
        # with open('./shaders/fragmentShader.shader', 'r') as f:
        #     fsSource = f.read()

        vsSource = """
        #version 450 core
        layout (location = 0) in vec4 position;
        layout (location = 1) in vec4 color;
        
        out vec4 vs_Color;
        
        void main() {
        
            gl_Position = position;
            gl_PointSize = 5.;
            vs_Color = color;
        
        }
        """

        fsSource = """
        #version 450 core
        out vec4 FragColor;

        in vec4 vs_Color;
        
        void main() {
        
            FragColor = vs_Color;
        
        }
        """

        self.program = compileShader(vsSource, fsSource)
        self.initBuffers()
        self.timer = QTimer(self)
        self.timer.timeout.connect(self.onTimeout)
        self.timer.start(100)
        self.dataset = 1


    def paintGL(self):

        bgColor = np.array([0, 0, 0, 1], dtype=np.float32)
        glClearBufferfv(GL_COLOR, 0, bgColor)
        glUseProgram(self.program)
        glEnable(GL_PROGRAM_POINT_SIZE)
        glDrawArrays(GL_POINTS, 0, self.n)


    def generateRandomData(self, n, mode='vertex'):

        if mode == 'vertex':
            dx = np.random.rand(n) * 2 - 1
            dy = np.random.rand(n) * 2 - 1
            zeros = np.zeros(n)
            ones = np.ones(n)
            data = np.vstack([dx, dy, zeros, ones]).T.astype(np.float32)
            return data.flatten()

        elif mode == 'color':
            r = np.random.rand(n)
            g = np.random.rand(n)
            b = np.random.rand(n)
            ones = np.ones(n)
            data = np.vstack([r, g, b, ones]).T.astype(np.float32)
            return data.flatten()


    def initBuffers(self):

        self.n = 100
        self.vertexData1 = self.generateRandomData(self.n, mode='vertex')
        self.colorData1 = self.generateRandomData(self.n, mode='color')
        self.vertexData2 = self.generateRandomData(self.n, mode='vertex')
        self.colorData2 = self.generateRandomData(self.n, mode='color')

        self.buffers = np.empty(4, dtype=np.uint32)
        glCreateBuffers(4, self.buffers)

        for buffer in self.buffers:
            glBindBuffer(GL_ARRAY_BUFFER, buffer)
            glNamedBufferStorage(buffer, self.vertexData1.nbytes, None,
                                 GL_DYNAMIC_STORAGE_BIT)

        glNamedBufferSubData(self.buffers[0], 0, self.vertexData1.nbytes, self.vertexData1)
        glNamedBufferSubData(self.buffers[1], 0, self.colorData1.nbytes, self.colorData1)
        glNamedBufferSubData(self.buffers[2], 0, self.vertexData2.nbytes, self.vertexData2)
        glNamedBufferSubData(self.buffers[3], 0, self.colorData2.nbytes, self.colorData2)

        self.VAO = GLuint(0)
        glCreateVertexArrays(1, self.VAO)

        glEnableVertexArrayAttrib(self.VAO, 0)
        glEnableVertexArrayAttrib(self.VAO, 1)
        glVertexArrayAttribFormat(self.VAO, 0, 4, GL_FLOAT, GL_FALSE, 0)
        glVertexArrayAttribFormat(self.VAO, 1, 4, GL_FLOAT, GL_FALSE, 0)
        glVertexArrayAttribBinding(self.VAO, 0, 0)
        glVertexArrayAttribBinding(self.VAO, 1, 1)

        glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[0], 0, 4 * 4)
        glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[1], 0, 4 * 4)


        glBindVertexArray(self.VAO)


    def onTimeout(self):

        if self.dataset == 1:
            glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[2], 0, 4 * 4)
            glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[3], 0, 4 * 4)
            self.dataset = 2
        elif self.dataset == 2:
            glVertexArrayVertexBuffer(self.VAO, 0, self.buffers[0], 0, 4 * 4)
            glVertexArrayVertexBuffer(self.VAO, 1, self.buffers[1], 0, 4 * 4)
            self.dataset = 1
        self.update()


app = QApplication([])
win = QWidget()
win.showMaximized()
layout = QHBoxLayout()
win.setLayout(layout)
layout.addWidget(MyOpenGLWidget())
win.show()
app.exec_()

Expected Output:预期 Output:

Two datsets switches on the screen every 100ms (as set in QTimer) like:每 100 毫秒(在 QTimer 中设置)在屏幕上切换两个数据集,例如: 在此处输入图像描述

Real Output:真正的 Output:

First frame is correct, but after first switching there is a white triangle on the screen instead of points, and there's no further effect.第一帧是正确的,但是第一次切换后屏幕上出现了一个白色的三角形而不是点,并且没有进一步的效果。 在此处输入图像描述

Rebind your vertex array object (self.VAO) each time in paintGL rather than just once in initBuffers .每次在paintGL中重新绑定您的顶点数组 object (self.VAO) 而不仅仅是在initBuffers中。 The official doco is not very clear, but these earlier answers suggest that changing attribs on a VAO probably won't take effect on the current binding.官方文档不是很清楚,但这些较早的答案表明更改 VAO 上的属性可能不会对当前绑定生效。

When is What bound to a VAO? 什么时候绑定到 VAO?

Changing vertex array object 更改顶点数组 object

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM