尝试在OpenGL中更新500多个顶点位置会导致读取访问冲突?

时间:2017-04-11 06:13:08

标签: python opengl pyopengl

我试图绘制大量顶点(类似于点云)。

与此同时,我也在尝试学习如何使用VBO并将Numpy数组传递给GLSL程序并为顶点位置设置动画。

我认为我已经取得了一些进展但是我受限于我可以设置array_size参数的大小,而超过500的任何内容通常会导致错误:WindowsError: exception: access violation reading 0x0999B000

有时它(随机)工作,我只是想知道我在分配内存或缓冲数组方面是否犯了错误?

只是添加一个注释,将来我希望一次更新2500多个顶点位置。并且想知道我怎么能做到这一点?

array_size = 100

#!/bin/env python
# coding: utf-8

import time
import numpy as np
from textwrap import dedent

from OpenGL.GL import *
from OpenGL.GL.shaders import compileShader, compileProgram

import pygame
from pygame.locals import *

##############################################################################
# OpenGL funcs
##############################################################################
buffers=None
shader = None
def init_gl():
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE) #allow the program to specify the point size

    global shader, buffers

    vertex_shader = compileShader(dedent('''

        uniform mat4 Projection = mat4(1);
        uniform mat4 ModelView = mat4(1);

        varying out vec3 _color;

        void main() {
            _color = gl_Color;
            gl_Position =  Projection * ModelView * gl_ModelViewProjectionMatrix * gl_Vertex;

            vec3 ndc = gl_Position.xyz / gl_Position.w ; // perspective divide.
            float zDist = 1.0-ndc.z ; // 1 is close (right up in your face,)
            // 0 is far (at the far plane)
            gl_PointSize = 25*zDist ; // between 0 and 50 now.

        }
        '''), GL_VERTEX_SHADER)
    fragment_shader = compileShader(dedent('''

        in vec3 _color;

        void main() {
            gl_FragColor = vec4(_color, 1.0); //gl_Color;
        }
        '''), GL_FRAGMENT_SHADER)
    shader = compileProgram(vertex_shader, fragment_shader)

    buffers=create_vbo()

yaw=0
pitch=0
def draw():
    global yaw, pitch
    glClear(GL_COLOR_BUFFER_BIT)# | GL_DEPTH_BUFFER_BIT)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    yaw+=0.39
    pitch+=0.27
    glTranslatef(0.0, 0.0, 0.0)
    glRotatef(yaw, 0, 1, 0)
    glRotatef(pitch, 1, 0, 0)

    setPoints()
    glFlush()

##############################################################################
# vertices
##############################################################################
array_size = 1000
scale = 0.15

#create dataset https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html
theta = np.linspace(-4 * np.pi, 4 * np.pi, array_size)
z = np.linspace(-2, 2, array_size)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)

vertices = np.dstack((x,y,z)) * scale
colors = np.tile(np.array([0.0, 1.0, 0.0]), (array_size,1)) #a bunch of green vertices
indices=np.arange(array_size)


def create_vbo():
    buffers = glGenBuffers(3)

    glBindBuffer(GL_ARRAY_BUFFER, buffers[0])
    glBufferData(GL_ARRAY_BUFFER,
            vertices.nbytes,  # byte size
            (ctypes.c_float*len(vertices.flat))(*vertices.flat),
            GL_STREAM_DRAW)

    glBindBuffer(GL_ARRAY_BUFFER, buffers[1])
    glBufferData(GL_ARRAY_BUFFER,
            colors.nbytes, # byte size
            (ctypes.c_float*len(colors.flat))(*colors.flat),
            GL_STATIC_DRAW)

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2])
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
            indices.nbytes, # byte size
            (ctypes.c_uint*len(indices.flat))(*indices.flat),
            GL_STATIC_DRAW)
    return buffers

def draw_vbo():
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
    glVertexPointer(3, GL_FLOAT, 0, None);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
    glColorPointer(3, GL_FLOAT, 0, None);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
    glDrawElements(GL_POINTS, indices.size, GL_UNSIGNED_INT, None);

    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY);


def setPoints():
    global shader

    glUseProgram(shader)
    draw_vbo()

    projection = np.array([#the matrix generated captured while using HTC Vive
        [ 0.75752085,  0.        ,  0.        ,  0.],
        [ 0.        ,  0.68160856,  0.        ,  0.],
        [ 0.05516453, -0.00299519, -1.00040019, -1.],
        [ 0.        ,  0.        , -0.20008004,  0.]
    ])
    modelview = np.array([#the matrix generated captured while using HTC Vive
        [ 0.99030989,  0.04490654,  0.13141415,  0.],
        [-0.01430531,  0.9742285 , -0.22510922,  0.],
        [-0.13813627,  0.22104797,  0.9654305 ,  0.],
        [-0.12975544, -0.9294402 , -1.06236947,  1.]
    ])

    glUniformMatrix4fv(glGetUniformLocation(shader, "Projection"), 1, False, projection)
    glUniformMatrix4fv(glGetUniformLocation(shader, "ModelView"), 1, False, modelview)

    glUseProgram(0)

##############################################################################
if __name__ == '__main__':

    pygame.init()
    pygame.display.set_mode((800, 600), HWSURFACE|OPENGL|DOUBLEBUF)

    init_gl()

    start_time = time.time()
    while time.time() - start_time < 5: #5 second animation
        draw()
        pygame.display.flip()

[UPDATE]

我相信当我意识到我没有正确地投射Numpy数组的数据类型时,我已回答了我自己的问题。因此,当我创建它们时,通过向我的数组添加.astype(),我已经设法获得2000+顶点以显示和动画:)

vertices = (np.dstack((x,y,z)) * scale).astype(np.float32)
colors = (np.tile(np.array([0.0, 1.0, 0.0]), (array_size,1))).astype(np.float32) #a bunch of green vertices
indices = np.arange(array_size).astype(np.uint32)

这是固定的例子:

#!/bin/env python
# coding: utf-8

import time
import numpy as np
from textwrap import dedent

from OpenGL.GL import *
from OpenGL.GL.shaders import compileShader, compileProgram

import pygame
from pygame.locals import *

##############################################################################
# OpenGL funcs
##############################################################################
buffers=None
shader = None
def init_gl():
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE) #allow the program to specify the point size

    global shader, buffers

    vertex_shader = compileShader(dedent('''

        uniform mat4 Projection = mat4(1);
        uniform mat4 ModelView = mat4(1);

        varying out vec3 _color;

        void main() {
            _color = gl_Color;
            gl_Position =  Projection * ModelView * gl_ModelViewProjectionMatrix * gl_Vertex;

            vec3 ndc = gl_Position.xyz / gl_Position.w ; // perspective divide.
            float zDist = 1.0-ndc.z ; // 1 is close (right up in your face,)
            // 0 is far (at the far plane)
            gl_PointSize = 25*zDist ; // between 0 and 50 now.

        }
        '''), GL_VERTEX_SHADER)
    fragment_shader = compileShader(dedent('''

        in vec3 _color;

        void main() {
            gl_FragColor = vec4(_color, 1.0); //gl_Color;
        }
        '''), GL_FRAGMENT_SHADER)
    shader = compileProgram(vertex_shader, fragment_shader)

    buffers=create_vbo()

yaw=0
pitch=0
def draw():
    global yaw, pitch
    glClear(GL_COLOR_BUFFER_BIT)# | GL_DEPTH_BUFFER_BIT)

    glMatrixMode(GL_MODELVIEW)
    glLoadIdentity()

    yaw+=0.39
    pitch+=0.27
    glTranslatef(0.0, 0.0, 0.0)
    glRotatef(yaw, 0, 1, 0)
    glRotatef(pitch, 1, 0, 0)

    setPoints()
    glFlush()

##############################################################################
# vertices
##############################################################################
array_size = 2000
scale = 0.15

#create dataset https://matplotlib.org/mpl_toolkits/mplot3d/tutorial.html
theta = np.linspace(-4 * np.pi, 4 * np.pi, array_size)
z = np.linspace(-2, 2, array_size)
r = z**2 + 1
x = r * np.sin(theta)
y = r * np.cos(theta)

vertices = (np.dstack((x,y,z)) * scale).astype(np.float32)
colors = (np.tile(np.array([0.0, 1.0, 0.0]), (array_size,1))).astype(np.float32) #a bunch of green vertices
indices = np.arange(array_size).astype(np.uint)


def create_vbo():
    buffers = glGenBuffers(3)

    glBindBuffer(GL_ARRAY_BUFFER, buffers[0])
    glBufferData(GL_ARRAY_BUFFER,
            vertices.nbytes,  # byte size
            (ctypes.c_float*len(vertices.flat))(*vertices.flat),
            GL_STREAM_DRAW)

    glBindBuffer(GL_ARRAY_BUFFER, buffers[1])
    glBufferData(GL_ARRAY_BUFFER,
            colors.nbytes, # byte size
            (ctypes.c_float*len(colors.flat))(*colors.flat),
            GL_STATIC_DRAW)

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2])
    glBufferData(GL_ELEMENT_ARRAY_BUFFER,
            indices.nbytes, # byte size
            (ctypes.c_uint*len(indices.flat))(*indices.flat),
            GL_STATIC_DRAW)
    return buffers

def draw_vbo():
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[0]);
    glVertexPointer(3, GL_FLOAT, 0, None);

    glBindBuffer(GL_ARRAY_BUFFER, buffers[1]);
    glColorPointer(3, GL_FLOAT, 0, None);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[2]);
    glDrawElements(GL_POINTS, indices.size, GL_UNSIGNED_INT, None);

    glDisableClientState(GL_COLOR_ARRAY)
    glDisableClientState(GL_VERTEX_ARRAY);


def setPoints():
    global shader

    glUseProgram(shader)
    draw_vbo()

    projection = np.array([#the matrix generated captured while using HTC Vive
        [ 0.75752085,  0.        ,  0.        ,  0.],
        [ 0.        ,  0.68160856,  0.        ,  0.],
        [ 0.05516453, -0.00299519, -1.00040019, -1.],
        [ 0.        ,  0.        , -0.20008004,  0.]
    ])
    modelview = np.array([#the matrix generated captured while using HTC Vive
        [ 0.99030989,  0.04490654,  0.13141415,  0.],
        [-0.01430531,  0.9742285 , -0.22510922,  0.],
        [-0.13813627,  0.22104797,  0.9654305 ,  0.],
        [-0.12975544, -0.9294402 , -1.06236947,  1.]
    ])

    glUniformMatrix4fv(glGetUniformLocation(shader, "Projection"), 1, False, projection)
    glUniformMatrix4fv(glGetUniformLocation(shader, "ModelView"), 1, False, modelview)

    glUseProgram(0)

##############################################################################
if __name__ == '__main__':

    pygame.init()
    pygame.display.set_mode((800, 600), HWSURFACE|OPENGL|DOUBLEBUF)

    init_gl()

    start_time = time.time()
    while time.time() - start_time < 5: #5 second animation
        draw()
        pygame.display.flip()

1 个答案:

答案 0 :(得分:2)

问题在于,您总体上没有指定dtype,这导致float64被选为dtype

print(vertices.dtype)
print(colors.dtype)
print(indices.dtype)

应打印"float32", "float32", "uint32"。但它会打印"float64", "float64", "int32"

问题在于现在您的数据是两倍大小。 因此,当您致电glBufferData()时,您告诉size data access violation reading 0x0999B000的实际尺寸是实际尺寸的两倍(与您实际给出的相比)它)。

这就是触发vertices = vertices.astype(np.float32) colors = colors.astype(np.float32) indices = indices.astype(np.uint32) 的原因,因为您的驱动程序正在尝试读取您提供的数据范围之外的数据。

最简单的修复方法是在设置顶点后执行以下操作:

glBufferData()

现在,您可以添加与RAM和VRAM一样多的顶点!

最后为了避免将来头痛,请在使用vertices致电assert vertices.dtype == np.float32 之前添加以下内容:

colors

indicesnp.uint32同样执行此操作(请改用int32)。

如果数据类型不是所需的数据类型,则会触发断言。

对于小于2 31 -1的指数,无论您使用uint32还是{{1}}都不会进行更改。然而,采用预期的类型可能是明智的,从而避免在将来意外地绊倒自己。 即使您可能无法达到该数量的指数或任何其他人。但比抱歉更安全。