运行 使用 opengl 函数 glBeginTransformFeedback 进入路障

running into roadblock with opengl function glBeginTransformFeedback

所以我在使用 opengl 函数时遇到了问题 glBeginTransformFeedback 当我取消对这些行的注释时,发生了错误。它说 1282 invalid operation 虽然很含糊。

我的问题是应该如何处理源代码以取消注释这三行:

#glBeginTransformFeedback(GL_POINTS)
#glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
#glEndTransformFeedback()

一个更广泛的问题是关于如何让它正确呈现的任何建议?

更新:在 Rabbid76 优秀代码的帮助下,程序现在可以运行了!!!非常感谢!

成功!!!!当前输出与预期输出相同:

支持文件:springmass_support.zip

源代码:

#!/usr/bin/python3

import sys
import time
import ctypes

fullscreen = True

sys.path.append("./shared")

from sbmloader import SBMObject    # location of sbm file format loader

from sbmath import m3dDegToRad, m3dRadToDeg, m3dTranslateMatrix44, m3dRotationMatrix44, m3dMultiply, m3dOrtho, m3dPerspective, rotation_matrix, translate, m3dScaleMatrix44, \
    scale, m3dLookAt, normalize

try:
    from OpenGL.GLUT import *
    from OpenGL.GL import *
    from OpenGL.GLU import *
    from OpenGL.raw.GL.ARB.vertex_array_object import glGenVertexArrays, glBindVertexArray
except:
    print ('''
    ERROR: PyOpenGL not installed properly.
        ''')
    sys.exit()

from array import array
from enum import Enum

import numpy as np 

import glm


from math import cos, sin 
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]

render_program = GLuint(0)

myobject = SBMObject()

POSITION_A = 0
POSITION_B = 1
VELOCITY_A = 2
VELOCITY_B = 3
CONNECTION = 4

POINTS_X            = 50
POINTS_Y            = 50
POINTS_TOTAL        = POINTS_X * POINTS_Y
CONNECTIONS_TOTAL = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X


m_vao = [GLuint(0) for _ in range(2)]
m_vbo = [GLuint(0) for _ in range(5)]

m_index_buffer = GLuint(0)
m_pos_tbo = [GLuint(0), GLuint(0)]
m_update_program = GLuint(0)
m_render_program = GLuint(0)
m_C_loc = GLuint(0)
m_iteration_index = 0

draw_points = True
draw_lines = True
iterations_per_frame = 16


def shader_load(filename, shader_type):

    result = GLuint(0)

    with open ( filename, "rb") as data:

        result = glCreateShader(shader_type)

        glShaderSource(result, data.read() )

    glCompileShader(result)

    return result


def load_shaders():
    global m_update_program
    global m_render_program

    vs = GLuint(0)
    fs = GLuint(0)
    buffer = ''

    vs = shader_load("update.vs.glsl", GL_VERTEX_SHADER)

    if (m_update_program):
        glDeleteProgram(m_update_program)

    m_update_program = glCreateProgram()
    glAttachShader(m_update_program, vs)


    # static const char * tf_varyings[] = 
    # {
        # "tf_position_mass",
        # "tf_velocity"
    # }


    # tricky to convert string array to string pointer

    tf_varyings = ["tf_position_mass", "tf_velocity"]

    # Prepare ctypes data containing the list tf_varyings of strings
    array_type = ctypes.c_char_p * len(tf_varyings)
    buff = array_type()
    for i, e in enumerate(tf_varyings):
        buff[i] = e.encode()
                                                                            #       ctypes.c_char
    tf_varyings_chrpp = ctypes.cast(ctypes.pointer(buff), ctypes.POINTER(ctypes.POINTER(GLchar)))


    glTransformFeedbackVaryings(m_update_program, 2, tf_varyings_chrpp, GL_SEPARATE_ATTRIBS)

    glLinkProgram(m_update_program)

    glGetShaderInfoLog(vs)
    glGetProgramInfoLog(m_update_program)

    glDeleteShader(vs)

    vs = shader_load("render.vs.glsl", GL_VERTEX_SHADER)
    fs = shader_load("render.fs.glsl", GL_FRAGMENT_SHADER)

    if (m_render_program):
        glDeleteProgram(m_render_program)
    m_render_program = glCreateProgram()
    glAttachShader(m_render_program, vs)
    glAttachShader(m_render_program, fs)

    glLinkProgram(m_render_program)



