在OpenGL着色器中渲染纹理

时间:2015-04-11 20:45:11

标签: python opengl pyqt5

我正在尝试学习如何在片段着色器中将纹理应用于OpenGL对象,但似乎最终只能使用静态。这是我到目前为止使用PyOpenGL执行此操作的python代码。

class GLWidget(QtOpenGL.QGLWidget):
    def __init__(self, parent=None):
        self.parent = parent
        QtOpenGL.QGLWidget.__init__(self, parent)
        self.yRotDeg = 0.0

    def initializeGL(self):
        self.initGeometry()

        self.vertex_code = """
            #version 120
            uniform float scale;
            attribute vec4 color;
            attribute vec2 texcoord;
            attribute vec2 position;
            varying vec4 v_color;
            varying vec2 v_texcoord;
            void main()
            {
                gl_Position = vec4(scale*position, 0.0, 1.0);
                v_color = color;
                v_texcoord = texcoord;
            } """

        self.fragment_code = """
            #version 120
            uniform sampler2D tex;
            varying vec4 v_color;
            varying vec2 v_texcoord;
            void main()
            {
                gl_FragColor = texture2D(tex, v_texcoord);
            } """

        ## Build and activate program
        # Request program and shader slots from GPU
        self.program = GL.glCreateProgram()
        self.vertex = GL.glCreateShader(GL.GL_VERTEX_SHADER)
        self.fragment = GL.glCreateShader(GL.GL_FRAGMENT_SHADER)

        # Set shaders source
        GL.glShaderSource(self.vertex, self.vertex_code)
        GL.glShaderSource(self.fragment, self.fragment_code)

        # Compile shaders
        GL.glCompileShader(self.vertex)
        GL.glCompileShader(self.fragment)

        # Attach shader objects to the program
        GL.glAttachShader(self.program, self.vertex)
        GL.glAttachShader(self.program, self.fragment)

        # Build program
        GL.glLinkProgram(self.program)

        # Get rid of shaders (not needed anymore)
        GL.glDetachShader(self.program, self.vertex)
        GL.glDetachShader(self.program, self.fragment)

        # Make program the default program
        GL.glUseProgram(self.program)

        # Create array object
        self.vao = GL.glGenVertexArrays(1)
        GL.glBindVertexArray(self.vao)

        # Request buffer slot from GPU
        self.data_buffer = GL.glGenBuffers(1)
        self.indices_buffer = GL.glGenBuffers(1)

        # Make this buffer the default one
        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.data_buffer)

        # Upload data
        GL.glBufferData(GL.GL_ARRAY_BUFFER, self.data.nbytes, self.data, GL.GL_DYNAMIC_DRAW)

        ## Bind attributes
        self.stride = self.data.strides[0]
        self.offset = ctypes.c_void_p(0)
        self.loc = GL.glGetAttribLocation(self.program, "position".encode('utf-8'))
        GL.glEnableVertexAttribArray(self.loc)
        GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.data_buffer)
        GL.glVertexAttribPointer(self.loc, 3, GL.GL_FLOAT, False, self.stride, self.offset)

        self.offset = ctypes.c_void_p(self.data.dtype["position"].itemsize)
        self.loc = GL.glGetAttribLocation(self.program, "color".encode('utf-8'))
        if self.loc != -1:
            print('COLOR')
            GL.glEnableVertexAttribArray(self.loc)
            GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.data_buffer)
            GL.glVertexAttribPointer(self.loc, 4, GL.GL_FLOAT, False, self.stride, self.offset)

        self.offset = ctypes.c_void_p(self.data.dtype["texcoord"].itemsize)
        self.loc = GL.glGetAttribLocation(self.program, "texcoord".encode('utf-8'))
        if self.loc != -1:
            print('TEXCOORD')
            GL.glEnableVertexAttribArray(self.loc)
            GL.glBindBuffer(GL.GL_ARRAY_BUFFER, self.data_buffer)
            GL.glVertexAttribPointer(self.loc, 2, GL.GL_FLOAT, False, self.stride, self.offset)

        ## BEGIN TEX

        img = Image.open('kitten.png') # .jpg, .bmp, etc. also work
        img_data = np.array(list(img.getdata()), np.int8)

        texture = GL.glGenTextures(1)
        GL.glPixelStorei(GL.GL_UNPACK_ALIGNMENT,1)
        GL.glBindTexture(GL.GL_TEXTURE_2D, texture)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_S, GL.GL_CLAMP)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_WRAP_T, GL.GL_CLAMP)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MAG_FILTER, GL.GL_LINEAR)
        GL.glTexParameterf(GL.GL_TEXTURE_2D, GL.GL_TEXTURE_MIN_FILTER, GL.GL_LINEAR)
        GL.glTexImage2D(GL.GL_TEXTURE_2D, 0, GL.GL_RGB, img.size[0], img.size[1], 0, GL.GL_RGB, GL.GL_UNSIGNED_BYTE, img_data)

        self.loc = GL.glGetUniformLocation(self.program, "tex".encode('utf-8'))
        print(self.loc)
        GL.glUniform1i(self.loc, 0)

        GL.glActiveTexture(GL.GL_TEXTURE0 + 0)
        GL.glBindTexture(GL.GL_TEXTURE_2D, texture)

        ## END TEX

        self.offset = ctypes.c_void_p(self.indices.itemsize)
        GL.glBindBuffer(GL.GL_ELEMENT_ARRAY_BUFFER, self.indices_buffer)
        GL.glBufferData(GL.GL_ELEMENT_ARRAY_BUFFER, sys.getsizeof(self.indices), self.indices, GL.GL_STATIC_DRAW)

        ## Bind uniforms
        self.loc = GL.glGetUniformLocation(self.program, "scale".encode('utf-8'))
        GL.glUniform1f(self.loc, 1.0)


    def resizeGL(self, width, height):
        if height == 0: height = 1

        GL.glViewport(0, 0, width, height)
        GL.glMatrixMode(GL.GL_PROJECTION)
        GL.glLoadIdentity()
        aspect = width / float(height)

        GLU.gluPerspective(45.0, aspect, 1.0, 100.0)
        GL.glMatrixMode(GL.GL_MODELVIEW)

    def paintGL(self):
        GL.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT)

        #GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)

        self.offset = ctypes.c_void_p(self.indices.itemsize)
        GL.glDrawElements(GL.GL_QUADS, 4, GL.GL_UNSIGNED_BYTE, self.offset)

    def initGeometry(self):
        # Indices not implemented yet (need to get correct size for it as well)
        self.data = np.zeros(4, [("position", np.float32, 2),
                            ("color",    np.float32, 4),
                            ("texcoord", np.float32, 2)])
        self.data['color']    = [ 
            (1,0,0,1),
            (0,1,0,1),
            (0,0,1,1),
            (1,1,0,1)
        ]
        self.data['position'] = [
            (-1,-1),
            (-1,+1),
            (+1,-1),
            (+1,+1)
        ]

        self.data['texcoord'] = [
            (1,1),
            (1,1),
            (1,1),
            (0,1)
        ]

        self.indices = np.array([
            0,
            2,
            3,
            1
        ], dtype=np.uint8)



    def spin(self):
        #print('spin')
        self.yRotDeg = (self.yRotDeg  + 1) % 360.0
        self.parent.statusBar().showMessage('rotation %f' % self.yRotDeg)
        self.updateGL()

我使用Qt5作为窗口框架,但是当我运行程序时,顶点显示正确,颜色显示正确,但应该叠加在顶部的纹理似乎是噪音。

这是我最终得到的video。如果这还不够,我可以在GitHub或其他东西上抛出完整代码,但似乎这包括所有相关信息。我可能在这里做错了什么?

编辑: 好吧,我接受了Reto的建议并更新了我的代码,这似乎主要解决了我的问题,除了我的纹理现在被分成两半并且颠倒了。这是结果:

OpenGL results

知道我在这里缺少什么。我以为我必须把我的坐标放在错误的顺序中,但似乎改变它们的顺序并不会改变方向。

1 个答案:

答案 0 :(得分:0)

我不确定我是否完全理解这段代码的python规范,但是:使用texcoords,因为它们目前在你的代码中,你根本不应该得到那个图像。四个中的3个是相同的,您在纹理空间中定义了一些零区域三角形。你应该从图像中的一行看到一些完全单色的三角形和一个三角形奇怪的colord。

然而,我认为那些texcoords根本就没用过。罪魁祸首是如何设置texcoord属性的属性指针:

self.offset = types.c_void_p(self.data.dtype["texcoord"].itemsize)

你可能意味着像

self.offset = types.c_void_p(self.data.dtype["position"].itemsize + self.data.dtype["color"].itemsize )

跳过位置和颜色数据,这与通过跳过位置设置“颜色”属性的方式一致。

目前,您实际上似乎指向了texcoord属性的颜色数据,这恰好是一些不错的0和1序列。