三次方贝塞尔曲线补丁及其控制笼的渲染不正确

时间:2019-07-02 07:45:46

标签: python python-3.x opengl glut opengl-3

我正在尝试移植Superbible OpenGL第7版第8章中的示例。并且控制笼线不会像示例gif或本书第375页上的图像那样从一点到另一点。取而代之的是将线条绘制到单个点。我在下面提供了它。谢谢。

预期输出:

expected output

我收到的实际输出:

actual output

支持文件:cubicbezier_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 ktxloader import KTXObject    # location of ktx file format loader
from textoverlay import OVERLAY_
from shader import shader_load, link_from_shaders

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

import numpy as np
from math import cos, sin
import glm
identityMatrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]

#myobject = SBMObject()
ktxobject = KTXObject()
overlay = OVERLAY_()

tess_program = GLuint(0)
draw_cp_program = GLuint(0)
patch_vao = GLuint(0)
patch_buffer = GLuint(0)
cage_indices = GLuint(0)

patch_data = [glm.vec3() for _ in range(16)] 

show_points=False
show_cage=False
wireframe=False
paused=False

class uniforms:
    class patch:
        mv_matrix=0
        proj_matrix=0
        mvp=0

    class control_point:
        draw_color=0
        mvp=0

uniform = uniforms()

def load_shaders():
    global uniform
    global tess_program
    global draw_cp_program

    if (tess_program):
        glDeleteProgram(tess_program)

    shaders = [GLuint(0) for _ in range(4)]

    shaders[0] = shader_load("cubicbezier.vs.glsl", GL_VERTEX_SHADER)
    shaders[1] = shader_load("cubicbezier.tcs.glsl", GL_TESS_CONTROL_SHADER)
    shaders[2] = shader_load("cubicbezier.tes.glsl", GL_TESS_EVALUATION_SHADER)
    shaders[3] = shader_load("cubicbezier.fs.glsl", GL_FRAGMENT_SHADER)

    tess_program = link_from_shaders(shaders, 4, True)

    uniform.patch.mv_matrix = glGetUniformLocation(tess_program, "mv_matrix")
    uniform.patch.proj_matrix = glGetUniformLocation(tess_program, "proj_matrix")
    uniform.patch.mvp = glGetUniformLocation(tess_program, "mvp")

    if (draw_cp_program):
        glDeleteProgram(draw_cp_program)

    shaders[0] = shader_load("draw-control-points.vs.glsl", GL_VERTEX_SHADER)
    shaders[1] = shader_load("draw-control-points.fs.glsl", GL_FRAGMENT_SHADER)

    draw_cp_program = link_from_shaders(shaders, 2, True)

    uniform.control_point.draw_color = glGetUniformLocation(draw_cp_program, "draw_color")
    uniform.control_point.mvp = glGetUniformLocation(draw_cp_program, "mvp")

class Scene:

    def __init__(self, width, height):
        global patch_vao
        global patch_buffer
        global cage_indices

        load_shaders()

        glGenVertexArrays(1, patch_vao)
        glBindVertexArray(patch_vao)

        glGenBuffers(1, patch_buffer)
        glBindBuffer(GL_ARRAY_BUFFER, patch_buffer)

        glBufferData(GL_ARRAY_BUFFER, ctypes.sizeof(ctypes.c_float)*3*16, None, GL_DYNAMIC_DRAW)
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, None)
        glEnableVertexAttribArray(0)

        indices = [
            0, 1, 1, 2, 2, 3,
            4, 5, 5, 6, 6, 7,
            8, 9, 9, 10, 10, 11,
            12, 13, 13, 14, 14, 15,

            0, 4, 4, 8, 8, 12,
            1, 5, 5, 9, 9, 13,
            2, 6, 6, 10, 10, 14,
            3, 7, 7, 11, 11, 15
        ]

        ar = np.array(indices, dtype='int')

        glGenBuffers(1, cage_indices)
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cage_indices)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, ctypes.sizeof(ctypes.c_int)*len(indices), ar, GL_STATIC_DRAW)

        overlay.init(80, 50)
        overlay.clear()
        overlay.drawText("W: Toggle wireframe", 0, 0)
        overlay.drawText("C: Toggle control cage", 0, 1)
        overlay.drawText("X: Toggle control points", 0, 2)
        overlay.drawText("P: Pause", 0, 3)

    def display(self):

        currentTime = time.time()

        gray = [ 0.1, 0.1, 0.1, 0.0 ]
        one = 1.0

        last_time = 0.0
        total_time = 0.0

        if (not paused):
            total_time += (currentTime - last_time)
        last_time = currentTime

        t = total_time

        patch_initializer = [
            -1.0,  -1.0,  0.0,
            -0.33, -1.0,  0.0,
             0.33, -1.0,  0.0,
             1.0,  -1.0,  0.0,

            -1.0,  -0.33, 0.0,
            -0.33, -0.33, 0.0,
             0.33, -0.33, 0.0,
             1.0,  -0.33, 0.0,

            -1.0,   0.33, 0.0,
            -0.33,  0.33, 0.0,
             0.33,  0.33, 0.0,
             1.0,   0.33, 0.0,

            -1.0,   1.0,  0.0,
            -0.33,  1.0,  0.0,
             0.33,  1.0,  0.0,
             1.0,   1.0,  0.0,
        ]

        glViewport(0, 0, self.width, self.height)
        glClearBufferfv(GL_COLOR, 0, gray)
        glClearBufferfv(GL_DEPTH, 0, one)

        glEnable(GL_DEPTH_TEST)

        p = glMapBufferRange(GL_ARRAY_BUFFER, 0, ctypes.sizeof(ctypes.c_float)*3*16, GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT)

        ppp = ((ctypes.c_float * 3) * 16).from_address(p) 

        for i in range(0, 16):

            fi = float(i) / 16.0

            ppp[i][0] = patch_initializer[i*3]
            ppp[i][1] = patch_initializer[(i*3)+1]

            ppp[i][2] = sin(t * (0.2 + fi * 0.3))

        glUnmapBuffer(GL_ARRAY_BUFFER)

        glBindVertexArray(patch_vao)

        glUseProgram(tess_program)

        proj_matrix = (GLfloat * 16)(*identityMatrix)
        proj_matrix = m3dPerspective(m3dDegToRad(50.0), float(self.width) / float(self.height), 1.0, 1000.0)

        T = (GLfloat * 16)(*identityMatrix)
        m3dTranslateMatrix44(T, 0.0, 0.0, -4.0)

        RX = (GLfloat * 16)(*identityMatrix)
        m3dRotationMatrix44(RX, t * m3dDegToRad(10.0), 1.0, 0.0, 0.0)

        RY = (GLfloat * 16)(*identityMatrix)
        m3dRotationMatrix44(RY, t * m3dDegToRad(17.0), 0.0, 1.0, 0.0)

        mv_matrix = (GLfloat * 16)(*identityMatrix)
        mv_matrix = m3dMultiply(T, m3dMultiply(RX, RY))

        glUniformMatrix4fv(uniform.patch.mv_matrix, 1, GL_FALSE, mv_matrix)
        glUniformMatrix4fv(uniform.patch.proj_matrix, 1, GL_FALSE, proj_matrix)
        glUniformMatrix4fv(uniform.patch.mvp, 1, GL_FALSE, m3dMultiply(proj_matrix , mv_matrix))

        if (wireframe):
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
        else:
            glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)

        glPatchParameteri(GL_PATCH_VERTICES, 16)
        glDrawArrays(GL_PATCHES, 0, 16)

        glUseProgram(draw_cp_program)
        glUniformMatrix4fv(uniform.control_point.mvp, 1, GL_FALSE, m3dMultiply(proj_matrix , mv_matrix))

        if (show_points):
            glPointSize(9.0)
            glUniform4fv(uniform.control_point.draw_color, 1, [0.2, 0.7, 0.9, 1.0])
            glDrawArrays(GL_POINTS, 0, 16)

        if (show_cage):
            glUniform4fv(uniform.control_point.draw_color, 1, [0.7, 0.9, 0.2, 1.0])
            glDrawElements(GL_LINES, 48, GL_UNSIGNED_SHORT, None)

        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
        overlay.draw()

        glutSwapBuffers()

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

    def keyboard(self, key, x, y ):
        global fullscreen
        global show_cage
        global show_points
        global wireframe
        global paused

        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
        elif key == b'c' or key == b'C':
            show_cage = not show_cage
        elif key == b'x' or key == b'X':
            show_points = not show_points
        elif key == b'w' or key == b'W':
            wireframe = not wireframe
        elif key == b'p' or key == b'P':
            paused = not paused
        elif key == b'r' or key == b'R':
            load_shaders()

    def init(self):
        pass

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

if __name__ == '__main__':
    glutInit()
    glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH)
    glutInitWindowSize(512, 512)
    w1 = glutCreateWindow('OpenGL SuperBible - Cubic Bezier Patch')
    glutInitWindowPosition(int((1360/2)-(512/2)), int((768/2)-(512/2)))
    fullscreen = 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()

移植自:cubicbezier.cpp

1 个答案:

答案 0 :(得分:1)

指定元素数组后,索引的类型分别为types.c_int'int'

ar = np.array(indices, dtype='int')
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cage_indices)
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
   ctypes.sizeof(ctypes.c_int)*len(indices), ar, GL_STATIC_DRAW)

但是,当线由glDrawElements绘制时,则指定的类型为GL_UNSIGNED_SHORT

glDrawElements(GL_LINES, 48, GL_UNSIGNED_SHORT, None)

glDrawElements中的第3个参数指定数组中索引的数据类型。如果数组中的数据为 uint32 类型,则枚举器常量必须为GL_UNSIGNED_INT。如果索引的类型为 uint16 ,则具有枚举数常量的类型必须为GL_UNSIGNED_SHORT

将数组形式ctypes.c_int的类型分别从'int'更改为ctypes.c_ushortnp.uint16,以解决此问题:

ar = np.array(indices, dtype=np.uint16)
glBufferData(GL_ELEMENT_ARRAY_BUFFER,
    ctypes.sizeof(ctypes.c_ushort)*len(indices), ar, GL_STATIC_DRAW)

请注意,如果数组中的类型为types.c_int,则每个索引都存储为4个字节。但是,如果指定的类型为GL_UNSIGNED_SHORT,则每个第2个字节应作为索引。这导致第二个索引为0。