class Scene:

    def __init__(self, width, height):
        global m_vao
        global m_vbo

        i = 0
        j = 0

        load_shaders()


        initial_positions = [glm.vec4() for _ in range(POINTS_TOTAL)]
        initial_velocities = [glm.vec3() for _ in range(POINTS_TOTAL)]
        connection_vectors = [glm.ivec3() for _ in range(POINTS_TOTAL)]


        n=0
        for j in range(0, POINTS_Y):
            fj = float(j) / float(POINTS_Y)

            for i in range(0, POINTS_X):

                fi = float(i) / float(POINTS_X)

                initial_positions[n] = glm.vec4((fi - 0.5) * float(POINTS_X), (fj - 0.5) * float(POINTS_Y), 0.6 * sin(fi) * cos(fj), 1.0)
                initial_velocities[n] = glm.vec3(0.0)
                connection_vectors[n] = glm.ivec4(-1)

                if (j != (POINTS_Y - 1)):

                    if (i != 0):
                        connection_vectors[n][0] = n - 1

                    if (j != 0):
                        connection_vectors[n][1] = n - POINTS_X

                    if (i != (POINTS_X - 1)):
                        connection_vectors[n][2] = n + 1

                    if (j != (POINTS_Y - 1)):
                        connection_vectors[n][3] = n + POINTS_X
                n+=1


        for i in range(0, 2):
            glGenVertexArrays(1, m_vao[i])

        for i in range(0, 5):
            glGenBuffers(i+1, m_vbo[i])

        for i in range(0, 2):

            glBindVertexArray(m_vao[i])

            glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])


            # POSITION_A
            glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])

            ar_position = np.empty([POINTS_TOTAL, 4], dtype='float32')
            for j, e in enumerate(initial_positions):
                ar_position[j] = e

            glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4()), ar_position, GL_DYNAMIC_COPY)
            glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
            glEnableVertexAttribArray(0)

            # VELOCITY_A
            glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i])

            ar_velocities = np.empty([POINTS_TOTAL, 3], dtype='float32')
            for j, e in enumerate(initial_velocities):
                ar_velocities[j] = e

            glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec3()), ar_velocities, GL_DYNAMIC_COPY)
            glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
            glEnableVertexAttribArray(1)

            # CONNECTION
            glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION])

            ar_connection = np.empty([POINTS_TOTAL, 4], dtype='uint32')
            for j, e in enumerate(connection_vectors):
                ar_connection[j] = e

            glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.ivec4()), ar_connection, GL_STATIC_DRAW)
            glVertexAttribIPointer(2, 4, GL_INT, 0, None)
            glEnableVertexAttribArray(2)

        glGenTextures(2, m_pos_tbo)
        glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[0])
        glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_A])
        glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[1])
        glTexBuffer(GL_TEXTURE_BUFFER, GL_RGBA32F, m_vbo[POSITION_B])

        lines = (POINTS_X - 1) * POINTS_Y + (POINTS_Y - 1) * POINTS_X

        glGenBuffers(1, m_index_buffer)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * ctypes.sizeof(ctypes.c_int), None, GL_STATIC_DRAW)

        e = glMapBufferRange(GL_ELEMENT_ARRAY_BUFFER, 0, lines * 2 * ctypes.sizeof(ctypes.c_int), GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)

        int_array = (ctypes.c_int * (4 * lines * 2)).from_address(e) 
        n = 0
        for j in range(0, POINTS_Y):
            for i in range(0, POINTS_X - 1):
                int_array[n] = i + j * POINTS_X
                n+=1

                int_array[n] = 1 + i + j * POINTS_X
                n+=1

        for i in range(0, POINTS_X):

            for j in range(0, POINTS_Y - 1):
                int_array[n] = i + j * POINTS_X
                n+=1

                int_array[n] = POINTS_X + i + j * POINTS_X
                n+=1


        glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER)


    def display(self):
        global m_iteration_index

        glUseProgram(m_update_program)

        glEnable(GL_RASTERIZER_DISCARD)


        for i in range( iterations_per_frame, 0, -1):

            glBindVertexArray(m_vao[m_iteration_index & 1])
            glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[m_iteration_index & 1])
            m_iteration_index +=1
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_vbo[POSITION_A + (m_iteration_index & 1)])
            glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, m_vbo[VELOCITY_A + (m_iteration_index & 1)])

            glBeginTransformFeedback(GL_POINTS)
            glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
            glEndTransformFeedback()

        glDisable(GL_RASTERIZER_DISCARD)

        black = [ 0.0, 0.0, 0.0, 0.0 ]

        glViewport(0, 0, self.width, self.height)
        glClearBufferfv(GL_COLOR, 0, black)

        glUseProgram(m_render_program)

        if (draw_points):
            glPointSize(4.0)
            glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)

        if (draw_lines):
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_index_buffer)
            glDrawElements(GL_LINES, CONNECTIONS_TOTAL * 2, GL_UNSIGNED_INT, None)

        glutSwapBuffers()

    def reshape(self, width, height):
        self.width = width
        self.height = height

    def keyboard(self, key, x, y ):
        global fullscreen

        print ('key:' , key)
        if key == b'\x1b': # ESC
            sys.exit()

        elif key == b'f' or key == b'F': #fullscreen toggle

            if (fullscreen == True):
                glutReshapeWindow(512, 512)
                glutPositionWindow(int((1360/2)-(512/2)), int((768/2)-(512/2)))
                fullscreen = False
            else:
                glutFullScreen()
                fullscreen = True

        print('done')

    def init(self):
        pass

    def timer(self, blah):

        glutPostRedisplay()
        glutTimerFunc( int(1/60), self.timer, 0)
        time.sleep(1/60.0)


if __name__ == '__main__':
    start = time.time()

    glutInit()


    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)

    glutInitWindowSize(512, 512)

    w1 = glutCreateWindow('OpenGL SuperBible - Spring-Mass Simulator')
    glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))

    fullscreen = False
    many_cubes = False
    #glutFullScreen()

    scene = Scene(512,512)
    glutReshapeFunc(scene.reshape)
    glutDisplayFunc(scene.display)
    glutKeyboardFunc(scene.keyboard)

    glutIdleFunc(scene.display)
    #glutTimerFunc( int(1/60), scene.timer, 0)

    scene.init()

    glutMainLoop()

我从 Superbible OpenGL 7ed 的第 7 章将此程序移植到 python。

移植自:springmass.cpp

sys.getsizeof() 不是 return 对象表示的缓冲区大小,它 return 是对象的大小。

如果您想获得由 ctypes 对象管理的缓冲区的大小,那么您需要我们 ctypes.sizeof。例如:

glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * sys.getsizeof(int), None, GL_STATIC_DRAW)
glBufferData(GL_ELEMENT_ARRAY_BUFFER, lines * 2 * ctypes.sizeof(ctypes.c_int), None, GL_STATIC_DRAW)

PyGLM 也提供了 glm.sizeof() 功能。例如:

glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * sys.getsizeof(glm.vec4()), ar, GL_DYNAMIC_COPY)
glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4), ar, GL_DYNAMIC_COPY)

NumPy provides the .itmesize属性。例如:

np.dtype('float32').itemsize

一个list对象,一行代码即可生成:

initial_positions = [glm.vec4() for _ in range(POINTS_TOTAL)]
initial_velocities = [glm.vec3() for _ in range(POINTS_TOTAL)]
connection_vectors = [glm.ivec3() for _ in range(POINTS_TOTAL)]

要从列表中生成数组,您可以通过 numpy.empty 创建一个空数组。
创建一个二维数组,形状分别为 ([POINTS_TOTAL, 4]) ([POINTS_TOTAL, 3])。 glm 矢量对象可以在循环中赋值。
请注意,积分属性的数组类型必须是 'uint32' 而不是 float32:

# POSITION_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[POSITION_A + i])

ar = np.empty([POINTS_TOTAL, 4], dtype='float32')
for j, e in enumerate(initial_positions):
    ar[j] = e

glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec4), ar, GL_DYNAMIC_COPY)
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(0)

# VELOCITY_A
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[VELOCITY_A + i])

ar = np.empty([POINTS_TOTAL, 3], dtype='float32')
for j, e in enumerate(initial_velocities):
    ar[j] = e

glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.vec3), ar, GL_DYNAMIC_COPY)
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, None)
glEnableVertexAttribArray(1)

# CONNECTION
glBindBuffer(GL_ARRAY_BUFFER, m_vbo[CONNECTION])

ar = np.empty([POINTS_TOTAL, 4], dtype='uint32')
for j, e in enumerate(connection_vectors):
    ar[j] = e

glBufferData(GL_ARRAY_BUFFER, POINTS_TOTAL * glm.sizeof(glm.ivec4), ar, GL_STATIC_DRAW)
glVertexAttribIPointer(2, 4, GL_INT, 0, None)
glEnableVertexAttribArray(2)

说明

int_array = ((ctypes.c_int * 4) * lines * 2).from_address(e) 

将生成一个 3 维数组。可以使用一次 *- 运算符生成二维数组,其中所有元素的大小(例如 (ctypes.c_int * (4 * lines * 2))):

int_array = (ctypes.c_int * (4 * lines * 2)).from_address(e) 
n = 0
for j in range(0, POINTS_Y):
    for i in range(0, POINTS_X - 1):
        int_array[n] = i + j * POINTS_X
        n+=1

        int_array[n] = 1 + i + j * POINTS_X
        n+=1

for i in range(0, POINTS_X):

    for j in range(0, POINTS_Y - 1):
        int_array[n] = i + j * POINTS_X
        n+=1

        int_array[n] = POINTS_X + i + j * POINTS_X
        n+=1

此外,当您生成顶点数组对象和缓冲区对象时还有一个问题。

为对象创建列表:

m_vao = [GLuint(0) for _ in range(2)]
m_vbo = [GLuint(0) for _ in range(5)]

for 循环中创建 1 个对象:

for i in range(0, 5):
    glGenBuffers(1, m_vbo[i])

for i in range(0, 2):
    glGenVertexArrays(1, m_vao[i])

终于可以glBeginTransformFeedback使用了:

for i in range( iterations_per_frame, 0, -1):

    glBindVertexArray(m_vao[m_iteration_index & 1])
    glBindTexture(GL_TEXTURE_BUFFER, m_pos_tbo[m_iteration_index & 1])
    m_iteration_index +=1
    glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, m_vbo[POSITION_A + (m_iteration_index & 1)])
    glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, m_vbo[VELOCITY_A + (m_iteration_index & 1)])

    glBeginTransformFeedback(GL_POINTS)
    glDrawArrays(GL_POINTS, 0, POINTS_TOTAL)
    glEndTransformFeedback()

我更改了iterations_per_frame = 1并跳过了​​glutIdleFunc,但激活了glutIdleFunc

class Scene:

   # [...]

   def timer(self, blah):

        glutPostRedisplay()
        glutTimerFunc( 50, self.timer, 0)

if __name__ == '__main__':

    # [...]

    #glutIdleFunc(scene.display)
    glutTimerFunc( 1000, scene.timer, 0) 

得到如下结果: