使用python';在OpenGL API中,如何在元素索引数组的子集上执行GLD元素?

使用python';在OpenGL API中,如何在元素索引数组的子集上执行GLD元素?,python,opengl,Python,Opengl,我正在努力熟悉python的OpenGL API,我已经到了可以对四边形进行纹理处理的地步。现在我想在不同的四边形上放置不同的纹理,所以我需要能够渲染四边形元素的子集,更改纹理,然后渲染其余的四边形 # based on http://www.pygame.org/wiki/GLSLExample import sys import pygame import OpenGL.GL as gl import OpenGL.GLU as glu import OpenGL.GLUT as g

我正在努力熟悉python的OpenGL API,我已经到了可以对四边形进行纹理处理的地步。现在我想在不同的四边形上放置不同的纹理,所以我需要能够渲染四边形元素的子集,更改纹理,然后渲染其余的四边形

# based on http://www.pygame.org/wiki/GLSLExample


import sys

import pygame

import OpenGL.GL as gl
import OpenGL.GLU as glu
import OpenGL.GLUT as glut
from OpenGL.arrays import vbo
import numpy
from math import *
from PIL import Image


def compile_shader(source, shader_type):
    shader = gl.glCreateShader(shader_type)
    #source = c_char_p(source)
    length = -1 #c_int(-1)
    gl.glShaderSource(shader, source)
    gl.glCompileShader(shader)

    status = gl.glGetShaderiv(shader, gl.GL_COMPILE_STATUS)
    if not status:
        print_log(shader)
        gl.glDeleteShader(shader)
        raise ValueError( 'Shader compilation failed' )
    return shader


def compile_program(vertex_source, fragment_source):
    vertex_shader = None
    fragment_shader = None
    program = gl.glCreateProgram()

    if vertex_source:
        vertex_shader = compile_shader(vertex_source, gl.GL_VERTEX_SHADER)
        gl.glAttachShader(program, vertex_shader)
    if fragment_source:
        fragment_shader = compile_shader(fragment_source, gl.GL_FRAGMENT_SHADER)
        gl.glAttachShader(program, fragment_shader)

    gl.glLinkProgram(program)

    if vertex_shader:
        gl.glDeleteShader(vertex_shader)
    if fragment_shader:
        gl.glDeleteShader(fragment_shader)

    return program


def load_texture(file_name):
    image  = Image.open(file_name)
    width  = image.size[0]
    height = image.size[1]
    image_bytes  = image.convert("RGBA").tobytes ( "raw", "RGBA", 0, -1)
    texture = gl.glGenTextures(1)

    gl.glBindTexture     ( gl.GL_TEXTURE_2D, texture )
    gl.glTexParameterf   ( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_REPEAT )
    gl.glTexParameterf   ( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_REPEAT )
    gl.glTexParameteri   ( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER,gl.GL_NEAREST )
    gl.glTexParameteri   ( gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER,gl.GL_LINEAR_MIPMAP_LINEAR )


    glu.gluBuild2DMipmaps ( gl.GL_TEXTURE_2D, gl.GL_RGBA, width, height, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, image_bytes )

    return texture

def perspective_matrix(fov, aspect_, near, far):
    # https://www.opengl.org/discussion_boards/showthread.php/166405-Perspective-Matrix-implementation

    f = 1/tan(fov/2)
    #print(f, fov)
    wiggy = -2 * far * near / (far - near)
    aspect = aspect_
    return numpy.matrix( [
                             [ f/aspect, 0, 0, 0],
                             [0, f, 0, 0],
                             [0, 0, (far+near)/(near-far), wiggy],
                             [0, 0, -1, 0]] , dtype=numpy.float32)


def print_log(shader):

    length = gl.glGetShaderiv(shader, gl.GL_INFO_LOG_LENGTH)

    if length > 0:

        log = gl.glGetShaderInfoLog(shader)
        print(log, file=sys.stderr)

#
#
#

def translation_matrix(x, y, z):
    return numpy.matrix([[1, 0, 0, x],
                         [0, 1, 0, y],
                         [0, 0, 1, z],
                         [0, 0, 0, 1]], numpy.float32)


def scale_matrix(scale):
    return numpy.array([[scale, 0, 0, 0],
                        [0, scale, 0, 0],
                        [0, 0, scale, 0],
                        [0, 0, 0, 1]], numpy.float32)


def norm4( mat):
    return [mat, mat / mat[0,3] ]


def rotation_matrix(radians, axis):
    len = sqrt( axis[0]*axis[0] + axis[1]*axis[1] + axis[2]*axis[2])
    x = axis[0]/len
    y = axis[1]/len
    z = axis[2]/len

    c = cos(radians)
    s = sin(radians)
    t=1-c

    # http://www.euclideanspace.com/maths/geometry/rotations/conversions/angleToMatrix/
    return numpy.matrix( [[ t*x*x + c, t*x*y - z*s, t*x*z + y*s, 0],
                          [ t*x*y + z*s, t*y*y + c, t*y*z - x*s, 0],
                          [ t*x*z - y*s, t*y*z + x*s, t*z*z + c, 0],
                          [0,0,0,1]], dtype=numpy.float32)


def attributes( handle ):
    # http://nullege.com/codes/show/src%40p%40y%40PyGLy-HEAD%40pygly%40shader.py/210/OpenGL.GL.glGetActiveAttrib/python
    """Returns an iterator for the attributes of the specified program.

    Each attribute returns a tuple.

    :rtype: (name, size, type)
    :return: A tuple consisting of 3 values:
        name is the variable name
        size is the variable size in bytes
        type is the GL enumeration
    """
    # get number of active uniforms
    num_attributes = gl.glGetProgramiv( handle, gl.GL_ACTIVE_ATTRIBUTES )

    for index in range( num_attributes ):
        yield attribute_for_index( handle, index )

def attribute_for_index( handle, index ):
    """Returns the attribute for the specified attribute index.

    :rtype: tuple(name, size, type)
    """
    # Constants like GLsizei are only found in OpenGL.constants
    # for older versions of pyopengl
    name_length = 30
    glNameSize = (gl.constants.GLsizei)()
    glSize = (gl.constants.GLint)()
    glType = (gl.constants.GLenum)()
    glName = (gl.constants.GLchar * name_length)()

    gl.glGetActiveAttrib(
        handle,
        index,
        name_length,
        glNameSize,
        glSize,
        glType,
        glName
        )

    name, size, type = glName.value, glSize.value, glType.value
    return name, size, type


def app():
    glut.glutInit(sys.argv)
    width, height = 640, 480
    pygame.init()
    pygame.display.set_mode((width, height), pygame.OPENGL | pygame.DOUBLEBUF)

    program = compile_program('''
    // Vertex program
    attribute vec2 vertex_uv;
    uniform mat4 mvp;
    varying vec3 pos;
    varying vec2 texCoord;
    void main() {
        pos = gl_Vertex.xyz;
        gl_Position = mvp * gl_Vertex;
        texCoord = vertex_uv;
    }
    ''', '''
    // Fragment program
    varying vec3 pos;
    varying vec2 texCoord;
    uniform sampler2D tex;
    void main() {
        gl_FragColor.rgb = texture(tex, texCoord);
        //gl_FragColor.r = 0.5;
    }
    ''')

    persp =perspective_matrix(pi/4, 1024/float(768), 0.01, 1000)


    mat_loc = gl.glGetUniformLocation(program, bytes("mvp", "ascii"))
    print(mat_loc)
    uv_loc = gl.glGetAttribLocation(program, bytes("vertex_uv", "ascii"))
    print(uv_loc)
    tmp = gl.glGetAttribLocation(program, bytes("gl_Vertex", "ascii"))
    print([ "gl_Vertex", tmp] )

    tex_loc = gl.glGetUniformLocation(program, bytes("tex", "ascii"))
    print(["tex uniform location", tex_loc])

    if False:
        name_length = 30
        glNameSize = (gl.constants.GLsizei)()
        glSize = (gl.constants.GLint)()
        glType = (gl.constants.GLenum)()
        glName = (gl.constants.GLchar * name_length)()
        gl.glGetActiveAttrib(program,1,
                             name_length,
                             glNameSize,
                             glSize,
                             glType,
                             glName)
        print(glName.value)

        for name,y,z in attributes(program):
            print([name,y,z, gl.glGetAttribLocation(program, name)])


    gl.glEnable(gl.GL_DEPTH_TEST)


    vertices = [-1, -1, -1,
                1, -1, -1,
                1, 1, -1,
                -1, 1, -1,

                -1, -1, 1,
                1, -1, 1,
                1, 1, 1,
                -1, 1, 1]
    uvs = [0,0,
           1,0,
           1,1,
           0,1,

           0,0,
           1,0,
           1,1,
           0,1,

           ]
    indices = [0,1,2,3,
               4,5,6,7,
#               8,9,10,11,
#               12,13,14,15,
#               16,17,18,19,
#               20,21,22,23
    ]

    vert_buffer = vbo.VBO(numpy.array(vertices, dtype=numpy.float32))
    uv_buffer = vbo.VBO(numpy.array(uvs, dtype=numpy.float32))
    index_buffer = vbo.VBO(numpy.array(indices, dtype=numpy.uint16), target=gl.GL_ELEMENT_ARRAY_BUFFER)

    #

    texture1 = load_texture("bear64.png")
    texture2 = load_texture("flamingo64.gif")

    #

    quit = False
    angle = 0

    gl.glUseProgram(program)

    # bind all our buffers out here because this scene is so static
    index_buffer.bind()
    vert_buffer.bind()
    gl.glEnableVertexAttribArray(0);
    gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, False, 0, None)

    if uv_loc>=0:
        uv_buffer.bind()
        gl.glEnableVertexAttribArray(uv_loc)
        gl.glVertexAttribPointer(uv_loc, 2, gl.GL_FLOAT, False, 0, None)

    import time
    while not quit:
        t0 = time.time()
        for e in pygame.event.get():

            if e.type == pygame.QUIT:
                quit=True
            elif e.type == pygame.KEYDOWN:
                print(e.key)
                if e.key == pygame.K_q:
                    quit = True


        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        z = (t0/10)%1

        mvp =  persp*translation_matrix(0,0,-5)* scale_matrix(1) * rotation_matrix(pi*0.4, [-1,0,0]) *rotation_matrix(z*pi*2, [0,0,1])
        #print( mvp.dot([0,0,0,1]) )
        mvp_ = numpy.asfortranarray(mvp, dtype=numpy.float32)
        gl.glUniformMatrix4fv(mat_loc, 1, False, mvp_)
        gl.glBindTexture(gl.GL_TEXTURE_2D, texture1)
        gl.glUniform1i( tex_loc, 0)


        gl.glDrawElements(gl.GL_QUADS, 4, gl.GL_UNSIGNED_SHORT, None)

        gl.glBindTexture(gl.GL_TEXTURE_2D, texture2)
        #gl.glDrawElements(gl.GL_QUADS, len(indices)-4, gl.GL_UNSIGNED_SHORT, 4*2)


        pygame.display.flip()


if __name__ == '__main__':
    app()
让我为这个例子的巨大而道歉,但OpenGL并不是紧凑型的

我的问题是:如何调用
gl.gldrawerelements
,以便它从
index\u缓冲区
(它是从
索引
构建的)中绘制剩余的四边形?我所看到的所有示例在指针字段中都没有使用。这与webGL不同,在webGL中,您可以传递
0
以从元素的开头开始,或者传递
i*2
以从
i
th索引开始


要运行该示例,您需要一个bear64.png和flamingo64.gif。任何旧图像都可以使用,但您可以尝试。

来自OpenGL 4参考(glBindBuffer):

而非零缓冲区对象绑定到 GL\u元素\u数组\u缓冲区目标,的索引参数 GLDrawerElements、GLDrawerElementsInstanced、GLDrawerElementsBaseVertex、, glDrawRangeElements、glDrawRangeElementsBaseVertex、, GLMultiDrawerements或GLMultiDrawerementsBaseVertex被解释 作为在基本机器中测量的缓冲区对象内的偏移量 单位

因此,应在“基本机器单位”中输入偏移量。它实际上是开始索引*索引类型大小。在您的例子中,索引类型是SHORT,SHORT size是2。诀窍是必须将值转换为C void指针,如下所示:

gl.glDrawElements(gl.GL_QUADS, len(indices)- start,
                  gl.GL_UNSIGNED_SHORT, ctypes.c_void_p(start*2)).

我正在奔向不同的活动,但这可能是你的答案。我们明天会更仔细地调查。祝你好运。在尝试了这个调用之后,我认为它的开始和结束参数绑定了索引数组引用的顶点索引(意思是
start如果我将
None
更改为
4*2
以尝试在索引数组中的第四个短字符开始,它将不会绘制任何内容。如果我使用0而不是None,它甚至会失败:
gl.glpaurements(gl.gl\u QUADS,4,gl.gl\u UNSIGNED\u short,0)
尝试强制使用c\u void\u ptr作为最后一个参数:gl.glpaurements)(gl.gl_QUADS,4,gl.gl_UNSIGNED_SHORT,ctypes.c_void_ptr(start*size))几乎都是这样。真正起作用的行是
gl.gldrawerelements(gl.gl_QUADS,len(index)-start,gl.gl_UNSIGNED_SHORT,ctypes.c_void_p(start*2))
将该代码示例合并到您的答案中,我会将其标记为已接受。太棒了!我实际上从未用Python编写过OpenGL:-D