简体   繁体   English

在OpenGL着色器中渲染纹理

[英]Rendering texture in OpenGL shader

I am trying to learn how to apply a texture to OpenGL objects within the fragment shader but seem to just end up with static. 我正在尝试学习如何将纹理应用于片段着色器中的OpenGL对象,但似乎最终只能达到静态。 Here is the python code I have so far for doing this with PyOpenGL. 这是我到目前为止使用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()

I'm using Qt5 as the windowing framework but when I run the program, the vertices are displayed correctly, and the colors are displayed correctly, but the texture that should be overlayed on top just seems to be noise. 我使用Qt5作为窗口框架,但是运行该程序时,顶点显示正确,颜色显示正确,但是应该叠加在顶部的纹理似乎只是杂色。

Here is a video of what I'm ending up with. 这是我要结束的视频 I can throw the full code on GitHub or something as well if this isn't enough, but it seems like this includes all the relevant info. 如果还不够的话,我可以将完整的代码放到GitHub或其他东西上,但这似乎包含所有相关信息。 What might I be doing wrong here? 我在这里可能做错了什么?

EDIT: Ok, I took Reto's advice and updated my code and that seems to have mostly fixed my problem except that my texture is now split in half and rendered upside down.. Here's the result: 编辑:好的,我接受了Reto的建议并更新了我的代码,这似乎基本解决了我的问题,除了我的纹理现在被分成两半并颠倒了。.结果如下:

OpenGL结果

Any idea what I'm missing here. 知道我在这里缺少什么。 I thought I must have just put my coordinates in the wrong order but it seems that changing their order doesn't change the orientation at all. 我以为我一定是将我的坐标放错了顺序,但似乎改变它们的顺序根本不会改变方向。

I'm not sure if I totally understand the python specifcs of this code, but: with the texcoords as they are currently in your code, you shouldn't get that image at all. 我不确定我是否完全理解该代码的python规范,但是:使用texcoords就像它们当前在您的代码中一样,您根本不应该获得该图像。 3 of the four are identical, you define some zero-area triange in texture space. 四个中的三个相同,您可以在纹理空间中定义一些零面积三角形。 You should see some completely single-colored triangle and one triangle weirdly colord from a single line in the image. 您应该看到一些完全单色的三角形和一个奇怪的三角形,它们从图像的单行中着色。

However, I think those texcoords are never used at all. 但是,我认为这些texcoords从未使用过。 The culprit is how you set the attribute pointer for the texcoord attribute: 罪魁祸首是如何为texcoord属性设置属性指针:

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

You probably mean something like 您可能会说类似

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

to skip the position and color data, which is also consistent with how you set up the "color" attribute by skipping position there. 跳过位置和颜色数据,这也与您通过跳过位置数据来设置“颜色”属性的方式保持一致。

Currently, you actually seem to point somewhere into your color data for your texcoord attribute, which happens to be some nice 0 and 1 sequences. 当前,您实际上似乎在指向texcoord属性的颜色数据中指向某个地方,恰好是一些0和1序列。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM