PyQt5 OpenGL Cubemap – Black window displaying

Question:

Title says it all. There are not many tutorials out there for this kind of thing. Can someone with some experience help me out.

I have tried a number of things. Interestingly I have been able to get OpenTK (C#) and OpenGL (C++) versions of this working, albeit outside of the Qt world (My boss wants this in Python). I know I am close but I just cant see the last issue/s.

I am only seeing a black window.

Code:

Python:

import sys
import numpy as np
import OpenGL.GL as gl

import array

from PyQt5 import QtGui
from PyQt5.QtCore import QSize, QPoint, Qt, pyqtSignal
from PyQt5.QtWidgets import QApplication, QWidget, QOpenGLWidget, QHBoxLayout

#from framework import *


vertexDim = 3
nVertices = 3

cubemap = np.array([-1.0,  1.0, -1.0,
                    -1.0, -1.0, -1.0,
                    1.0, -1.0, -1.0,
                    1.0, -1.0, -1.0,
                    1.0,  1.0, -1.0,
                    -1.0,  1.0, -1.0,

                    -1.0, -1.0,  1.0,
                    -1.0, -1.0, -1.0,
                    -1.0,  1.0, -1.0,
                    -1.0,  1.0, -1.0,
                    -1.0,  1.0,  1.0,
                    -1.0, -1.0,  1.0,

                    1.0, -1.0, -1.0,
                    1.0, -1.0,  1.0,
                    1.0,  1.0,  1.0,
                    1.0,  1.0,  1.0,
                    1.0,  1.0, -1.0,
                    1.0, -1.0, -1.0,

                    -1.0, -1.0,  1.0,
                    -1.0,  1.0,  1.0,
                    1.0,  1.0,  1.0,
                    1.0,  1.0,  1.0,
                    1.0, -1.0,  1.0,
                    -1.0, -1.0,  1.0,

                    -1.0,  1.0, -1.0,
                    1.0,  1.0, -1.0,
                    1.0,  1.0,  1.0,
                    1.0,  1.0,  1.0,
                    -1.0,  1.0,  1.0,
                    -1.0,  1.0, -1.0,

                    -1.0, -1.0, -1.0,
                    -1.0, -1.0,  1.0,
                    1.0, -1.0, -1.0,
                    1.0, -1.0, -1.0,
                    -1.0, -1.0,  1.0,
                    1.0, -1.0,  1.0], dtype='float32')

class App(QWidget):

    def __init__(self):
        super(App, self).__init__()

        self.glWidget = GLWidget()

        mainLayout = QHBoxLayout()
        mainLayout.addWidget(self.glWidget)
        self.setLayout(mainLayout)

        self.title = 'OpenGL Window - PyQt5'
        self.left = 20
        self.top = 30
        self.initUI()

    def initUI(self):
        self.setWindowTitle(self.title)

        #self.setGeometry(self.left, self.top, self.width, self.height)

class GLWidget(QOpenGLWidget):



    clicked = pyqtSignal()

    def __init__(self, parent=None):
        super(GLWidget, self).__init__(parent)

        self.profile = QtGui.QOpenGLVersionProfile()
        self.profile.setVersion( 2, 1 )

        self.xRot = 0
        self.yRot = 0
        self.zRot = 0
        # self.program = None

        self.lastPos = QPoint()

    def getOpenglInfo(self):
        info = """
            Vendor: {0}
            Renderer: {1}
            OpenGL Version: {2}
            Shader Version: {3}
        """.format(
            gl.glGetString(gl.GL_VENDOR),
            gl.glGetString(gl.GL_RENDERER),
            gl.glGetString(gl.GL_VERSION),
            gl.glGetString(gl.GL_SHADING_LANGUAGE_VERSION)
        )

        return info


    def rotateBy(self, xAngle, yAngle, zAngle):
        print(str(xAngle) + ", " + str(yAngle) + ", " + str(zAngle))
        self.xRot += xAngle
        self.yRot += yAngle
        self.zRot += zAngle
        self.update()


    def minimumSizeHint(self):
        return QSize(600, 400)

    def initializeGL(self):
        print(self.getOpenglInfo())

        self.gl = self.context().versionFunctions( self.profile )

        # Vertex Array Object
        self.vao = QtGui.QOpenGLVertexArrayObject( self )
        self.vao.create()

        # Set up and link shaders
        self.program = QtGui.QOpenGLShaderProgram( self )
        self.program.addShaderFromSourceFile( QtGui.QOpenGLShader.Vertex, 'shader.vert' )
        self.program.addShaderFromSourceFile( QtGui.QOpenGLShader.Fragment, 'shader.frag' )
        self.program.link()

        self.vao.bind()


        self.vertices = np.array([ 0.0, 0.0, 0.0, # x, y, z
                                   1.0, 0.0, 0.0, 
                                   0.5, 1.0, 0.0 ], dtype='float32')
        self.vbo_vertices = self.setVertexBuffer( cubemap, 3, self.program, "position" )

        self.colors = np.array([ 1.0, 0.0, 0.0, 1.0, # r, g, b, a
                                 0.0, 1.0, 0.0, 1.0, 
                                 0.0, 0.0, 1.0, 1.0 ], dtype='float32')
        self.vbo_colors = self.setVertexBuffer( self.colors, 4, self.program, "texture" )
        self.vao.release()

        self.program.bind()


        self.program.release()


    def resizeGL(self, width, height):
        gl.glViewport(0, 0, width, height)

    def setVertexBuffer( self, data_array, dim_vertex, program, shader_str ):
        vbo = QtGui.QOpenGLBuffer( QtGui.QOpenGLBuffer.VertexBuffer )
        vbo.create()
        vbo.bind()

        vertices = np.array( data_array, np.float32 )
        vbo.allocate( vertices, vertices.shape[0] * vertices.itemsize )

        attr_loc = program.attributeLocation( shader_str )
        program.enableAttributeArray( attr_loc )
        program.setAttributeBuffer( attr_loc, gl.GL_FLOAT, 0, dim_vertex )
        vbo.release()

        return vbo


    def paintGL(self):
        gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT)
        gl.glClearColor(0.0, 0.0, 0.2, 0.0)


        self.program.bind()

        self.texture = QtGui.QOpenGLTexture(QtGui.QOpenGLTexture.TargetCubeMap)
        self.texture.create()
        print(self.texture.isCreated())

        img = QtGui.QImage("C:\Users\path\to\image.jpg")

        self.texture.bind()
        self.texture.setSize(1024, 1024)
        self.texture.setFormat(QtGui.QOpenGLTexture.RGBAFormat)

        self.texture.allocateStorage()


        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapNegativeX, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())
        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapNegativeY, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())
        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapNegativeZ, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())
        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapPositiveX, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())
        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapPositiveY, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())
        self.texture.setData(0, 0, QtGui.QOpenGLTexture.CubeMapPositiveZ, QtGui.QOpenGLTexture.RGBA, QtGui.QOpenGLTexture.UInt16, img.bits())


        self.texture.setMinMagFilters(QtGui.QOpenGLTexture.Linear, QtGui.QOpenGLTexture.Linear)
        self.texture.setWrapMode(QtGui.QOpenGLTexture.ClampToEdge)



        # initialise Camera matrix with initial rotation
        m = QtGui.QMatrix4x4()
        m.ortho(-0.5, 0.5, 0.5, -0.5, 4.0, 15.0)
        m.translate(0.0, 0.0, -10.0)
        m.rotate(self.xRot / 16.0, 1.0, 0.0, 0.0)
        m.rotate(self.yRot / 16.0, 0.0, 1.0, 0.0)
        m.rotate(self.zRot / 16.0, 0.0, 0.0, 1.0)

        self.program.setUniformValue('mvp', m)
        self.program.setUniformValue('texture', 0)

        self.vao.bind()
        gl.glDrawArrays( gl.GL_TRIANGLES, 0, 36 )
        self.vao.release()      


        self.program.release()

    def mousePressEvent(self, event):
        self.lastPos = event.pos()


    def mouseMoveEvent(self, event):
        dx = event.x() - self.lastPos.x()
        dy = event.y() - self.lastPos.y()

        if event.buttons() & Qt.LeftButton:
            self.rotateBy(8 * dy, 8 * dx, 0)
        elif event.buttons() & Qt.RightButton:
            self.rotateBy(8 * dy, 0, 8 * dx)

        self.lastPos = event.pos()
        self.paintGL()

    def mouseReleaseEvent(self, event):
        self.clicked.emit()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())

Shaders:

shader.vert

#version 430 core

in vec3 position;

out vec3 texCoords;
uniform mat4 mvp;


void main(void)
{
    texCoords = position;
    gl_Position = mvp * vec4(position, 1.0);    
}

shader.frag

#version 430 core
in vec3 texCoords;

uniform sampler2D texture;

void main(void)
{
    gl_FragColor = texture2D(texture, texCoords.xy);
}
Asked By: TheLastGIS

||

Answers:

Since self.texture is a cube map (QtGui.QOpenGLTexture.TargetCubeMap), the type of the corresponding texture sampler uniform has to be samplerCube rather than sampler2D.

The texture coordinates for a cube map are treated as a direction vector emanating from the center of a cube.
Since the center of the cube model is (0, 0, 0), the direction vector respectively texture coordinate is equal the model position of the fragment.

Change the fragment shader to solve the issue:

#version 430 core
in vec3 texCoords;

uniform samplerCube texture;

void main(void)
{
    gl_FragColor = texture(texture, texCoords.xyz);
}

Note, texture2D respectively textureCube are compatibility profile functions. You’ve to use the function texture which works in compatibility as in core profile.

Answered By: Rabbid76
Categories: questions Tags: , , , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.